0
#include <stdio.h>

// this works
void print_stuff (void* buf) {
    printf ("passed arg as buf*: %s\n", buf);
}

/* This works */
void print_stuff_3 (char* buf) {
    printf ("passed arg as char*: %s\n", buf);
}

// this does not work
void print_stuff_2 (char** buf) {
    printf ("%s\n", *buf);
}

int main () {
    char s [] = "hi";

    printf ("s = %s\n", s); 

    // these work
    print_stuff (&s);
    print_stuff_3 (&s);

    // this results in a Segfault
    print_stuff_2(&s);
    return 0;
}

I am a bit confused about the way things are passed around in C. I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behaviour happen?

In particular, print_stuff_2 segfaults, whereas I thought that print_stuff_3 would give an error.

EDIT: To clarify, I expected print_stuff(&s) and print_stuff_3(&s) to fail (while they succeed), while print_stuff_2(&s) fails, whereas I feel it should succeed.

Daniel Kats
  • 5,141
  • 15
  • 65
  • 102

3 Answers3

3

You need to remember that strings are not fundamental types in C. They are arrays of characters. Therefore

char s [] = "hi";

makes s a char * (in terms of variable type), i.e. a pointer to the first character of a 3 character array (h, i and NUL).

So in order to pass a pointer to the string, you what to use your print_stuff_3, as printf()'s %s argument takes exactly that (a pointer to the string, i.e. a pointer to the first character). Call this with print_stuff_3(s).

print_stuff works because a pointer is a pointer. It will be translated to a void * pointer on calling print_stuff, then printf()'s %s will convert it back to a char *. Call this with print_stuff(s).

print_stuff_2 doesn't work because you are taking the address of where s is stored. Had you written char *s = "hi"; that would work if you used print_stuff_2(&s). You'd pass the address of the pointer, then dereference that (to get the value of the pointer, i.e. the pointer to the first character) in by using *buf. Except buf then would be a poor choice of name, as you would be passing a pointer to a pointer to characters.

The complication is as follows. As it is, you are doing &s which just returns s when you have

char s [] = "hi";

(see How come an array's address is equal to its value in C? ), but returns the address at which the pointer variable s is stored on the stack if you have:

char *s = "hi";

Taking the address of an array doesn't really make sense (so evaluates to the address of the first element). You need to use char *s = "hi"; if you want to take the address of the pointer.

Community
  • 1
  • 1
abligh
  • 24,573
  • 4
  • 47
  • 84
  • But I thought (as you actually said) that `char []` is the same as `char*`. And `&(char *)` is `(char**)`. So why isn't `&(char[])` the same as `&(char *)`? – Daniel Kats Feb 10 '14 at 19:25
  • 1
    `char s[] = "hi";` does make `s` (the variable) a `char *` (i.e. a pointer to the first character) but that does not mean it is the same as `char * s = "hi"`. Consider for instance `sizeof s`. The first will return 3, the second the size of a `char *` pointer. Read: http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its-value-in-c - I've clarified the answer a little. – abligh Feb 10 '14 at 19:37
0

In C, array names are decays to pointer to its first element when passed to a function in most cases. When passing s to the function print_stuff, s decays to pointer to h. No need to pass it with &. &s is of pointer to array (char (*)[3]) type, i.e, it is giving the address of the entire array s.

In function call

print_stuff_3 (&s);  

your compiler should warn you

[Warning] passing argument 1 of 'print_stuff_3' from incompatible pointer type [enabled by default]   

I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behavior happen?

No. You thought wrong. &s is of type char (*)[3].

haccks
  • 104,019
  • 25
  • 176
  • 264
  • You should try running it. Both work. That's why I'm confused. – Daniel Kats Feb 10 '14 at 19:18
  • It will run but your compiler should through a warning I mentioned in my answer. – haccks Feb 10 '14 at 19:19
  • If you call `print_stuff_2` with `&s`, do you think it should run? If yes, try it. It does not. That was my feeling as well. – Daniel Kats Feb 10 '14 at 19:20
  • No. It should not. `&s` is of type `char (*)[3]`. `print_stuff_2` expects argument of type `char **`. You are passing wrong argument. – haccks Feb 10 '14 at 19:23
  • It was my understanding that arrays in C are pointers to the first element, and in particular that `char*` and `char[]` were identical. – Daniel Kats Feb 10 '14 at 19:25
  • Arrays are not pointers. `char *` and `char[]` are not identical except when they are parameter of a function. – haccks Feb 10 '14 at 19:27
  • 1
    @BlackSheep There is often some confusion about this, it's called "array pointer equivalence", and it rests very much on the nuance that "equivalent" does not mean "equal". http://c-faq.com/aryptr/aryptrequiv.html but in a nutshell: An array variable, `char a[]` can *decay* to a pointer in normal usage, and does so when given as a function parameter, `void f(char a[])` to prevent arrays passing by value (how much space on the stack would `char a[]` require?). – kfsone Feb 10 '14 at 20:58
0

void print_stuff (void* buf) & void print_stuff_3 (char* buf) In both functions, buf is of char * taking address as argument. Which should be print_stuff (s) & print_stuff_3 (s) respectively as s is the base address of char array s. So you shouldn't pass &s which is address of s.

As the below function buf is of type char **, it will expect address of address like print_stuff_2(&s) provided your declaration is char *s = "hi",

void print_stuff_2 (char** buf) {
    printf ("%s\n", *buf);
}
Sunil Bojanapally
  • 12,528
  • 4
  • 33
  • 46
  • What you are saying is that the first two functions should fail but the third one should work (i.e. `print_stuff_2` should work). But actually the exact opposite happens. – Daniel Kats Feb 10 '14 at 19:31
  • *As the below function buf is of type char **, it will expect address of address like*: Yes true, but it is not expecting `&s`, having type of `char (*)[3]`. – haccks Feb 10 '14 at 19:38