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),那么指针表达式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的执行应该在赋值之前结束。如果这些要求没被满足,那么行为是未定义的。[译者注:比如:

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限定的指针来混叠。特别地,如果ab是两个不相交的数组,那么对形式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——类型限定符的更多相关文章

  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. linux系统编程综合练习-实现一个小型的shell程序(三)

    上节中已经实现了对普通命令的解析,包括输入重定向,输出重定向,管道,后台作业,这次就来执行已经解析好的命令,对应的函数为:execute_command(),首先对带有管道的命令进行执行: 比如:&q ...

  2. 检查SQL Server数据库各个库表空间使用的方法

    /*创建一张表:表名Data,列名:表名,列数,预留空间,数据占用空间,索引占用空间,剩余空间*/ CREATE TABLE Data ( 表名 ), 列数 ), 预留空间 ), 数据占用空间 ), ...

  3. K8s中的多容器Pod和Pod内容器间通信

    容器(Container)常被用来解决比如微服务的单个问题,但在实际场景中,问题的解决往往需要多容器方案.本文会讨论将多个容器整合进单个Kubernetes Pod 中,以及Pod中的容器之间是如何通 ...

  4. 初学 Size Balanced Tree(bzoj3224 tyvj1728 普通平衡树)

    SBT(Size Balance Tree), 即一种通过子树大小(size)保持平衡的BST SBT的基本性质是:每个节点的size大小必须大于等于其兄弟的儿子的size大小: 当我们插入或者删除一 ...

  5. 用LinkedList和ArrayList实现自定义栈的异同

    //ArrayList已连续的空间进行存储数据  //LinkedList已链表的结构存储数据    //栈  MyStark ms=new MyStark();//new 一个实现栈的类  //压栈 ...

  6. .Net Core WebApi实现跨域

    .Net Core 需要引用一个包  Microsoft.AspNetCore.Cors 让接口实现跨域,需要配置两个地方. 一.Startup.cs 这里需要配置两个地方 public void C ...

  7. 流媒体知识 wiki

    媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等.最近因为项目的关系,需要理清媒 ...

  8. learning java AWT MenuBar Menu MenuItem菜单

    import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java ...

  9. Linux中三种SCSI target的介绍之STGT

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/scaleqiao/article/deta ...

  10. Ural1297 最长回文子串(后缀数组+RMQ)

    /* 源程序丢失QWQ. 就不粘代码了. 大体做法是把串反转然后连接. 做一遍后缀数组. 对height做一遍rmq. 然后对于每个位置的奇偶分别判断, 记下pos. 注意求的是[l+1,r]的hei ...