我一直试图理解以下来自 C11 标准的引用
6.6 常量表达式
。
- 地址常量是一个空指针,一个指向指定静态对象的左值的指针 存储持续时间或指向函数指示符的指针;它应使用 一元和运算符或整数常量强制转换为指针类型,或隐式使用 数组或函数类型的表达式。数组下标 [] 和成员访问 。 和 ->运算符、地址 & 和间接寻址 * 一元运算符和指针强制转换可以 用于创建地址常量,但对象的值不应 使用这些运算符进行访问。
如何使用这些运算符访问对象的值,尽管它们不应该访问? 你能举一些例子吗?
为什么不应使用这些运算符访问对象的值?
谢谢。
它的意思是,形成地址常量的表达式不允许依赖于对象的值。
这类似于一个更简单的情况:
// at file scope
int x = 10; // OK, 10 is an integer constant expression
int y = x; // Error, x is the value of an object
对于地址常量的情况,它提醒我们,地址常量的计算不应该在形成该计算时读取对象的值,例如:
int a[5]; // OK
int *p = &a[1]; // OK
int *q = &a[1] + a[0]; // Error, uses value of a[0]
这个陈述最好用一个简单的例子来说明:
static struct {
int id;
int count;
} items[20];
int *midCount = &items[10].count;
上面,midCount
是一个地址常量,指向索引 10 处items
结构count
字段。地址运算符应用于成员访问的结果和数组下标运算符,以便获取地址。但是,根据规范,int
所述地址处的对象不会被访问。
下面是一个表达式,它访问与上述地址表达式位于同一位置的对象:
int count = items[10].count;
为什么不应使用这些运算符访问对象的值?
因为不需要计算地址常量。编译器可以计算数组内count
items
偏移量,并在加载期间应用此偏移量,当items
绑定到内存时,无需读取或写入位置本身。这类似于sizeof
运算符的行为,它不计算其表达式:
size_t sz = sizeof(items[10].count); // No memory access
我认为访问值的限制不是在运行时,而是在翻译期间。不允许在常量表达式中取消引用常量表达式指针。
例如
// global scope
int a = 0;
int *b = (a++ , &a);
我们不希望编译器在构建b
的初始值时跟踪a
的状态。
此外,编译器可能不知道该地址的实际值,因为它可能是在另一个具有一些常量初始值设定项的转换单元中定义的。