Let's start by talking about types. In your main
function, you declare err
as
const char *err;
and when you call the set_error
function, you pass the expression &err
, which will have type "pointer to const char *
", or const char **
.
However, in your function declaration and definition, you declare the parameter err
as
int *err;
The types const char **
and int *
aren't compatible, which is why the compiler is yakking. C doesn't allow you to assign pointer values of one type to pointer variables of a different type (unless one is a void *
, which is a "generic" pointer type). Different pointer types are not guaranteed to have the same size or representation on a particular platform.
So that's where the compiler issue is coming from; what's the solution?
In C, string literals like "Error message"
have type char *
1 (const char *
in C++), so whatever I assign it to needs to have a type of either char *
or const char *
. Since we're dealing with a string literal, the latter is preferable (attempting to modify the contents of a string literal invokes undefined behavior; some platforms put string literals in read-only memory, some don't). So you need to make the following changes to your code2:
void set_error( const char **err )
{
*err = "Error message";
}
int main( void ) // you're not dealing with command line arguments, so for this
{ // exercise you can use `void` for your parameter list
const char *err;
set_error( &err );
return 0;
}
Remember that C passes all function arguments by value; this means that the formal parameter err
in set_error
is a different object in memory than the actual parameter err
in main
; if the code had been
void set_error( const char *err )
{
err = "Error message";
}
int main( void )
{
const char *err;
set_error( err );
return 0;
}
then the change to err
in set_error
would not be reflected in the variable err
in main
. If we want set_error
to modify the value of err
in main
, we need to pass set_error
a pointer to err
and dereference it in the function. Since the parameter err
has type const char **
, the expression *err
has type const char *
, which is the type we need for this assignment to succeed.
1. Actually, that's not true; string literals have type "N-element array of char
", where N
is the number of characters in the string plus the 0 terminator. However, for reasons that aren't really worth going into here, the compiler will convert expressions of array type to expressions of pointer type in most circumstances. In this case, the string literal "Error message"
is converted from an expression of type "14-element array of char
" to "pointer to char
".
2. A function definition also serves as a declaration; I typically put the called function before the caller so I don't have to mess with separate declarations. It means my code reads "backwards" or from the bottom up, but it saves some maintenance headaches.