3

I want to understand where exactly in code an array gets converted to a pointer. For example:

void foo( int* pData, int len){}

int main(void){
    char data[] = "Hello world";

    foo( (int*)data, sizeof(data));

    return 0;
}

I know that an array decays to a pointer to the first element if it is assigned to a pointer. However in the example above, I typecast the array data to int* first before passing it in function and assigning it to a pointer. Does the conversion/decay to pointer occurs at the typecasting point ? If so, isn't it true to say that the typecasting operation has the same effect as using the assignment operator with respect to array conversion/decay? Also would sizeof(data) be equal to the address length or array length?

Thank you for help!

2 Answers2

1

The conversion of arrays to pointers in C is spelled out in section 6.3.2.1p3 of the C standard:

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. If the array object has register storage class, the behavior is undefined.

This means that the array is immediately converted to a pointer anywhere it is used except for the three cases listed above.

So applying the above to (int*)data, data is the operand of the typecast operator. Since this operator is not one of the ones listed above, data in this expression is converted from char [12] to char *, then the cast converts the char * to an int *.

Also, as mentioned above, the array is not converted when passed to sizeof. This means sizeof(data) evaluates to the size of char [12] which is 12.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Adding to @dbush, sizeof operator returns 12 here because data was initialized as an array in your example. That's the another inherent difference between arrays and pointers. –  Sep 21 '19 at 21:17
  • @Harveer - that's not quite true. `data` is initialised using a string literal. A string literal is not actually an array - but it is represented in memory as a statically allocated array as far as your program is concerned. And string literals are a special case in the language - it is possible to do `const char *pointer = "Hello World";` but not `const char *pointer2 = {'a', 'b'};` – Peter Sep 21 '19 at 22:32
  • The operand of `sizeof` in `sizeof(data)` is not `data`; it is `(data)`. A parenthesized expression is an expression, so the rule quoted in this answer would say that `data` is converted to a pointer to its first element. There are separate rules in the specification of a parenthesized expression that cover this. – Eric Postpischil Sep 21 '19 at 23:52
  • @Peter No, it's an array and is different than a pointer pointing to a string literal. Underneath, compiler would expand this "char data[]" to have the size equal to the string it is initialized with. see here: https://godbolt.org/z/0cmJPI. Here, compiler knows that the size of an array by looking at it's string literal size. You can try changing that to provide a static size of 3 and you will still see the same assembly. I hope this helps. –  Sep 22 '19 at 02:05
  • @Harveer - you're missing my point. I'm not suggesting a string literal is a pointer. – Peter Sep 22 '19 at 02:17
  • Probably I am not getting your point. But this is not a string literal. String literals are immutable as they get allocated in read-only memory of the process. But this array above is mutable. –  Sep 22 '19 at 02:35
  • @dbush is it correct to say that the array conversion is always evaluated and is not a one time operation done at the first opportunity the condition is valid for an array conversion? – onemogunner Sep 22 '19 at 02:41
  • @Harveer: Whether string literals are put in read-only memory is up to the C implementation. The C standard does not require it. – Eric Postpischil Sep 22 '19 at 03:04
  • Also, when we access an element using the expression data[2] for element 2 for example, data will be converted to pointer according to the rule above. Can someone confirm this is true please? – onemogunner Sep 22 '19 at 03:53
  • @user12100664 yes this is true. `data[2]` is exactly the same as `*(data + 2)`. – dbush Sep 22 '19 at 03:55
0

Outwith the declaration, you can consider data to be equivalent to a pointer to the start of the array, but with the following exceptions:

  • sizeof(data) will give you the size of the array in bytes.
  • _Alignof(data) will be the same as _Alignof(*data) (and both give you the alignment of the type of the array elements)
  • &data has the same value as just data, but have a type of char (*)[sizeof(data] so arithmetic will use the full size of the array. Eg &data+1 will give you the address after the whole array rather than the address of the second element. See How come an array's address is equal to its value in C?
  • you can't change it's value (ie in this sense it is equivalent to a char *const).

When you call your function, you are taking the value of data (ie the address of the start of the array) and typecasting to an int *. The resulting int * behaves like any other int * and the exceptions don't apply, this is the 'decay'.

Graeme
  • 2,971
  • 21
  • 26
  • &data has the type: ”char (*)[]” and does not have the same value as data. – Fredrik Sep 21 '19 at 21:59
  • @Fredrick, definitely the same value, both are the address of the array. Good catch on the type though. See https://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its-value-in-c – Graeme Sep 21 '19 at 22:12
  • `_Alignof(data)` is not a standard C expression. The C standard only defines `_Alignof` applied to a type name, not to an expression. It was a mistake in the standard to include mention of `_Alignof` in discussing the automatic array conversion, and that mistake was corrected in C 2018. – Eric Postpischil Sep 21 '19 at 23:55