0

I'm learning about the pointers in C. I don't understand why this code fails during the compilation process.

#include <stdio.h>

void set_error(int *err);

int main(int argc, const char * argv[])
{
    const char *err;
    set_error(&err);
    return 0;
}


void set_error(int *err) {
    *err = "Error message";
}
objc_bd
  • 247
  • 1
  • 4
  • 11

7 Answers7

3

You declare the function to expect a pointer-to-int (int *). But you give it a pointer-to-pointer-to-char and set_error treats it as such. Change the declaration thusly:

void set_error(const char ** err)

If you had compiled with warnings enabled (-Wall for GCC) it would give the following warnings:

In function 'main':
warning: passing argument 1 of 'set_error' from incompatible pointer type [enabled by default]
set_error(&err);
^
note: expected 'int *' but argument is of type 'const char **'
 void set_error(int *err);
      ^
In function 'set_error':
warning: assignment makes integer from pointer without a cast [enabled by default]
  *err = "Error message";
       ^
Kninnug
  • 7,992
  • 1
  • 30
  • 42
  • +1 given what the OP's original code was trying to do (return a `char const*` as an **out-parameter**) this is the only answer so far that actually addresses that. – WhozCraig Dec 04 '13 at 19:27
2

Your function expects int * type argument but you are passing to it const char ** type argument.
Change your function declaration to

void set_error(const char **err);  
haccks
  • 104,019
  • 25
  • 176
  • 264
  • I'm not the down-voter, but the only thing left to fix in this is the type being passed in the description, its `const char**`, not `char**`. (the prototype is correct). I somewhat doubt the down-voter is still around, but if they are, that may be the thing they're waiting on. – WhozCraig Dec 04 '13 at 19:35
1

One problem is that set_error expects an int * parameter, but you're passing the address of a char *, which makes it a char **. In addition, as noted by @Kninnug there's a buffer overwrite problem here which needs to be dealt with. Try rewriting your code as:

#include <stdio.h>
#include <string.h>

void set_error(char *err, size_t errbuf_size);

int main(int argc, const char * argv[])
{
    char err_buf[1000];
    set_error(err_buf, sizeof(err_buf));
    printf("err_buf = '%s'\n", err_buf);
    return 0;
}


void set_error(char *err, size_t errbuf_size) {
    strncpy(err, "Error message", errbuf_size-1);
}

As you'll notice in the rewritten version of set_error, another problem is that you can't just assign a value to a pointer and have the target buffer changed - you need to use the string functions from the standard library (here I'm use strncpy to copy the constant "Error message" to the buffer pointed to by the char * variable err). You may want to get familiar with these.

Share and enjoy.

  • Beware that there is a possibility for a buffer-overflow here if the `err` argument passed to `set_error` isn't big enough. – Kninnug Dec 04 '13 at 19:33
1

The issue you have unearths an important facts about strings in C. It also raises an interesting fact about scoping.

1. There is no such thing as a string in C; only a pointer to an array of characters.

Therefore, your statement *err = "Error message"; is wrong because by derefencing err you're not getting to the value of the string, but it's first character. (You can't quantify the 'value of a string' in C because there's no such thing as a string in C)

*err is actually undefined because nothing is yet assigned.

Note that the usual definition of a string is const char * or char * so I've changed this from what you had for the note below:

#include <stdio.h>

int main(void){
    char * a = "hello";
    if (*a == 'h'){
       printf("it's an 'H'\n");
    }
    else{
        printf("no it isn't\n");
    }
}

You'll see that *err actually returns the value of the first character because a[0] == *a

2. You cannot return pointers to locally scoped data in C

set_error() has the correct intentions, but is doomed to fail. Although "Error message"looks like a value, it is actually already a pointer (because strings in C are pointers to character arrays, as mentioned above).

Therefore, taking (1) into account you might expect to be able to do this:

void set_int(int *myint) {
    *myint = 1; //works just fine because 1 is a value, not a reference
}

void set_error(char *err) {
    // doesn't work because you're trying to assign a pointer to a char
    *err = "Error message"; 

void set_error_new(char *err) {
    //doesn't work because when the function returns, "Error Message" is no longer available on the stack" (assignment works, but when you later try to get at that data, you'll segfault
    err = "Error message"; 
}

You need to take a different approach to how you play with so-called 'strings' in C. Think of them as a pointer to a character array and you'll get better at understanding these issues. Also see C: differences between char pointer and array

Community
  • 1
  • 1
0

Firstly you have to change your function's declaration to

void set_error(char **err);

The body of the function is the same. Also you declared err variable as const char *err and tried change it. It generates a warning.

Deck
  • 1,969
  • 4
  • 20
  • 41
0

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.
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • while the main discussion you make is excellent, if you try and use `*err` after the call to `set_error()` you'll get undefined behaviour, because the `"Error Message"` literal is declared within the scope of the function. On my system (Linux amd64) adding `printf("%s"\n", err);` before `main()` returns causes a segfault. –  Dec 04 '13 at 23:59
  • String literals have static storage duration (6.4.5/6), and should be visible over the entire program; the behavior should be well-defined. I don't understand why you would get a segfault (I do not, for what it's worth). – John Bode Dec 05 '13 at 12:43
  • +1 for the spec and good arguments. I was confused by this too. I thought literals were stored in the TXT area. I took a look at the code again, and realised that as the pointer itself is passed by value, only the local pointer is being modified. we need `set_error(char ** err);` See http://pastebin.com/7RVs0upy for what works on my machine. –  Dec 05 '13 at 15:56
-1

1st error--> You are noticing is due to the fact that your function expects a pointer to int and you are passing a pointer to const char

2nd error--> You dereferenced the pointer and inserted the value "Error Message" which is a string and you pointer was pointer to char.

3rd error--> set_error(&err); --> This statement is wrong as err itself stores an address so there is no need to put & putting & means you are passing the address of the pointer *err and not the address which it is holding. So try this.

include <stdio.h>

void set_error(const char* err[]);    //Function Declaration 

int main()
{
    const char* err[1000];
    set_error(err);
    printf("%s",*err);
    return 0;
}


void set_error(const char* err[])
{ 
     *err = "Error Message";
}
APan
  • 364
  • 1
  • 10
  • Before posting code it's always a good idea to compile and run it. In this case the compile blows off at line 1 - the preprocessor directive should be `#include` rather than `include`. Further, it looks like the definition of `err` may not be doing quite what was intended - e.g. was the intent **really** to declare `err` as an array of 1000 character pointers? ??? I'm afraid the comment labeled "2nd error" is not correct - in C there is no such thing as a "string", per se; 'strings' are simply arrays of characters terminated by a NUL and thus using a `char *` to point to one is correct usage. – Bob Jarvis - Слава Україні Dec 05 '13 at 18:40