1

Why does the following code terminate to a segmentation fault why the alternate version i.e. commented code, does not? The two versions of the code look the same to me. What am I missing?

    #include <stdio.h>
  1 
  2 void get_input(char**); // void get_input(char*);
  3 
  4 int main(void)
  5 {
  6   char name[20];
  7   get_input((char**)&name); //get_input(name);
  8   printf("%s", name);
  9   
 10 }
 11 
 12 void get_input(char** m)//get_input(char* m)
 13 {
 14   scanf("%s", *m); // scanf("%s", m);
 15 }
6
  • 8
    name is not a pointer. &name is not a char **. Wishing it (by casting) doesn't make it so. Commented Oct 16, 2015 at 18:19
  • @EOF name is a pointer Commented Oct 16, 2015 at 18:21
  • 6
    @amchacon: *sigh*. No. It's an array, which means it decays into a pointer under certain conditions. Lookie here, C11 draft standard 6.3.2.1 Lvalues, arrays, and function designators, Section 3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.[...]. Note the explicit mention of &. Commented Oct 16, 2015 at 18:22
  • 3
    Cast means "shut up I know what I'm doing". Consider not saying that when you don't actually mean that. Commented Oct 16, 2015 at 18:23
  • @amchacon Please don't get confused with decaying here . Array and pointers are both different things . Commented Oct 16, 2015 at 18:38

3 Answers 3

5

name is an array of characters. Its type is char[20].

In certain cases arrays decay into pointers. This is not one of those cases.

The C standard specifically mentions that an argument of the address-of operator does not decay. The result of applying the address-of operator to an array name is, unsurprisingly, is the address of the array.

In this case &name has the type char (*)[20]. This type is very different from char**. The former describes a pointer that points to a memory location that contains 20 characters. The latter describes a pointer that points to a memory location which contains a pointer that points to another memory location that contains a character. You cannot cast one to the other and hope it will work.

Sign up to request clarification or add additional context in comments.

1 Comment

i hope you wouldn't mind me choosing the answer of ricovox as the accepted answer.
2

The answer by n.m. is correct. But if you want to know where the problem occurs "under-the-hood", take a look at the following code:

#include <stdio.h>
 void get_input(char**);

 int main(void)
 {
   char name[20];
   char* pname = name;
   char** ppname = (char**)&name; //this is what you were passing to get_input
   printf("Address of name: %d\n", name);
   printf("Value of pname: %d\n", pname);
   printf("Value of &name: %d\n", &name); 
   printf("Value of ppname: %d\n", ppname);
   get_input(ppname); 
   printf("Input: %s\n", name);
}

 void get_input(char** ppinput)
 {
   char* pinput = *ppinput;
   printf("Value of ppinput: %d\n", ppinput);
   printf("Value of pinput: %d\n", pinput);
   // The next line of code causes SEGMENTATION FAULT because 
   //   pinput is the value of name[0], which is garbage,
   //   so you don't own the memory it points to.
   scanf("%s", pinput); 
 }

If you compile and run that, you will see output similar to this:

Address of name: 2358816
Value of pname: 2358816
Value of &name: 2358816
Value of ppname: 2358816
Value of ppinput: 2358816
Value of pinput: 1
Segmentation fault

Take a look at the address of name and compare that with the value of pname (the pointer to name) and the value of ppname (which is defined as a char**). The OP was perhaps expecting that &name would return a pointer to pname (i.e. that &name returns a pointer to a pointer to the first char in the array). However, you can see that pname and ppname are the same! This is because the compiler interprets &name as a pointer to the array, which incidentally is at the same address as the first character in the array (which is what pname points to).

The get_input function is actually perfectly fine. If ppinput (which the OP called "m") were truly a pointer to a pointer to a char, the function would work as expected. The char** would be dereferenced to a char* and scanf would fill it without a problem.

But as shown above, ppname is actually a pointer to an array, which is the same as a pointer to the first element of that array. So ppname is, in effect, the same thing as pname. So in the OP's code, he was really passing a value that is effectively a char* to get_input, instead of a char**.

Then, when get_input dereferences ppinput, it gets the VALUE of the first character in the char[] array (which in my output happened to be 1) instead of a pointer to that value, which is what scanf expects.

The OP could do exactly what he was intending to do in his question by simply changing the line (from my code example):

char** ppname = (char**)&name;

to

char** ppname = &pname;

Now this value for ppname truly IS a pointer to a pointer, which is what the OP was expecting &name to be. After you make that change, you really will be passing a char** to get_input and the function will work as expected.

I hope that sheds more light on the issue. The important dogmatic points were already mentioned by n.m. but a practical note to take from this is that a reference to an array returns the same value as a pointer to the first element. I.e. (int)&name is (unintuitively) the same as (int)name when name is declared as an array. I say "unintuitively" because if you are not familiar with c++ arrays, you might expect that &var would always return a different value than var, but as this example shows, that turns out to not be true for arrays.

(Note that above, I've used int for pointer values and likewise %d for printf. This is bad practice in terms of portability, but for this illustration it should work and get the point across.)

1 Comment

nice. thank you for this very detailed and yes informative explanation. i am tempted to turn this one into the accepted answer.
1

char ** is a pointer to a pointer.when you pass the address of the array,it has type char (*)[20] which is incompatible with parameter of type char**.This is how you can correct the code :

#include <stdio.h>

void get_input(char* m); // void get_input(char*);

int main(void)
{
    char name[20];
    get_input(name); //get_input(name);
    printf("%s", name);

}

void get_input(char* m)//get_input(char* m)
{
    scanf("%s", m); // scanf("%s", m);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.