int v, *w, x[5], *y[5], (*z[5])(int, int);
Where v is an int, w is a pointer, x is an array, y is an array of pointers, z is an array of function pointers, etc.Similarly, typedef is also just a keyword in front of a regular declaration.
int foo[5];
typedef int foo[5];
int bar(void);
typedef int bar(void);
Now you can use `bar *` as a function pointer.The entire language works like this.
int v;
means that `v` is an `int`. int *w;
means that `*w` is an `int`, meaning `w` is a pointer to an `int`. int *y[5]
(note that `◌[]` has higher precedence than `*◌`, so this is `*(y[5])`) means that `*y[5]` is an `int`, so `y[5]` is a pointer to an `int`, meaning `y` is an array of `int` pointers. int (*(*kitchensink[5])(int, int))[6];
means that `(*(*kitchensink[5])(int, int))[6]` is an int, so- `*(*kitchensink[5])(int, int)` is an array of `int`.
- `(*kitchensink[5])(int, int)` is a pointer to array of `int`.
- `kitchensink[5]` is a function pointer to a function that takes `(int, int)` and returns a pointer to an array of `int`.
- `kitchensink` is an array of function pointers to functions that take `(int, int)` and return a pointer to an array of `int`.
But some misguided style guides demand the misleading `int* w;`, and then act surprised by `int* w, x`;
(Most style guides tell you to declare one name per line anyway...)
I'm sad now.
How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this, it makes sense, there's no reasonable alternative. How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers" (actually it's worse because you have to find the right possibly-empty array of existing array specifiers first, just because there's a list of array specifiers somewhere in the type doesn't mean it's the one you should be prepending to).
"Declaration follows use" immediately goes out the window when faced with typedeffed types being used as the base type, or (as mentioned) generics in descendant languages of C. Instead you get "declaration builds up a type by wrapping layers around a core, use breaks down a type layer by layer starting from the outside" (so, necessarily, they mirror each other). C could have worked that way, and it would have made more sense.
"Declaration follows use" is the type level equivalent of taking off your socks before taking off your shoes because that's the order in which you put them on.
There absolutely are reasonable alternate ways to represent ordered data that don't involve templates. The way that C does it makes sense in most cases, and if you are looking at something that you cannot understand, you are looking at bad code.
> "Declaration follows use" immediately goes out the window when faced with typedeffed types being used as the base type
Typedefs are an abstraction. If you create a typedef, it is usually because you only want to handle the data as a whole, passing it to helper functions that remove the typedef. Also, declaration of use does not break down with typedefs:
typedef char *(*fn)(int, char *);
fn my_fn;
char *s = (*my_fn)(0, ""); // Proper use
> "Declaration follows use" is the type level equivalent of taking off your socks before taking off your shoes because that's the order in which you put them on.Please give me an example of some C code where this is the case.
std::array isn't a thing in C, so you don't.
huh? where is the extra layer?
std::array<int, 5> array_of_ints = { 1, 2, 3, 4, 5 };
>How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers"?
int c_style_array[5] = {2, 3, 5, 7, 11}; std::array<std::array<int, 3>, 5> array_of_ints;
int c_style_array[5][3];
But the C-style array is more readable, so I am not sure what the complaint really is about.int c_style_array[2][5][3];
There is also no jumping back and forth. C declarations are also recursively constructed. One can complain about the irregularity of pointers syntax.