I am playing with example code provided in one of the answers to typedef fixed length array .
The answer only states that the code fails, but does not explain, why. Could anyone provide an explanation?
#include <stdio.h>
typedef int twoInts[2];
void print(twoInts *twoIntsPtr);
void intermediate (twoInts twoIntsAppearsByValue);
int main () {
twoInts a;
a[0] = 0;
a[1] = 1;
print(&a);
intermediate(a);
return 0;
}
void intermediate(twoInts b) {
printf("im1: %d, %d\n", b[0], b[1]);
print(&b);
printf("im2: %d, %d\n", b[0], b[1]);
}
void print(twoInts *c){
printf("pr: %d, %d\n", (*c)[0], (*c)[1]);
}
Compiling this produces the following warnings:
a.c: In function ‘intermediate’:
a.c:19:11: warning: passing argument 1 of ‘print’ from incompatible pointer type [-Wincompatible-pointer-types]
19 | print(&b);
| ^~
| |
| int **
a.c:5:21: note: expected ‘int (*)[2]’ but argument is of type ‘int **’
5 | void print(twoInts *twoIntsPtr);
| ~~~~~~~~~^~~~~~~~~~
And the output is:
pr: 0, 1
im1: 0, 1
pr: 1854416416, 32767
im2: 0, 1
What I can't understand is why the "pr" lines differ. After all - both a
and b
have type twoInts
and both produce same results when index operator ([]
) is applied.
========== UPDATE ===========
I now realize that what I'm really interested in is what the actual value (actual bytes) does the argument c
has?
Let's say we have the following memory layout:
The original array is located at memory address 150:
m[150]=0
m[154]=1
Argument b
is in stack and has (let's say) address 140. My understanding is that it has type int*
and therefore probably holds address of first element of the array:
m[140]=150 // argument b
Calling the print() from main()
will probably put the argument c
at the very same address, but I'm not sure about the value:
m[140]=? // argument c, print() is called directly from main()
Calling the print() from intermediate()
will put argument c
at even earlier address, but would the value be 140 - the address of argument b
?
m[130]=? // argument c, print() called from intermediate()
CodePudding user response:
A function parameter that has an array type is adjusted by the compiler to pointer to the array element type.
So this function declaration
void intermediate (twoInts twoIntsAppearsByValue);
is adjusted by the compiler to the declaration
void intermediate ( int * twoIntsAppearsByValue);
Within the function
void intermediate(twoInts b) {
printf("im1: %d, %d\n", b[0], b[1]);
print(&b);
printf("im2: %d, %d\n", b[0], b[1]);
}
there is used this statement
print(&b);
where the argument expression has the type int **
while the corresponding parameter of the function has the type int ( * )[2]
.
void print(twoInts *c){
printf("pr: %d, %d\n", (*c)[0], (*c)[1]);
}
These pointer types are not compatible and the compiler shall issue a corresponding message.
In the function intermediate
the expression &b
points to its local variable (parameter) b
.
In the call of the function print
in main
print(&a);
the expression &a
points to the original array.
Here is a demonstration program that shows the difference.
#include <stdio.h>
typedef int twoInts[2];
int main( void )
{
twoInts a = { [0] = 0, [1] = 1 };
twoInts *p1 = &a;
int *p2 = a;
int **pp2= &p2;
printf( "*p1 = %p\n", ( void * )*p1 );
printf( "*pp2 = %p\n", ( void * )*pp2 );
printf( "*( int ( * )[2] )pp2 = %p\n", ( void * )*( int ( * )[2] )pp2 );
printf( "&p2 = %p\n", ( void * )&p2 );
}
The program output might look like
*p1 = 0x7ffef35980c8
*pp2 = 0x7ffef35980c8
*( int ( * )[2] )pp2 = 0x7ffef35980c0
&p2 = 0x7ffef35980c0
So you got the ouput
pr: 1854416416, 32767
because the address of the parameter b
was considered as the address of the array itself.
CodePudding user response:
I have played with my program a bit more and discovered two interesting properties:
- if I print the pointer that
print()
function receives then it is (unsurprisingly) differs between two invocations, the second pointer holding smaller address than the first (also somewhat unsurprisingly). - if I remove the address-taking in the
intermediate()
function and just callprint(b);
then the pointers become the same and therefore the output also becomes the same:
void intermediate(twoInts b) {
printf("im1: %d, %d\n", b[0], b[1]);
print(b);
printf("im2: %d, %d\n", b[0], b[1]);
}
Output:
pr: 0, 1
im1: 0, 1
pr: 0, 1
im2: 0, 1
While this is not really an answer to my question, this still provides enough insight.
The b
seems to be interpreted sometimes as int*
and sometimes as int**
- all within the same function. Therefore when I was taking address of it, it was effectively producing int***
.
So I guess I should be grateful that it didn't segfault at (*c)[1])
.