The logic behind C declarations
The logic behind C declarations
and the only correct way to declare<br>pointers
2026-07-25
Every once in a while, I stumble upon people on the<br>Internet arguing about which of the following is the<br>“correct” way to write C variable declarations:
int *variable;<br>int* variable;
with the obligatory third person who will mention these<br>alternatives as a joke to make fun of the discussion:
int * variable;<br>int*variable;
Of course, the correct answer is “Match the style of the<br>existing codebase”; still, which style should one adopt when<br>starting a new codebase?
Those rooting for int* variable will say<br>that * is part of the type (“pointer to<br>int”). Those rooting for<br>int *variable will respond stating that, in the<br>declaration int* a, b, c, a will<br>end up being a pointer, while b and<br>c will be ints, therefore<br>* is strictly bound to the variable that sits<br>after it. Then someone will argue that this makes no sense<br>and it’s the reason why we should never declare more than<br>one variable in a single statement.
The correct answer is int *variable, but the<br>reason is a bit more nuanced than this.
How C declarations<br>work
The syntax of C declarations is more logical than it<br>seems; you just need to look at it from a different point of<br>view that is not obvious at a glance.
Every declaration is structured like this:
type [, expression [...]];
type is a type, such as int or<br>struct sockaddr_in (a primitive, a struct, a<br>union or something declared via typedef);
expression is an expression that contains<br>exactly one identifier that doesn’t already exist.
What the declaration expresses is: “Declare a new<br>variable such that the expression expression<br>results in a value of type type”.
For example:
// Declare "variable" such that the<br>// expression "variable" is of type "int".<br>int variable;
// Declare "variable" such that the<br>// expression "*variable" is of type "int".<br>int *variable;
When you type int *variable, you aren’t<br>saying that variable is an int*.<br>You are saying that *variable is an<br>int. That’s why the asterisk should be attached<br>to variable rather than to<br>int.
By the way, this logic is valid for any kind of C<br>declaration:
// Declare "variable" such that the<br>// expression "variable[10]" is of<br>// type "int".<br>int variable[10];
This is the reason why we write<br>int variable[] and not<br>int[] variable like they do in some other<br>languages like Java.
You may argue that using variable[10] after<br>the declaration above will result in undefined behavior, as<br>the last index of the array is 9, but that is<br>beyond the point. From a purely syntactical point of view,<br>variable[10] is an int<br>and will be treated as an int by the compiler.<br>The undefined behavior is purely a runtime concern.
Take a look at the following two declarations as<br>well:
// Declare "variable1" such that the<br>// expression "*variable1[10]" is of<br>// type "int".<br>int *variable1[10];
// Declare "variable2" such that the<br>// expression "(*variable2)[10]" is of<br>// type "int".<br>int (*variable2)[10];
variable1 will be an array of pointers to<br>int; variable2 will be a pointer<br>to an array of ints.
Lastly, let’s consider functions.
// Declare "function" such that the<br>// expression "function(a, b)" is of<br>// type "int".<br>int function(int a, float b);
This slightly breaks the logic, because you call the<br>function as function(a, b), not<br>function(int a, float b), but there<br>needs to be a way to specify the types of<br>parameters, and that was the most reasonable place from a<br>practical point of view.
Also note that in historical (pre-ANSI) C a function was<br>defined differently:
int function(a, b)<br>int a;<br>float b;<br>return a + b;
This is sounder from a theoretical point of view, but it<br>makes things annoying to define, so ANSI went for<br>int function(int a, float b).
Function pointers are declared just like functions, but<br>with an asterisk to indicate that you need to dereference<br>them before calling:
// Declare "variable" such that the<br>// expression "(*variable)(a, b)" is of<br>// type "int".<br>int (*variable)(int a, float b);
In reality, to call a function pointer you can skip the<br>asterisk and just use variable(a, b); this<br>shortcut can’t be carried over to the declaration, because<br>it would make it ambiguous with a function declaration.
Here’s a complex example that puts everything<br>together:
// An array of function pointers that<br>// return a pointer to int<br>int *(*variable[10])(int a, float b);
Specifying types on<br>their own
There are situations where you need to name a type on its<br>own, outside of a declaration. The most obvious example is<br>type casting. Based on the rules above alone, there is no<br>meaningful way to specify the name of a complex type (like a<br>pointer or an array) on its own.
The solution C chose is: simply use the syntax you’d use<br>to declare a variable of that type, but omit the<br>identifier.
// Cast "a" to an int<br>int b = (int)a;
// Cast "a" to a pointer to int<br>int *b = (int *)a;
// Cast "a" to a function pointer<br>int (*b)(int, float) =<br>(int...