21

In C, it's possible to typedef an array, using this construction :

typedef int table_t[N];

Here, table_t is now defined as an array of N int. Any variable declared such as table_t t; will now behave as a normal array of int.

The point of such construction is to be used as an argument type in a function, such as :

int doSomething(table_t t);

A relatively equivalent function prototype could have been :

int doSomething(int* t);

The merit of the first construction is that it enforces N as the size of the table. In many circumstances, it's safer to enforce this property, rather than relying on the programmer to properly figure out this condition.

Now it's all good, except that, in order to guarantee that the content of table will not be modified, it's necessary to use the const qualifier.

The following statement is relatively simple to understand :

int doSomething(const int* t);

Now, doSomething guarantee that it will not modify the content of the table passed as a pointer. Now, what about this almost equivalent construction ? :

int doSomething(const table_t t);

What is const here ? the content of the table, or the pointer to the table ? If it's the pointer which is const, is there another way (C90 compatible) to retain the ability to define the size of the table and to tell that its content will be const ?

Note that it's also necessary sometimes to modify the content of the table, so the const property cannot be embedded into the typedef definition.

[Edit] Thanks for the excellent answers received so far. To summarize :

  • The initial assumption of typedef enforcing size N was completely wrong. It basically behaves the same as a normal pointer.
  • The const property will also behave the same as if it was a pointer (in stark contrast with a typedef to a pointer type, as underlined by @random below)
  • To enforce a size (which was not the initial question, but end up being quite important now...), see Jonathan's answer
Community
  • 1
  • 1
Cyan
  • 13,248
  • 8
  • 43
  • 78
  • 1
    It doesn't matter if you declare the argument as an array of `N` entries, it will still be a pointer, and the size `N` is discarded. – Some programmer dude Jun 29 '15 at 06:25
  • 7
    If I understood your supposition of "enforces N as the table size", its wrong. [See counter-example](http://ideone.com/p5KP0N). No such enforcement takes place. – WhozCraig Jun 29 '15 at 06:27
  • [Contents are constant](http://ideone.com/vUPzYf) – Mohit Jain Jun 29 '15 at 06:30
  • *"..Any variable declared such as table_t t; will now behave as a normal array"*... integer array to be specific. – WedaPashi Jun 29 '15 at 06:37
  • You might find [Write the prototype for a C function that takes an array of exactly 16 integers](http://stackoverflow.com/questions/4710454/) informative. – Jonathan Leffler Jun 29 '15 at 06:52
  • "The point of such construction is to be used as an argument type in a function" - no it isn't , you gain nothing by using it as function argument type. – M.M Jun 29 '15 at 07:09
  • Thanks for the link @Jonathan, it's an excellent complementary answer. – Cyan Jun 29 '15 at 07:16
  • "Hence the const property will behave the same" - it's still an array type. For example, `sizeof(table_t)` is `N*sizeof(int)`, not `sizeof(int *)`. Arrays evaluate to pointers in many contexts, but they're not actually pointers. – Random832 Jun 29 '15 at 13:19
  • As it happens, const does _not_ act the same. It acts _kind of_ the same as `const int *`, but if the type had been `typedef int *table_t`, then `const table_t` would have acted instead like `int * const`, which is very different. – Random832 Jun 29 '15 at 13:25
  • @random, this would be in contradiction with Jens Gunstedt statement – Cyan Jun 29 '15 at 21:35
  • @Chan Not really. `int const *` is the same as `const int *`, and different from `int * const`. The last one makes the pointer type constant. The statement I was replying to was incorrectly suggesting that table_t was actually a pointer type, and that the results are the same as it would be for const pointer_t (with pointer_t defined as in my answer below). – Random832 Jun 29 '15 at 21:37
  • > For example, sizeof(table_t) is N*sizeof(int), not sizeof(int *). In the context of `int doSomething(table_t t);`, how would evaluate `sizeof(t)` then ? – Cyan Jun 29 '15 at 21:44

5 Answers5

13

First, you are mistaken, the function prototypes

int doSomething(table_t t);
int doSomething(int* t);

are exactly equivalent. For function parameters, the first array dimension is always rewritten as a pointer. So there is no guarantee for the size of the array that is received.

const-qualification on arrays always applies to the base type of the array, so the two declarations

const table_t a;
int const a[N];

are equivalent, and for functions parameters we have

int doSomething(const table_t t);
int doSomething(int const* t);
psmears
  • 26,070
  • 4
  • 40
  • 48
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
10

The content of the table will be constant. Easily checked with this code.

#include<stdio.h>

typedef int table_t[3];
void doSomething(const table_t t)
{
    t++;    //No error, it's a non-const pointer.
    t[1]=3; //Error, it's a pointer to const.

}

int main()
{
    table_t t={1,2,3};
    printf("%d %d %d %ld",t[0],t[1],t[2],sizeof(t));
    t[1]=5;
    doSomething(t);
    return 0;
}
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
Raman
  • 2,735
  • 1
  • 26
  • 46
5

Array types and pointer types are not 100% equivalent, even in this context where you do ultimately get a pointer type for the function parameter. Your mistake is in assuming that const would have acted the same way if it were a pointer type.

To expand on ARBY's example:

typedef int table_t[3];
typedef int *pointer_t;

void doSomething(const table_t t)
{
    t++;    //No error, it's a non-const pointer.
    t[1]=3; //Error, it's a pointer to const.
}

void doSomethingElse(const pointer_t t)
{
    t++;    //Error, it's a const pointer.
    t[1]=3; //No error, it's pointer to plain int
}

It does act similarly to const int *, but const pointer_t is instead equivalent to int * const.

(Also, disclaimer, user-defined names ending with _t are not allowed by POSIX, they're reserved for future expansion)

Random832
  • 37,415
  • 3
  • 44
  • 63
  • That's a good point. The initial question was limited to `typedef int table_t[N];` example (hence the first one). That's nonetheless interesting to underline the difference with `typedef int *pointer_t;`. – Cyan Jun 29 '15 at 21:39
4

The merit of the first construction is that it enforces N as the size of the table.

I'm not sure what you mean here. In what contexts would it "enforce" it? If you declare a function as

int doSomething(table_t t);

array size will not be enforced. I order to enforce the size, you'd have to go a different route

int doSomething(table_t *t); // equivalent to 'int (*t)[N]'

What is const here ?

As for const... When const is applied to array type it "drops down" all the way to array elements. This means that const table_t is an array of constant ints, i.e. it is equivalent to const int [N] type. The end result of this is that the array becomes non-modifiable. In function parameter declaration context const table_t will be converted into const int *.

However, note one peculiar detail that is not immediately obvious in this case: the array type itself remains non-const-qualified. It is the individual elements that become const. In fact, it is impossible to const-qualify the array type itself in C. Any attempts to do so will make const-qualification to "sift down" to individual elements.

This peculiarity leads to rather unpleasant consequences in array const-correctness. For example, this code will not compile in C

table_t t;
const table_t *pt = &t;

even though it looks quite innocently from the const-correctness point of view and will compile for any non-array object type. C++ language updated its const-correctness rules to resolve this issue, while C continues to stick to its old ways.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

The standard 6.7.6.3 says:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’

Meaning that when you declare a function parameter as a const int array type, it decays into a pointer to const int (first element in array). Equivalent to const int* in this case.

Also note that because of the above mentioned rule, the array size specified adds no additional type safety! This is one big flaw in the C language, but that's how it is.

Still, it is good practice to declare the array with fixed width like you have, because static analysers or clever compilers may produce a diagnostic about different types.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Good point. Static analyzers tend to be much more complete at finding overflow errors nowadays. An indication of size can indeed hint them. – Cyan Jun 29 '15 at 07:29