C 中数组的 const 和 typedef

const and typedef of arrays in C

在 C 中,可以 typedef 一个数组,使用这种结构:

typedef int table_t[N];

这里,table_t现在定义为N个int的数组。任何声明为 table_t t; 的变量现在将表现为 int 的普通数组。

这种构造的要点是在函数中用作参数类型,例如:

int doSomething(table_t t);

相对等效的函数原型可能是:

int doSomething(int* t);

第一个构造的优点是它强制 N 作为 table 的大小。在许多情况下,强制执行此 属性 比依赖程序员正确找出此条件更安全。

现在一切都好了,只是为了保证table的内容不被修改,需要使用const修饰符

下面的语句比较容易理解:

int doSomething(const int* t);

现在,doSomething 保证它不会修改作为指针传递的 table 的内容。 现在,这个几乎等效的结构怎么样? :

int doSomething(const table_t t);

这里的 const 是什么? table 的内容或指向 table 的指针? 如果指针是 const,是否有另一种方法(C90 兼容)来保留定义 table 大小的能力并告知其内容将为 const?

请注意,有时也需要修改table的内容,因此const 属性不能嵌入到typedef定义中。

[编辑] 感谢您迄今为止收到的出色答复。 总结一下:

table 的内容将保持不变。使用此代码轻松检查。

#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;
}

首先,你错了,函数原型

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

完全等价。对于函数参数,第一个数组维度总是被重写为指针。所以无法保证接收到的数组大小。

const——对数组的限定总是适用于数组的基类型,所以两个声明

const table_t a;
int const a[N];

是等价的,对于函数参数我们有

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

标准 6.7.6.3 说:

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

意思是当你将函数参数声明为const int数组类型时,它会衰减为指向const int(数组中的第一个元素)的指针。在这种情况下相当于 const int*

另请注意,由于上述规则,指定的数组大小不会增加额外的类型安全性!这是C语言的一大缺陷,但就是这样。

不过,像您一样声明具有固定宽度的数组是个好习惯,因为静态分析器或聪明的编译器可能会产生关于不同类型的诊断。

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

我不确定你在这里的意思。在什么情况下会 "enforce" 呢?如果你声明一个函数为

int doSomething(table_t t);

数组大小将强制执行。我命令强制尺寸,你必须走不同的路线

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

What is const here ?

至于 const... 当 const 应用于数组类型时,它 "drops down" 一直到数组元素。这意味着 const table_t 是常量 int 的数组,即它等同于 const int [N] 类型。这样做的最终结果是数组变得不可修改。在函数参数声明上下文中 const table_t 将被转换为 const int *.

但是,请注意一个特殊的细节,在这种情况下不是很明显:数组类型本身仍然是非 const 限定的。是个元素变成了const。事实上,在 C 中不可能对数组类型本身进行 const 限定。任何这样做的尝试都会对单个元素进行 "sift down" 的 const 限定。

这种特性导致数组常量正确性方面的相当不愉快的后果。例如,此代码不会在 C 中编译

table_t t;
const table_t *pt = &t;

尽管从 const 正确性的角度来看它看起来很无辜,并且可以针对任何非数组对象类型进行编译。 C++ 语言更新了它的 const-correctness 规则来解决这个问题,而 C 继续坚持它的旧方式。

数组类型和指针类型不是 100% 等价的,即使在这种情况下您最终会得到函数参数的 a 指针类型。您的错误是假设 const 如果 指针类型,它会以相同的方式行事。

扩展 ARBY 的示例:

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
}

它确实与 const int * 类似,但 const pointer_t 相当于 int * const

(另外,免责声明,POSIX 不允许以 _t 结尾的用户自定义名称,它们保留用于将来扩展)