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定义中。
[编辑] 感谢您迄今为止收到的出色答复。
总结一下:
- typedef 强制大小 N 的最初假设是完全错误的。它的行为基本上与普通指针相同。
-
const
属性 的行为也与指针相同 (与指针类型的 typedef 形成鲜明对比,如下面的@random 所强调)
- 要强制执行大小(这不是最初的问题,但现在变得非常重要...),请参阅 Jonathan's answer
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 结尾的用户自定义名称,它们保留用于将来扩展)
在 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定义中。
[编辑] 感谢您迄今为止收到的出色答复。 总结一下:
- typedef 强制大小 N 的最初假设是完全错误的。它的行为基本上与普通指针相同。
-
const
属性 的行为也与指针相同 (与指针类型的 typedef 形成鲜明对比,如下面的@random 所强调) - 要强制执行大小(这不是最初的问题,但现在变得非常重要...),请参阅 Jonathan's answer
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 结尾的用户自定义名称,它们保留用于将来扩展)