#include int main() { /* The last section established the difference between these four types: */ float a [10] ; // array float (*pa )[10] = & a ; // pointer to array float a0 = a[0] ; // element float *pa0 = &(a[0]); // pointer to element /* But the expression used for obtaining the pointer to the first element is pedantic. The root of all C programmer experience with arrays is the shortcut */ float *pa0x = a ; // (ok) /* which reproduces @pa0@, in type and value: */ assert( pa0 == pa0x ); /* The validity of this initialization is unsettling, in the context of the facts established in the last section. Notably, it initializes name @pa0x@ from expression @a@, when they are not of the same type: */ assert( sizeof(pa0x) != sizeof(a) ); void f( float x[10], float *y ) { static_assert( sizeof(x) == sizeof(void*) ); static_assert( sizeof(y) == sizeof(void*) ); } f(0,0); // reusing local var `float a[10];` float v; f( a, a ); // ok: two decays, one into an array spelling f( &v, &v ); // ok: no decays; a non-array passes to an array spelling char ca[] = "hello"; // array on stack, initialized from read-only data char *cp = "hello"; // pointer to read-only data [decay here] void edit(char c[]) { // param is pointer c[3] = 'p'; } edit(ca); // ok [decay here] edit(cp); // Segmentation fault edit("hello"); // Segmentation fault [decay here] void decay( float x[10] ) { static_assert( sizeof(x) == sizeof(void*) ); } static_assert( sizeof(a) == 10 * sizeof(float) ); decay(a); void no_decay( float (*px)[10] ) { static_assert( sizeof(*px) == 10 * sizeof(float) ); } static_assert( sizeof(*pa) == 10 * sizeof(float) ); no_decay(pa); }