void foo(const char *s);
is equivalent to:
void foo(const char s[]);
Are there similar equivalents to the following two?
void foo(char * const s);
void foo(const char * const s);
In C++, the compiler will automatically convert function parameters of type array of N elements of type T (where N can be unknown) into pointer to T. In the same transformation top level const qualifiers for the arguments are dropped:
void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )
That means that in the pointer case, the top level const qualifier is removed, and in the case of the array it is converted to pointer to. Now, on the other hand you cannot mix both, only because you cannot declare a constant array of N elements of type T, since arrays are always const. That means that you cannot write:
void f( const int a[] const ); // Invalid type definition
As the type of the parameter is invalid. If it was a valid type, then the conversions would apply, but because it is not, the compiler will reject the code before trying to perform the conversion.
This is treated in §8.3.5/3 of the C++03 standard (and probably somewhere close in C++11)
A single name can be used for several different functions in a single scope; this is function overloading (clause 13). All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type. The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any cv-qualifier modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types is the function’s parameter type list.
Note that since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise:
void f( int a[10] ) { a[5] = 7; }
The compiler is not going to check that the passed array has 10 elements, it reads the declaration as void f( int * ), and will gladly accept a call with an array of less elements or even no array at all (a pointer to a single int). Using a pointer in the actual code:
void f( int *a ) { a[5] = 7; }
Will likely trigger some alarms in a code review: are we guaranteed that in all calls to f the argument will be at least 6 elements big? Should we not pass also the size just in case?
You cannot in C89, but in C99 you can declare the equivalents as:
void foo(char s[const]);
void foo(const char s[const]);
this will be useful in some cases:
class AA {
void foo(char a[]);
void foo(const char a[]);
};
void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }
and in C:
extern void foo(char a[]);
extern void fooc(const char a[]);
void foo(char* const a) { … }
void fooc(const char* const a) { … }
I thought that a pointer can be null, while an array argument cannot be null (and that the compiler is permitted to optimize knowing that; however on a simple example gcc-4.6 don't do such an optimization, even with -O3).
I am expecting that the compiler would optimize differently the two functions below. It does not. I don't have my C standard at hand to check if it could remove the test in ss below.
int s (int *t)
{
if (!t)
return 0;
return t[0] + t[1];
}
int ss (int t[])
{
if (!t) // never false, since t is an array!!
return 0;
return t[0] + t[1];
}
Related
Functionally and syntactically speaking, is there a difference between a function whose prototype is int foo(void) and int foo(void *)?
I know the difference between, for example, int bar(int) and int bar(int *) - one of them is looking for an int, and the other is looking for an int pointer. Does void behave the same way?
From this answer on Software Engineering, void is treated specially depending on how it's used. In C and C++, void is used to indicate an absence of a data type, whereas void * is used to indicate a pointer which points to some data/space in memory that does not have a type. void * cannot be dereferenced on its own, and must be cast to another type first. This cast need not be explicit in C, but must be explicit in C++. (This is why we don't cast the return value of malloc, which is void *.)
When used with a function as a parameter, void means a total absence of any parameters, and is the only parameter allowed. Attempting to use void like a variable type or include other arguments results in a compiler error:
int foo(void, int); //trying to use "void" as a parameter
int bar(void baz); //trying to use "void" as an argument's type
main.c:1:8: error: 'void' must be the first and only parameter if specified
int foo(void, int);
^
main.c:2:14: error: argument may not have 'void' type
int bar(void baz);
^
It is similarly impossible to declare a variable with type void:
int main(void) {
void qux; //trying to create a variable with type void
}
main.c:5:8: error: variable has incomplete type 'void'
void qux;
void as a return value for a function indicates no data will be returned. Since it is impossible to declare a variable of type void, it is impossible to catch the return value of a void function, even with a void pointer.
void foo(int i) { return; }
int main(void) {
void *j;
j = foo(0);
return 0;
}
main.c:5:5: error: assigning to 'void *' from
incompatible type 'void'
j = foo(0);
^ ~~~~~~
The typeless void * is a different case. A void pointer indicates a pointer to a location in memory, but does not indicate the type of data at that pointer. (This is the used to achieve polymorphism in C, such as with the qsort() function.) These pointers can be tricky to use, however, as it is very easy to accidentally cast them to the wrong type. The code below won't throw any compiler errors in C, but results in undefined behavior:
#include <stdio.h>
int main(void) {
double foo = 47.2; //create a double
void *bar = &foo; //create a void pointer to that double
char *baz = bar; //create a char pointer from the void pointer, which
//is supposed to hold a double
fprintf(stdout, "%s\n", baz);
}
The following code, however, is perfectly legal; casting to and from a void pointer never changes the value it holds.
#include <stdio.h>
int main(void) {
double foo = 47.2;
void *bar = &foo;
double *baz = bar;
fprintf(stdout, "%f\n", *baz);
}
47.200000
As a function parameter, void * indicates that the type of the data at the pointer you are passing in is not known, and it is up to you, the programmer, to properly handle whatever is at that memory location. As a return value, void * indicates that the type of the data being returned is not known or is typeless, and must be handled by the program.
int quux(void *); //a function that receives a pointer to data whose type is not known, and returns an int.
void *quuz(int); //a function that receives an int, and returns a pointer to data whose type is not known.
tl;dr void in a function prototype means "no data" and indicates no return value or no parameters, void * in a function prototype means "the data at the pointer this function is given does not have a known type" and indicates a parameter or return value whose pointer must be cast to a different type before the data at the pointer can be used.
foo(void) - function with no parameters
foo(void *) - function with one void * parameter
What is void *? It is just the pointer to the data with no specified type. It Can be casted to any other pointer type
unsigned add(void *arr)
{
unsigned *uarr = arr;
return uarr[0] + uarr[1];
}
Functionally and syntactically speaking, is there a difference between a function whose prototype is int foo(void) and int foo(void *)?
There is a difference:
int foo(void) declares a function that accepts no arguments.
int foo(void *) declares a function that accepts single argument of type void*.
In C++, int foo(void) is equvalent to int foo().
I am learning the basics of C++ and OOP in my university now. I am not 100% sure how a function pointer works when assigning functions to them. I encountered the following code:
void mystery7(int a, const double b) { cout << "mystery7" << endl; }
const int mystery8(int a, double b) { cout << "mystery8" << endl; }
int main() {
void(*p1)(int, double) = mystery7; /* No error! */
void(*p2)(int, const double) = mystery7;
const int(*p3)(int, double) = mystery8;
const int(*p4)(const int, double) = mystery8; /* No error! */
}
From my understanding, the p2 and p3 assignments are fine as the function parameters types match and const-ness is correct. But why don't the p1 and p4 assignments fail? Shouldn't it be illegal to match const double/int to non-const double/int?
According to the C++ Standard (C++ 17, 16.1 Overloadable declarations)
(3.4) — Parameter declarations that differ only in the presence or
absence of const and/or volatile are equivalent. That is, the const
and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called.
So in the process of determining of the function type the qualifier const for example of the second parameter of the function declaration below is discarded.
void mystery7(int a, const double b);
and the function type is void( int, double ).
Also consider the following function declaration
void f( const int * const p );
It is equivalent to the following declaration
void f( const int * p );
It is the second const that makes the parameter constant (that is it declares the pointer itself as a constant object that can not be reassigned inside the function). The first const defines the type of the pointer. It is not discarded.
Pay attention to that though in the C++ Standard there is used the term "const reference" references themselves can not be constant opposite to pointers. That is the following declaration
int & const x = initializer;
is incorrect.
While this declaration
int * const x = initializer;
is correct and declares a constant pointer.
There is a special rule for function arguments passed by value.
Although const on them will affect their usage inside the function (to prevent accidents), it's basically ignored on the signature. That's because the constness of an object passed by value has no effect whatsoever on the original copied-from object at the call site.
That's what you're seeing.
(Personally I think that this design decision was a mistake; it's confusing and unnecessary! But it is what it is. Note that it comes from the same passage that silently changes void foo(T arg[5]); into void foo(T* arg);, so there's plenty of hokey bullsh!t in there already that we have to deal with!)
Do recall, though, that this doesn't just erase any const in such an argument's type. In int* const the pointer is const, but in int const* (or const int*) the pointer is non-const but is to a const thing. Only the first example relates to constness of the pointer itself and will be stripped.
[dcl.fct]/5 The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. — end note ]
There is a situation where adding or removing a const qualifier to a function argument is a serious bug. It comes when you pass an argument by pointer.
Here’s a simple example of what could go wrong. This code is broken in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// char * strncpy ( char * destination, const char * source, size_t num );
/* Undeclare the macro required by the C standard, to get a function name that
* we can assign to a pointer:
*/
#undef strncpy
// The correct declaration:
char* (*const fp1)(char*, const char*, size_t) = strncpy;
// Changing const char* to char* will give a warning:
char* (*const fp2)(char*, char*, size_t) = strncpy;
// Adding a const qualifier is actually dangerous:
char* (*const fp3)(const char*, const char*, size_t) = strncpy;
const char* const unmodifiable = "hello, world!";
int main(void)
{
// This is undefined behavior:
fp3( unmodifiable, "Whoops!", sizeof(unmodifiable) );
fputs( unmodifiable, stdout );
return EXIT_SUCCESS;
}
The problem here is with fp3. This is a pointer to a function that accepts two const char* arguments. However, it points to the standard library call strncpy()¹, whose first argument is a buffer that it modifies. That is, fp3( dest, src, length ) has a type that promises not to modify the data dest points to, but then it passes the arguments on to strncpy(), which modifies that data! This is only possible because we changed the type signature of the function.
Trying to modify a string constant is undefined behavior—we effectively told the program to call strncpy( "hello, world!", "Whoops!", sizeof("hello, world!") )—and on several different compilers I tested with, it will fail silently at runtime.
Any modern C compiler should allow the assignment to fp1 but warn you that you’re shooting yourself in the foot with either fp2 or fp3. In C++, the fp2 and fp3 lines will not compile at all without a reinterpret_cast. Adding the explicit cast makes the compiler assume you know what you’re doing and silences the warnings, but the program still fails due to its undefined behavior.
const auto fp2 =
reinterpret_cast<char*(*)(char*, char*, size_t)>(strncpy);
// Adding a const qualifier is actually dangerous:
const auto fp3 =
reinterpret_cast<char*(*)(const char*, const char*, size_t)>(strncpy);
This doesn’t arise with arguments passed by value, because the compiler makes copies of those. Marking a parameter passed by value const just means the function doesn’t expect to need to modify its temporary copy. For example, if the standard library internally declared char* strncpy( char* const dest, const char* const src, const size_t n ), it would not be able to use the K&R idiom *dest++ = *src++;. This modifies the function’s temporary copies of the arguments, which we declared const. Since this doesn’t affect the rest of the program, C doesn’t mind if you add or remove a const qualifier like that in a function prototype or function pointer. Normally, you don’t make them part of the public interface in the header file, since they’re an implementation detail.
¹ Although I use strncpy() as an example of a well-known function with the right signature, it is deprecated in general.
On this page I found a good example of function pointers in C++ (as well as of functors, but this question isn't about functors). Below is some copypasta from that page.
#include <iostream>
double add(double left, double right) {
return left + right;
}
double multiply(double left, double right) {
return left * right;
}
double binary_op(double left, double right, double (*f)(double, double)) {
return (*f)(left, right);
}
int main( ) {
double a = 5.0;
double b = 10.0;
std::cout << "Add: " << binary_op(a, b, add) << std::endl;
std::cout << "Multiply: " << binary_op(a, b, multiply) << std::endl;
return 0;
}
I understand the code in general terms, but there are a couple of things that I've always found confusing. Function binary_op() takes a function pointer *f, but when it's used, for example on line 19 binary_op(a, b, add), the function symbol add is passed in, not what one would think of as its pointer, &add. Now you may say that this is because the symbol add is a pointer; it's the address of the bit of code corresponding to the function add(). Very well, but then there still seems to be a type discrepancy here. The function binary_op() takes *f, which means f is a pointer to something. I pass in add, which itself is a pointer to code. (Right?) So then f is assigned the value of add, which makes f a pointer to code, which means that f is a function just like add, which means that f should be called like f(left, right), exactly how add should be called, but on line 12, it's called like (*f)(left, right), which doesn't seem right to me because it would be like writing (*add)(left, right), and *add isn't the function, it's the first character of the code that add points to. (Right?)
I know that replacing the original definition of binary_op() with the following also works.
double binary_op(double left, double right, double f(double, double)) {
return f(left, right);
}
And in fact, this makes much more sense to me, but the original syntax doesn't make sense as I explained above.
So, why is it syntactically correct to use (*f) instead of just f? If the symbol func is itself a pointer, then what precisely does the phrase "function pointer" or "pointer to a function" mean? As the original code currently stands, when we write double (*f)(double, double), what kind of thing is f then? A pointer to a pointer (because (*f) is itself a pointer to a bit of code)? Is the symbol add the same sort of thing as (*f), or the same sort of thing as f?
Now, if the answer to all of this is "Yeah C++ syntax is weird, just memorise function pointer syntax and don't question it.", then I'll reluctantly accept it, but I would really like a proper explanation of what I'm thinking wrong here.
I've read this question and I think I understand that, but haven't found it helpful in addressing my confusion. I've also read this question, which also didn't help because it doesn't directly address my type discrepancy problem. I could keep reading the sea of information on the internet to find my answer but hey, that's what Stack Overflow is for right?
This is because C function pointer are special.
First of, the expression add will decay into a pointer. Just like reference to array will decay into a pointer, reference to function will decay into a pointer to function.
Then, the weird stuff it there:
return (*f)(left, right);
So, why is it syntactically correct to use (*f) instead of just f?
Both are valid, you can rewrite the code like this:
return f(left, right);
This is because the dereference operator will return the reference to the function, and both a reference to a function or a function pointer are considered callable.
The funny thing is that a function reference decay so easily that it will decay back into a pointer when calling the dereference operator, allowing to dereference the function as many time as you want:
return (*******f)(left, right); // ah! still works
As the original code currently stands, when we write double (*f)(double, double), what kind of thing is f then?
The type of f is double (*)(double, double) i.e. it is a pointer to a function of type double(double,double).
because (*f) is itself a pointer
It is not.
Q: What do you get when you indirect through a pointer (such as in *f)? A: You get an lvalue reference. For example, given an object pointer int* ptr, the type of the expression *ptr is int& i.e. lvalue reference to int.
The same is true for function pointers: When you indirect through a function pointer, you get an lvalue reference to the pointed function. In the case of *f, the type is double (&)(double, double) i.e. reference to function of type double(double,double).
Is the symbol add the same sort of thing as (*f), or the same sort of thing as f?
The unqualified id expression add is the same sort of thing as *f i.e. it is an lvalue:
Standard draft [expr.prim.id.unqual]
... The expression is an lvalue if the entity is a function ...
the function symbol add is passed in, not what one would think of as its pointer, &add. Now you may say that this is because the symbol add is a pointer;
No. That's not the reason.
add is not a pointer. It is an lvalue. But lvalues of function type implicitly convert to a pointer (this is called decaying):
Standard draft [conv.func]
An lvalue of function type T can be converted to a prvalue of type “pointer to T”. The result is a pointer to the function.
As such, the following are semantically equivalent:
binary_op(a, b, add); // implicit function-to-pointer conversion
binary_op(a, b, &add); // explicit use of addressof operator
So, why is it syntactically correct to use (*f) instead of just f?
Turns out that calling a function lvalue has the same syntax as calling a function pointer:
Standard draft [expr.call]
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function.
The postfix expression shall have function type or function pointer type.
For a call to a non-member function or to a static member function, the postfix expression shall either be an lvalue that refers to a function (in which case the function-to-pointer standard conversion ([conv.func]) is suppressed on the postfix expression), or have function pointer type.
These are all the same function call:
add(parameter_list); // lvalue
(*f)(parameter_list); // lvalue
(&add)(parameter_list); // pointer
f(parameter_list); // pointer
P.S. These two declarations are equivalent:
double binary_op(double, double, double (*)(double, double))
double binary_op(double, double, double (double, double))
This is because of the following rule, which is complementary to the implicit decay into function pointer:
Standard draft [dcl.fct]
The type of a function is determined using the following rules.
The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator.
After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T” ...
First of all a function parameter specified as a function declaration is adjusted to pointer to the function when the compiler determinates the type of the parameter. So for example following function declarations
void f( void h() );
void f( void ( *h )() );
are equivalent and declare the same one function.
Consider the following demonstrative program
#include <iostream>
void f( void h() );
void f( void ( *h )() );
void h() { std::cout << "Hello Ray\n"; }
void f( void h() ) { h(); }
int main()
{
f( h );
}
From the c++ 17 Standard (11.3.5 Functions):
5 The type of a function is determined using the following rules. The
type of each parameter (including function parameter packs) is
determined from its own decl-specifier-seq and declarator. After
determining the type of each parameter, any parameter of type “array
of T” or of function type T is adjusted to be “pointer to T”.
On the other hand, according to the C++ 17 Standard
9 When there is no parameter for a given argument, the argument is
passed in such a way that the receiving function can obtain the value
of the argument by invoking va_arg (21.11). [ Note: This paragraph
does not apply to arguments passed to a function parameter pack.
Function parameter packs are expanded during template instantiation
(17.6.3), thus each such argument has a corresponding parameter when a
function template specialization is actually called. — end note ] The
lvalue-to-rvalue (7.1), array-to-pointer (7.2), and function-to-pointer (7.3)
standard conversions are performed on the argument expression
So what is the difference between these two declarations
void f( void h() );
void f( void ( *h )() );
For the first declaration you may consider the parameter h within the function body like a typedef for a function pointer.
typedef void ( *H )();
For example
#include <iostream>
void f( void h() );
void f( void ( *h )() );
void h() { std::cout << "Hello Ray\n"; }
typedef void ( *H )();
void f( H h ) { h(); }
int main()
{
f( h );
}
According to the C++ 17 Standard (8.5.1.2 Function call)
1 A function call is a postfix expression followed by parentheses
containing a possibly empty, comma-separated list of
initializer-clauses which constitute the arguments to the function.
The postfix expression shall have function type or function pointer
type.
So you may also define the function like
void f( void h() ) { ( *h )(); }
Or even like
void f( void h() ) { ( ******h )(); }
because when the operator * is applied to a function name then the function name is implicitly convereted to pijnter to the function.
I have a few functions that return void. I made pointers to these functions and wanted to have an array of these functions:
Why does this code work:
#include <cstdio>
using std::puts;
void tell() {
puts("hi");
};
void slap() {
puts("goodbye");
}
int main(int argc, char *argv[])
{
void (*tp)() = tell;
void (*sp)() = slap;
void(*funcs[])() = {tp, sp};
for (auto point:funcs) {
point();
}
return 0;
}
When I try this code with out specifying a pointer in funcs (i.e. void(funcs[])() = {tp, sp}; I get " error: 'funcs' declared as array of functions of type 'void ()' " Which is exactly what they are - so why is that an error?
I also don't get the syntax, wouldn't the () at the end of void(*funcs[])() indicate actually calling a function?
C++ Standard 8.3.5/10 says:
There shall be no arrays of functions, although there can be arrays of pointers to functions.
The declaration of "funcs" must be read using the "spiral rule":
funcs[]: funcs is an array
*funcs[]: funcs is an array of pointers
(*funcs[])(): funcs is an array of pointers to functions with no parameters
void (*funcs[])(): funcs is an array of pointers to functions with no parameters returning void.
Well you can declare it explicitly like this:
void (*actions[5])();
But this is nearly unreadable.
To make it more readable use a typedef.
typedef void(*Action)(); // Action is the typename for a pointer
// to a function return null and taking
// no parameters.
Action actions[5]; // An array of 5 Action objects.
Or for your purposes:
int main()
{
Action actions[] = {&tell, &slap};
}
Without the asterisk, void (funcs[])() declares an array of functions rather than array of pointers to functions. The latter is allowed in the C++ grammar while the former is not.
[dcl.array]/p1:
T is called the array element type; this type shall not be a reference type, the (possibly cv-qualified) type void, a function type or an abstract class type.
The contents of the initializer-list ({tp, sp}) are functions but they are converted to pointers via the function-to-pointer conversion:
[conv.func]/p1
An lvalue of function type T can be converted to a prvalue of type “pointer to T.” The result is a pointer to the function.
Note that C++ also doesn't allow an array of references.
I also don't get the syntax, wouldn't the () at the end of void(*funcs[])() indicate actually calling a function?
No, this is a declaration of an array type. The () is part of the construction of the type which specifies the argument list of the function. The entire type indicates "an array of pointers to functions which take zero arguments (()) and return void". It may become clearer with the use of a type alias:
using void_f = void (*)();
void_f funcs[] = {tp, sp};
use like this:
int main(int argc, char *argv[])
{
void (*tp)() = tell;
void (*sp)() = slap;
void (*funcs[])() = {tp, sp};
for (void (*point)():funcs)
{
point;
}
return 0;
}
Say I have this example:
char const * const
foo( ){
/* which is initialized to const char * const */
return str;
}
What is the right way to do it to avoid the compiler warning "type qualifier on return type is meaningless"?
The way you wrote it, it was saying "the returned pointer value is const". But non-class type rvalues are not modifiable (inherited from C), and thus the Standard says non-class type rvalues are never const-qualified (right-most const was ignored even tho specified by you) since the const would be kinda redundant. One doesn't write it - example:
int f();
int main() { f() = 0; } // error anyway!
// const redundant. returned expression still has type "int", even though the
// function-type of g remains "int const()" (potential confusion!)
int const g();
Notice that for the type of "g", the const is significant, but for rvalue expressions generated from type int const the const is ignored. So the following is an error:
int const f();
int f() { } // different return type but same parameters
There is no way known to me you could observe the "const" other than getting at the type of "g" itself (and passing &f to a template and deduce its type, for example). Finally notice that "char const" and "const char" signify the same type. I recommend you to settle with one notion and using that throughout the code.
In C, because function return values, and qualifying values is meaningless.
It may be different in C++, check other answers.
const int i = (const int)42; /* meaningless, the 42 is never gonna change */
int const foo(void); /* meaningless, the value returned from foo is never gonna change */
Only objects can be meaningfully qualified.
const int *ip = (const int *)&errno; /* ok, `ip` points to an object qualified with `const` */
const char *foo(void); /* ok, `foo()` returns a pointer to a qualified object */
None of the previous answers actually answer the "right way to do it" part of the question.
I believe that the answer to this is:
char const * foo( ){
which says you are returning a pointer a constant character.