14
void check(void* elemAddr){
    char* word = *((char**)elemAddr);
    printf("word is %s\n",word);
}

int main(){
    char array[10] = {'j','o','h','n'};
    char * bla = array;
    check(&bla);
    check(&array);
}

Output:

word is john

RUN FINISHED; Segmentation fault; core dumped;

First one works, but second not. I don't understand why this happens.

Maroun
  • 94,125
  • 30
  • 188
  • 241
Ioane Sharvadze
  • 2,118
  • 21
  • 35

5 Answers5

7

The problem is, when we do &array, we are getting a char (*)[10] from an char [10], instead of a char **.

Before we do our experiment, I will emphasize that, when we pass an array as an argument to a function, C actually casts the array to a pointer. The big bucket of data is not copied.

Thus, int main(int argc, char **argv) is identical to int main(int argc, char *argv[]) in C.

This made it available for us to print the address of an array with a simple printf.

Let's do the experiment:

char array[] = "john";
printf("array:  %p\n", array);
printf("&array: %p\n", &array);

// Output:
array:  0x7fff924eaae0
&array: 0x7fff924eaae0

After knowing this, let's dig into your code:

char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);

bla is char *, and &bla is char **.

However, array is char [10], and &array is char (*)[10] instead of char **.

So when you pass &array as an argument, char (*)[10] acts like a char * when passing as an argument, as is said above.

Therefore **(char **) &bla == 'j' while *(char *) &array == 'j'. Do some simple experiments and you will prove it.

And you are casting void *elemAddr to a char ** and try to deference it. This will only work with &bla since it is char **. &array will cause a segfault because "john" is interpreted as an address as you do the cast.

Star Brilliant
  • 2,916
  • 24
  • 32
  • The correct phrasing is that when passed to a function, an array (or the address of the array) *decays* to a pointer - and your are right to a simple pointer :-). And the called function receives that pointer. – Serge Ballesta Jan 04 '15 at 12:01
3

For check(&bla); you are sending pointer to pointer

void check(void* elemAddr){
    char* word = *((char**)elemAddr);  // works fine for pointer to pointer
    printf("word is %s\n",word);
}

This is working fine.

But, for check(&array); you are passing pointer only

void check(void* elemAddr){
    char* word = *((char**)elemAddr);  // This is not working for pointer 

    char* word = *(char (*)[10])(elemAddr);   // Try this for [check(&array);]

    printf("word is %s\n",word);
}

Full Code--

Code for check(array);:

void check(void* elemAddr){
    char* word = *(char (*)[10])(elemAddr);
    printf("word is %s\n",word);
}

int main() {
   char array[10] = {'j','o','h','n'};
    check((char*)array);
  return 0;
}

Code for check(&bla);:

void check(void* elemAddr){
    char* word = *((char**)elemAddr);
    printf("word is %s\n",word);
}

int main() {
   char array[10] = {'j','o','h','n'};
   char* bla = array;
   check(&bla);
  return 0;
}
Shahriar
  • 13,460
  • 8
  • 78
  • 95
  • `char* word = ((char*)elemAddr); // Try this for [check(&array);]` Is undefined behavior. – 2501 Jan 03 '15 at 15:16
  • 1
    @AerofoilKite 2501 states that casting a `char (*)[10]` to `char *` is "undefined behavior" in C. Please understand that you can't use a C compiler to test for undefined behavior (as "undefined" includes "expected by Airfoil Kite"). Another compiler might produce different results. – Nikolai Ruhe Jan 03 '15 at 15:51
  • I downvoted because, in your answer, you just claim that one is this and the other is that but you do not explain the difference. – Nikolai Ruhe Jan 03 '15 at 15:55
  • One is used for pointer to pointer and another is used for only pointer.. Am i wrong? please make me clear not for vote.. – Shahriar Jan 03 '15 at 15:57
  • @MattMcNabb, Is it ok now? – Shahriar Jan 04 '15 at 11:30
  • In the third code block (when passing in `array`), you should cast to `char*`. – interjay Jan 04 '15 at 11:40
  • @interjay, Tnx.. Gradually its becoming a perfect code.. :) . But why `char*` is important here.. I am passing a pointer. Why should I cast? – Shahriar Jan 04 '15 at 11:44
  • 2
    Passing in the array is fine as `check(array)`, but in the function you should have `char* word = (char*)elemAddr;` or just `char* word = elemAddr;` (the cast from `void*` is optional in C). The reason: When you pass in `array`, it [decays](http://stackoverflow.com/questions/1461432/what-is-array-decaying) to a pointer of type `char*` with the value `&array[0]`. That's why you need to cast to `char*` in the function. – interjay Jan 04 '15 at 11:53
  • I see you have fixed the old error I pointed out 3 months ago. But as interjay has mentioned you have introduced a new one. – 2501 Mar 18 '16 at 12:11
2

The C specification says that array and &array are the same pointer address.

Using the name of an array when passing an array to a function will automatically convert the argument to a pointer per the C specification (emphasis mine).

6.3.2.1-4

Except when it is the operand of the sizeof 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. If the array object has register storage class, the behavior is undefined.

So calling func(array) will cause a pointer to char[] to be passed to the function. But there is a special case for using the address-of operator on an array. Since array has type "array of type" it falls into the 'Otherwise' category of the specification (emphasis mine).

6.5.3.2-3

The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. Otherwise, the result is a pointer to the object or function designated by its operand

So calling func(&array) will still cause a single pointer to be passed to the function just like calling func(array) does since both array and &array are the same pointer value.

Common-sense would lead you to believe that &array is a double pointer to the first element of the array because using the & operator typically behaves that way. But arrays are different. So when you de-reference the passed array pointer as a double pointer to the array you get a Segmentation fault.

T Johnson
  • 824
  • 1
  • 5
  • 10
1

This is not a direct answer to your question, but it might be helpful to you in the future.

Arrays are not pointers:


  • type arr[10]:

    • An amount of sizeof(type)*10 bytes is used

    • The values of arr and &arr are necessarily identical

    • arr points to a valid memory address, but cannot be set to point to another memory address


  • type* ptr = arr:

    • An additional amount of sizeof(type*) bytes is used

    • The values of ptr and &ptr are typically different, unless you set ptr = (type*)&ptr

    • ptr can be set to point to both valid and invalid memory addresses, as many times as you will


As with regards to your question: &bla != bla == array == &array, and therefore &bla != &array.

barak manos
  • 29,648
  • 10
  • 62
  • 114
-1

One problem is that your char array is NOT NECESSARILY going to be null-terminated. Since array is an automatic variable that is allocated locally on the stack, it is not guaranteed to be zeroed-out memory. So, even though you are initializing the first 4 chars, the latter 6 are left undefined.

However ...

The simple answer to your question is that &bla != &array so your check() function is assuming it will find null-terminated character arrays at 2 different addresses.

The following equations are true:

array == &array    // while not the same types exactly, these are equivalent pointers
array == bla
&array == bla
*bla == array[0]

&bla is never going to equal anything you want because that syntax references the address of the bla variable on the local stack and has nothing to do with its value (or what it points to).

Hope that helps.

alpartis
  • 1,086
  • 14
  • 29
  • Not my DV, but when you specify an initializer for some of an array's elements then the rest are initialized to 0. Also, your equations such as `array == &array` can be misleading because those are different types. – interjay Jan 04 '15 at 13:36
  • Ah, you're right regarding the array initialization -- I missed that along the way and just always did explicit initialization for clarity. Thanks for the reminder. – alpartis Jan 04 '15 at 14:34