ISO/IEC 9899:2011 条款6.7.3——类型限定符
6.7.3 类型限定符
语法
1、type-qualifier:
const
restrict
volatile
_Atomic
约束
2、除了指针类型(其被引用的类型是一个对象类型)之外的类型,不应该被restrict限定。
3、被_Atomic修饰的类型不应该是一个数组类型或一个函数类型。
语义
4、与限定类型相关联的属性仅对作为左值的表达式有意义。[注:实现可以将一个非volatile的一个const对象放置到一个只读存储区域。此外,实现不需要为这么一个对象分配存储空间,如果其地址永远不被使用。]
5、如果同一个限定符在同一个specifier-qualifier-list中出现了多次,要么是直接出现,要么是通过多个typedef出现,那么行为就好比它仅出现一次。如果在一个specifier-qualifier-list中,其它限定符与_Atomic限定符一同出现,那么结果类型是如此限定的原子类型。
6、如果企图通过使用不具有const限定的类型的一个左值来修改一个用const限定类型定义的对象,那么行为是未定义的。如果企图通过使用一个不具有volatile限定类型的一个左值来引用一个用volatile限定类型定义的一个对象,那么行为是未定义的。[注:这应用于这么些对象,其行为就好比它们用限定类型定义,即便它们实际上没有在程序中定义为对象(比如在一个存储器映射的输入/输出地址上的一个对象)。]
7、一个具有volatile限定类型的对象可以用对实现未知的方法进行修改,或具有其它未知的副作用。因此,任一引用这么一个对象的表达式应该根据在5.1.2.3中所描述的抽象机的规则来严格计算。此外,在每个最后存储在对象中的值的顺序点应该遵循由该抽象机所规定的条约,除了作为由先前所提到的未知因素所修改的情况。[注:如何构成对一个具有volatile限定类型的对象的访问是由实现定义的。]
8、通过一个restrict限定的指针来访问的一个对象具有一个特殊的与该指针的关联性。这个关联定义在下面的6.7.3.1,要求所有对那个对象的访问,直接或间接使用那个特定指针的值。[注:比如,将由malloc所返回的一个值赋给一单个指针的语句,建立了在所分配的对象与该指针之间的关联性。]对restrict限定符(像register存储类一样)的使用目的在于提升优化,使得从所有组成一个遵循标准的程序的预处理翻译单元中删除该限定符的所有实例,而不改变其意义(也就是说,这是可观察到的行为)。
9、如果数组类型的说明包含了任一类型限定符,那么元素类型就这么被限定,而不是数组类型。如果函数类型的说明包含了任一限定符,那么行为是未定义的。[注:这两个都可能通过使用typedef而发生。]
10、对于两个相兼容的限定类型,两者都应该具有一个兼容类型的完全相同的限定版本;在一个说明符列表内的类型限定符的次序或限定符的次序不影响指定的类型。
11、例1 一个声明的对象
extern const volatile int real_time_clock;
可以被硬件修改,但不能赋值、递增、或递减。
12、以下声明和表达式描述了当类型限定符修改一个聚合类型时的行为:
const struct s { int mem; } cs = { };
struct s acs; // 对象acs是可修改的
typedef int A[][];
const A a = {{, , }, {, , }}; // const int的数组的数组
int *pi;
const int *pci; acs = cs; // 有效
cs = ncs; // 违背了用于=的可修改左值的约束
pi = &ncs.mem; // 有效
pi = &cs.mem; // 违背了用于=的类型约束
pci = &cs.mem; // 有效
pi = a[]; // 无效:a[0]具有类型“const int *”
13、例3 声明
_Atomic volatile int *p;
指定了p具有类型“指向volatile atomic int的指针”,一个指向volatile限定的原子类型的指针。
6.7.3.1 对restrict的正式定义
1、设D是一个普通标识符的声明,它提供了一种将一个对象P指派为一个restrict限定的指向类型T的指针的方式。[译者注:D表示为T * restrict P;]
2、如果D出现在一个语句块内,且不具有extern存储类型,那么将B设为该语句块。如果D出现在一个函数定义的形参声明列表中,那么将B设为相关联的语句块。否则,将B设为main的语句块(或在一个独立式环境中,在程序启动时所调用的函数)。[译者注:
void foo(void)
{
if( > )
// B语句块1
{
T* restrict P;
}
} void foo2(T* restrict P)
// B语句块2
{ } int main(void)
// B语句块3
{ }
]
3、一个指针表达式E被称为基于对象P,如果(在B的执行中的某个顺序点处,B的执行在对E的计算之前)对指向一个数组对象的一个拷贝的P修改为P之前所指向的对象,将改变E的值。[注:换句话说,E依赖于P自己的值,而不是通过P间接引用的一个对象的值。比如,如果标识符p具有类型(int **restrict),那么指针表达式p与p+1都基于由p所指派的restrict限定的指针对象,但*p和p[1]就不是如此。]
4、在对B的每次执行期间,设L为任一左值,且让&L基于P。如果L用于访问它所指派的对象X的值,且X也被修改(通过任何手段),那么要应用以下要求:T不应该用const限定。用于访问X的值的每个其它左值也应该让其地址基于P。出于此子条款的目的,每个修改X的访问应该被认作为也对P进行修改。如果赋给P的值是一个指针表达式E的值,该指针基于另一个restrict限定的指针对象P2,与语句块B2相关联,那么B2的执行应该在B的执行之前开始,要么B2的执行应该在赋值之前结束。如果这些要求没被满足,那么行为是未定义的。[译者注:比如:
int main(void)
{ // B语句块 T X;
T *L = &X;
T ** restrict P = &L;
// 这样,对于需要访问X的每个其它左值,也应该将其地址基于P,即
T *O = L; // O为另一个要访问X的指针变量
*O = ; // 通过指针变量O对X进行修改 // 赋给P的值是一个表达式E的值,E含有P2
P = > ? (++X, &L /** B2语句块 */) : NULL; // 语句块B2在赋值之前结束
}
]
5、这里,对B的一次执行是对程序执行的一部分,它相应于一个标量类型以及与B相关联的自动存储周期对象的生命周期。
6、一个翻译器可以自由地忽略对任一或所有restrict使用的连带影响。
7、例1 在文件域声明了以下代码:
int * restrict a;
int * restrict b;
extern int c[];
断言了,如果一个对象通过a、b或c其中之一进行访问,并且该对象在程序中的任一地方被修改,那么它永远都不会使用其它两个进行访问。
8、例2 在以下例子中的函数形参声明
void f(int n, int * restrict p, int * restrict q)
{
while (n-- > )
*p++ = *q++;
}
断言了,在函数的每个执行期间,如果一个对象通过指针形参其中之一进行访问,那么它就不会通过另一个进行访问。
9、从restrict的获益是,它们允许翻译器对函数f做有效依赖分析,而不检查程序中对f的任一调用。成本是,程序员必须对所有这些调用进行检查,以确保它们不会给出未定义行为。比如,下面在g中对f的第二个调用具有未定义行为,因为d[1]到d[49]的每一个元素既通过p又通过q进行访问。
void g(void)
{
// 译者注:f函数是上述代码片段中所定义的一个拷贝函数
extern int d[];
f(, d + , d); // 有效
f(, d + , d); // 未定义行为
}
10、例3 以下函数形参声明
void h(int n, int * restrict p, int * restrict q, int * restrict r)
{
int i;
for(i = ; i < n; i++)
p[i] = q[i] + r[i];
}
描述了一个未被修改的对象如何能通过两个用restrict限定的指针来混叠。特别地,如果a与b是两个不相交的数组,那么对形式h(100, a, b, b)的调用具有已定义的行为,因为数组b在函数h中没被修改。
11、例4 在restrict限定的指针之间赋值的限制规则,并不区分一个函数调用与一个等价的嵌套语句块之间的差异。但是有一个例外,只有在嵌套语句块中,restrict限定的指针之间的“外部到内部”赋值,才有已定义的行为。
{
int * restrict p1;
int * restrict q1;
p1 = q1; // 未定义行为
{
int * restrict p2 = p1; // 有效
int * restrict q2 = q1; // 有效
p1 = q2; // 未定义行为[译者注:内部到外部是无效的]
p2 = q2; // 未定义行为
}
}
12、还有一个例外是,允许一个由restrict限定的指针的值被带出它所在的语句块(或者,更精确地说,是用于指派它的普通标识符),当那个语句块结束执行时来声明该标识符。比如,在下面的例子中,允许new_vector返回一个vector。
typedef struct { int n; float * restrict v; } vector;
vector new_vector(int n)
{
vector t;
t.n = n;
t.v = malloc(n * sizeof(float));
return t;
}
ISO/IEC 9899:2011 条款6.7.3——类型限定符的更多相关文章
- ISO/IEC 9899:2011 条款6.7.8——类型定义
6.7.8 类型定义 语法 1.typedef-name: identifier 约束 2.一个typedef名指定了一个可变修改的类型,然后它应该具有语句块作用域. 语义 3.在一个声明中,该声明的 ...
- ISO/IEC 9899:2011 条款6.7.7——类型名
6.7.7 类型名 语法 1.type-name: specifier-qualifier-list abstract-declaratoropt abstract-declarator: po ...
- ISO/IEC 9899:2011 条款6.7.2——类型说明符
6.7.2 类型说明符 语法 1.type-specifier: void char short int long float double signed unsigned _Bool _Comple ...
- ISO/IEC 9899:2011 条款6.2.5——类型
6.2.5 类型 1.存储在一个对象中的值或由一个函数所返回的值的意义由用于访问该对象的表达式的类型来确定.(声明为一个对象的一个标识符是最简单的这种表达式:其类型在标识符的声明中指定.)类型被划分为 ...
- ISO/IEC 9899:2011 条款6.2.6——类型的表示
6.2.6 类型的表示 6.2.6.1 通用类型 1.所有类型的表示都是未指定的,除了在本小节所描述的之外. 2.除了位域(bit-field),对象由连续的一个或多个字节序列构成,这些字节序列的字节 ...
- ISO/IEC 9899:2011 条款6.7.6——声明符
6.7.6 声明符 语法 1.declarator: pointeropt direct-declarator direct-declarator: identifier ( declar ...
- ISO/IEC 9899:2011 条款6.5.16——赋值操作符
6.5.16 赋值操作符 语法 1.assignment-expression: conditional-expression unary-expression assignment-opera ...
- ISO/IEC 9899:2011 条款3——术语、定义与符号
3. 术语.定义与符号 1.对于此国际标准的意图,应用了以下定义.其它术语是在用斜体类型或一个语法规则左侧出现的地方定义.在本国际标准中所显式定义的术语不被假定为对其它地方所定义的类似术语的隐式引用. ...
- ISO/IEC 9899:2011 条款5——5.2.1 字符集
5.2.1 字符集 1.两个字符集和它们相关联的依次顺序应该被定义:写在源文件中的集合(源字符集),以及在执行环境中被解释的集合(执行字符集).每个集合此外被划分为一个基本字符集,其内容由本子条款给出 ...
随机推荐
- 《团队名称》第九次团队作业:Beta冲刺与验收准备
项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十三 团队作业9:Beta冲刺与团队项目冲刺 团队名称 发际线总和我作队 作业学习目标 (1)掌握软件黑盒测试技术:(2)掌握软件 ...
- 国产MM才叫漂亮[景甜]
- RCNN,Fast RCNN,Faster RCNN 的前生今世:(1) Selective Search
Selective Search for Object Recoginition 这篇论文是J.R.R. Uijlings发表在2012 IJCV上的一篇文章,主要介绍了选择性搜索(Selective ...
- Flume组件
1.什么是Flume:apache顶级项目,主要用来做数据采集.分布式.高可用,将海量日志进行采集.聚合.传输的系统.能够对数据进行简单处理在发送到接收方. 2.Flume组件:source.chan ...
- Python 的内置字符串方法(收藏专用)
Python 的内置字符串方法(收藏专用) method 字符串 string python3.x python 4.7k 次阅读 · 读完需要 44 分钟 5 字符串处理是非常常用的技能,但 ...
- .net使用WebUploader做大文件的分块和断点续传
ASP.NET上传文件用FileUpLoad就可以,但是对文件夹的操作却不能用FileUpLoad来实现. 下面这个示例便是使用ASP.NET来实现上传文件夹并对文件夹进行压缩以及解压. ASP.NE ...
- learning java Cloneable
class Address{ String Detail; public Address(String detail){ this.Detail = detail; } } class User im ...
- codevs 2058 括号序列
2058 括号序列 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题解 题目描述 Description 定义满足以下规则字符串为规则序列,否 ...
- 编译安装和二进制安装mysql
二进制安装mysql-5.6.46 mysql二进制安装,已经编译成二进制了,只需要做一些配置即可 [root@localhost ~]$ yum install autoconf libaio -y ...
- 性能测试学习第十天-----性能案例分析之CPU消耗过高&响应时间较长
一.现象 /pinter/case/cpu?type=1 使用google的gjson.tojson性能较差 type=2 使用性能好的阿里巴巴的fastjson库 压测过程中,发现应用服 ...