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.

3 Answers 3

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.

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

2 Comments

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 *)?
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: stackoverflow.com/questions/2528318/… - I've clarified the answer a little.
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].

7 Comments

You should try running it. Both work. That's why I'm confused.
It will run but your compiler should through a warning I mentioned in my answer.
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.
No. It should not. &s is of type char (*)[3]. print_stuff_2 expects argument of type char **. You are passing wrong argument.
@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". 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?).
|
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);
}

2 Comments

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.
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].

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.