The logic behind C declarations (and the only correct way to declare pointers)

altermetax3 pts0 comments

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...

variable type function declare expression pointer

Related Articles