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 一个声明的对象

  1. extern const volatile int real_time_clock;

可以被硬件修改,但不能赋值、递增、或递减。

12、以下声明和表达式描述了当类型限定符修改一个聚合类型时的行为:

  1. const struct s { int mem; } cs = { };
  2. struct s acs; // 对象acs是可修改的
  3. typedef int A[][];
  4. const A a = {{, , }, {, , }}; // const int的数组的数组
  5. int *pi;
  6. const int *pci;
  7.  
  8. acs = cs; // 有效
  9. cs = ncs; // 违背了用于=的可修改左值的约束
  10. pi = &ncs.mem; // 有效
  11. pi = &cs.mem; // 违背了用于=的类型约束
  12. pci = &cs.mem; // 有效
  13. pi = a[]; // 无效:a[0]具有类型“const int *”

13、例3 声明

  1. _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的语句块(或在一个独立式环境中,在程序启动时所调用的函数)。[译者注

  1. void foo(void)
  2. {
  3. if( > )
  4. // B语句块1
  5. {
  6. T* restrict P;
  7. }
  8. }
  9.  
  10. void foo2(T* restrict P)
  11. // B语句块2
  12. {
  13.  
  14. }
  15.  
  16. int main(void)
  17. // B语句块3
  18. {
  19.  
  20. }

3、一个指针表达式E被称为基于对象P,如果(在B的执行中的某个顺序点处,B的执行在对E的计算之前)对指向一个数组对象的一个拷贝的P修改为P之前所指向的对象,将改变E的值。[注:换句话说,E依赖于P自己的值,而不是通过P间接引用的一个对象的值。比如,如果标识符p具有类型(int **restrict),那么指针表达式pp+1都基于由p所指派的restrict限定的指针对象,但*pp[1]就不是如此。]

4、在对B的每次执行期间,设L为任一左值,且让&L基于P。如果L用于访问它所指派的对象X的值,且X也被修改(通过任何手段),那么要应用以下要求:T不应该用const限定。用于访问X的值的每个其它左值也应该让其地址基于P。出于此子条款的目的,每个修改X的访问应该被认作为也对P进行修改。如果赋给P的值是一个指针表达式E的值,该指针基于另一个restrict限定的指针对象P2,与语句块B2相关联,那么B2的执行应该在B的执行之前开始,要么B2的执行应该在赋值之前结束。如果这些要求没被满足,那么行为是未定义的。[译者注:比如:

  1. int main(void)
  2. { // B语句块
  3.  
  4. T X;
  5. T *L = &X;
  6. T ** restrict P = &L;
  7. // 这样,对于需要访问X的每个其它左值,也应该将其地址基于P,即
  8. T *O = L; // O为另一个要访问X的指针变量
  9. *O = ; // 通过指针变量O对X进行修改
  10.  
  11. // 赋给P的值是一个表达式E的值,E含有P2
  12. P = > ? (++X, &L /** B2语句块 */) : NULL; // 语句块B2在赋值之前结束
  13. }

5、这里,对B的一次执行是对程序执行的一部分,它相应于一个标量类型以及与B相关联的自动存储周期对象的生命周期。

6、一个翻译器可以自由地忽略对任一或所有restrict使用的连带影响。

7、例1 在文件域声明了以下代码:

  1. int * restrict a;
  2. int * restrict b;
  3. extern int c[];

断言了,如果一个对象通过a、b或c其中之一进行访问,并且该对象在程序中的任一地方被修改,那么它永远都不会使用其它两个进行访问。

8、例2 在以下例子中的函数形参声明

  1. void f(int n, int * restrict p, int * restrict q)
  2. {
  3. while (n-- > )
  4. *p++ = *q++;
  5. }

断言了,在函数的每个执行期间,如果一个对象通过指针形参其中之一进行访问,那么它就不会通过另一个进行访问。

9、从restrict的获益是,它们允许翻译器对函数f做有效依赖分析,而不检查程序中对f的任一调用。成本是,程序员必须对所有这些调用进行检查,以确保它们不会给出未定义行为。比如,下面在g中对f的第二个调用具有未定义行为,因为d[1]d[49]的每一个元素既通过p又通过q进行访问。

  1. void g(void)
  2. {
  3. // 译者注:f函数是上述代码片段中所定义的一个拷贝函数
  4. extern int d[];
  5. f(, d + , d); // 有效
  6. f(, d + , d); // 未定义行为
  7. }

10、例3 以下函数形参声明

  1. void h(int n, int * restrict p, int * restrict q, int * restrict r)
  2. {
  3. int i;
  4. for(i = ; i < n; i++)
  5. p[i] = q[i] + r[i];
  6. }

描述了一个未被修改的对象如何能通过两个用restrict限定的指针来混叠。特别地,如果ab是两个不相交的数组,那么对形式h(100, a, b, b)的调用具有已定义的行为,因为数组b在函数h中没被修改。

11、例4 在restrict限定的指针之间赋值的限制规则,并不区分一个函数调用与一个等价的嵌套语句块之间的差异。但是有一个例外,只有在嵌套语句块中,restrict限定的指针之间的“外部到内部”赋值,才有已定义的行为。

  1. {
  2. int * restrict p1;
  3. int * restrict q1;
  4. p1 = q1; // 未定义行为
  5. {
  6. int * restrict p2 = p1; // 有效
  7. int * restrict q2 = q1; // 有效
  8. p1 = q2; // 未定义行为[译者注:内部到外部是无效的]
  9. p2 = q2; // 未定义行为
  10. }
  11. }

12、还有一个例外是,允许一个由restrict限定的指针的值被带出它所在的语句块(或者,更精确地说,是用于指派它的普通标识符),当那个语句块结束执行时来声明该标识符。比如,在下面的例子中,允许new_vector返回一个vector

  1. typedef struct { int n; float * restrict v; } vector;
  2. vector new_vector(int n)
  3. {
  4. vector t;
  5. t.n = n;
  6. t.v = malloc(n * sizeof(float));
  7. return t;
  8. }

ISO/IEC 9899:2011 条款6.7.3——类型限定符的更多相关文章

  1. ISO/IEC 9899:2011 条款6.7.8——类型定义

    6.7.8 类型定义 语法 1.typedef-name: identifier 约束 2.一个typedef名指定了一个可变修改的类型,然后它应该具有语句块作用域. 语义 3.在一个声明中,该声明的 ...

  2. ISO/IEC 9899:2011 条款6.7.7——类型名

    6.7.7 类型名 语法 1.type-name: specifier-qualifier-list    abstract-declaratoropt abstract-declarator: po ...

  3. ISO/IEC 9899:2011 条款6.7.2——类型说明符

    6.7.2 类型说明符 语法 1.type-specifier: void char short int long float double signed unsigned _Bool _Comple ...

  4. ISO/IEC 9899:2011 条款6.2.5——类型

    6.2.5 类型 1.存储在一个对象中的值或由一个函数所返回的值的意义由用于访问该对象的表达式的类型来确定.(声明为一个对象的一个标识符是最简单的这种表达式:其类型在标识符的声明中指定.)类型被划分为 ...

  5. ISO/IEC 9899:2011 条款6.2.6——类型的表示

    6.2.6 类型的表示 6.2.6.1 通用类型 1.所有类型的表示都是未指定的,除了在本小节所描述的之外. 2.除了位域(bit-field),对象由连续的一个或多个字节序列构成,这些字节序列的字节 ...

  6. ISO/IEC 9899:2011 条款6.7.6——声明符

    6.7.6 声明符 语法 1.declarator: pointeropt    direct-declarator direct-declarator: identifier (    declar ...

  7. ISO/IEC 9899:2011 条款6.5.16——赋值操作符

    6.5.16 赋值操作符 语法 1.assignment-expression: conditional-expression unary-expression    assignment-opera ...

  8. ISO/IEC 9899:2011 条款3——术语、定义与符号

    3. 术语.定义与符号 1.对于此国际标准的意图,应用了以下定义.其它术语是在用斜体类型或一个语法规则左侧出现的地方定义.在本国际标准中所显式定义的术语不被假定为对其它地方所定义的类似术语的隐式引用. ...

  9. ISO/IEC 9899:2011 条款5——5.2.1 字符集

    5.2.1 字符集 1.两个字符集和它们相关联的依次顺序应该被定义:写在源文件中的集合(源字符集),以及在执行环境中被解释的集合(执行字符集).每个集合此外被划分为一个基本字符集,其内容由本子条款给出 ...

随机推荐

  1. python3_pygame游戏窗口创建

    python3利用第三方模块pygame创建游戏窗口 步骤1.导入pygame模块 步骤2.初始化pygame模块 步骤3.设置游戏窗口大小 步骤4.定义游戏窗口背景颜色 步骤5.开始循环检测游戏窗口 ...

  2. Kotlin属性揭秘与延迟初始化特性

    在上一次https://www.cnblogs.com/webor2006/p/11210181.html学习了Kotlin的伴生对象,这次来学习属性相关的东东. 属性揭秘: 先声明一个属性: 没啥可 ...

  3. linux网络编程之socket编程(一)

    今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...

  4. No.4.测试子类继承父类各代码块和构造方法的执行顺序

    Son子类 public class Son extends Parent { static String y ="son的static属性"; public static voi ...

  5. Spring -08 -自动注入 -byName/byType/constructor -全局使用default-autowire=” byName"

    1.在Spring 配置文件中对象名和ref=”id”id 名相同使用自动注入,可以不配置<property/>2.两种配置办法 2.1在<bean>中通过 autowire= ...

  6. Vue当中的this

    10事件绑定 methods当中的this就是Vue实例对象vm var vm = new Vue({ el: '#app', data: { num: 0 }, // 注意点: 这里不要忘记加逗号 ...

  7. sublime——开启自动保存

    前言 懒 步骤 失去焦点自动保存 "save_on_focus_lost": true 首选项-->设置-->Ctrl+F搜索‘save’,找到“save_on_foc ...

  8. idea添加tomcat和服务添加项目

  9. HttpClient SSL connection could not be established error

    系统从.net framework 升级到dotnet core2.1 原先工作正常的httpclient,会报SSL connection could not be established erro ...

  10. 洛谷 P1279 字串距离 题解

    每日一题 day24 打卡 Analysis 字符串+dp 仔细观察发现,对于f[i][j],它的值为以下三个值中的最小者: f[i-1][j]+k //a[i]对应空格 f[i][j-1]+k // ...