1

I know in C we have two different types of syntax when it comes to pointers parameters. I just wonder if they are really the same. Consider this little program where I have these two functions fx1 and fx2 that take pointers parameters with a different sintax :

void fx1( int* p ){

    *p = 10;

}

void fx2( int p[] ){

    *p = 10;

}

int main(){

    int a = 20;
    
    fx1(&a);
    fx2(&a);

alessio solari
  • 313
  • 1
  • 6
  • Yes, the compiler will treat the argument declaration `int p[]` and `int *p`. With that said, the different style could be used to add some semantic meaning. Like for example use `int *p` when you have a pointer to a single `int` value, and `int p[]` when you have an array of `int` elements. But in the end, it's up to you to decide on the style you prefer, unless company style-guides tell you how it should be done. – Some programmer dude Aug 01 '23 at 08:41

6 Answers6

5

They are not entirely the same. Consider this code:

typedef struct foo T;

void fx1(T *p)  { }
void fx2(T p[]) { }

When compiling this, Clang accepts the first function without complaint but reports “array has incomplete element type 'T' (aka 'struct foo')” for the second.

That is because the element type of an array must have complete type, per C 2018 6.7.6.2 1, and Clang applies that rule before the rule in C 2018 6.9.1 7 that an array parameter is adjusted to be a pointer.

Of course, the first function could not operate on what p points to, *p, without completing the type T. However, this situation could arise in functions that merely pass the pointer to other functions that know the complete type or in functions that convert the pointer to a different type. For example, the comparison routine passed to qsort may be declared as int compare(const void *a, const void *b); and could not be declared as int compare(const void a[], const void b[]);.

A more arcane difference is that the array grammar allows this:

void fx2(int p[static 3]);

which says the argument passed for p must point to at least three elements. There is no corresponding grammar for pointer parameters.

And a yet more esoteric difference occurs with this:

void fx2(int p[foo()]) {}

This nominally declares p as an array with a variable number of elements, and there is no corresponding grammar for a pointer parameter. And it is troublesome because the C standard is unclear about whether the adjustment of p from an array to a parameter occurs before foo() expression is evaluated. Clang evaluates it and GCC does not.

Aside from the issues above, the parameter p of fx2 is, after adjustment, identical to the parameter p of fx1.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    Interesting edge case I didn't know about. Best answer. – Petr Skocik Aug 01 '23 at 09:20
  • Eric, Given "That is because the element type of an array must have complete type, per C 2018 6.7.6.2 1, and Clang applies that rule before the rule in C 2018 6.9.1 7 that an array parameter is adjusted to be a pointer." --> does a compiler that does not likewise apply those rules compliant? .. or an unresolved difference in C spec interpretation? – chux - Reinstate Monica Aug 01 '23 at 17:51
3

Yes, they do exactly the same and it's just a matter of preference. Although you might wanna consider the following case.

In most cases when you define an array you explicitly define it's size:

int array[SOME_SIZE];

Where as when you use the pointer syntax you don't

int* array;

One important notice is when using function pointers, then these syntaxes do differ, but that's beyond the scope of your question. If interested I suggest you take a look at this stack overflow thread

Joerie
  • 77
  • 8
1
#include <stdio.h>

void fx1( int* p ) { *p = 10; }

void fx2( int p[] ) { *p = 30; }

int main( )
{
    int a = 20;
    fx1( &a );
    printf( "fx1 a = %d\n", a );
    fx2( &a );
    printf( "fx2 a = %d\n", a );
}

output

fx1 a = 10
fx2 a = 30

Both methods have the same effect: The address of the formal parameter points to the address of the incoming parameter.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Wildptr
  • 15
  • 3
1

Here's an empirical proof:

void a( int *p );
void a( int p[] );

is accepted by C compilers without complaint. So is

void b( int (*p)[3][4] );
void b( int p[][3][4] );

for a demonstration that only the innermost dimension decays and

void c( int (*fn)(void) );
void c( int fn(void) );

for a demonstration that a very similar thing to pointer adjustment happens with function pointers when they're put as function parameters.

If the function declarations in these pairs were incompatible, compilers would have to complain about the conflicting declarations, but they don't, ergo the declarations are compatible: https://godbolt.org/z/GsPYdjzTK

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 1
    Re “If the items in these pair were incompatible, compilers would have to reject the conflicting declarations”: There are two minor technical errors in this. One, there is a minor allowance of non-compatible types: They may be differently qualified, per C 2018 6.7.6.3 15. For example, `const volatile int` and `int` are not compatible types, but the types of `f` in `void f(const volatile int x);` and `void f(int x);` are compatible. (Also, the types `int (*fn)(void)` and `int fn(void)` are in fact incompatible; it is just that the rule for whether the functions they are parameters to are… – Eric Postpischil Aug 01 '23 at 13:16
  • 1
    … compatible says it depends on the parameter types after adjustment, not the declared parameter types.) Two, the compiler is not required to reject conflicting declarations. Conflicting declarations violate the constraint in 6.7 4, which means the compiler is required to issue a diagnostic message, per 5.1.1.3 1, but it is allowed to accept it. – Eric Postpischil Aug 01 '23 at 13:18
1

In the C language arrays are passed as pointers to the first element of the array so both are equivalent.

IMO (especially for beginners) void foo(int array[]) should be avoided. People think (because they see the array syntax) that they can use sizeof(array)/sizeof(array[0]) to take its size, which is not possible.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
0___________
  • 60,014
  • 4
  • 34
  • 74
0

The answer is YES!

in pre K&R C language definition, they were so equivalent that the asterisk one was missing at all (pointer declaration was only done by appending [] to the defined pointer object name). In actual C, normal global or automatic variables you shouldn't even use that definition with brackets, because that results in an unclear, unspecified length array type definition that results normally in a one element array declaration (array size completes to one element when used). But for a function parameter declaration, they are completely equivalent, even:

void f(int param[10]);  /* complete array type */

and

void f(int *param); /* pointer */

are exactly equivalent (exactly meaning exactly the same thing) because of array decayment into pointer (which consists in the rewriting of one dimensional arrays into pointers of the array cell type.) This allows you to pass an array by just using its name and solves the problem of using the array name itself as a pointer to one array cell value.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31