在function原型中是顶级易失性还是​​限制显着?

以下原型有什么实际区别吗?

void f(const int *p); void f(const int *restrict p); void f(const int *volatile p); 

C11 6.7.6.3/15(最后一句)部分说,顶级限定符不是为了确定类型兼容性而考虑的,即允许函数定义在其参数上具有与原型不同的顶级限定符。声明了。

然而(与C ++不同)它并没有说它们被完全忽略了。 在const的情况下,这显然没有实际意义; 但是在volatilerestrict的情况下可能存在差异。

例:

 void f(const int *restrict p); int main() { int a = 42; const int *p = &a; f(p); return a; } 

原型中是否存在restrict允许编译器优化读取a for return a;

( 相关问题 )

如果标准中没有任何内容,则由编译器决定,但似乎至少对于gcc 4.9(对于x86),它们会被忽略。 检查我用来取笑编译器的这个小片段:

 static int b; void f(const int *p) { b = *p + 1; } int main() { int a = 42; const int *p = &a; f(p); return a; } 

如果我按原样编译它,我得到

 f(int const*): pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movl (%rax), %eax addl $1, %eax movl %eax, b(%rip) popq %rbp ret main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl $42, -12(%rbp) leaq -12(%rbp), %rax movq %rax, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call f(int const*) movl -12(%rbp), %eax leave ret 

如果我使用void f(const int * __ restrict__ p)编译它,我得到

 f(int const*): pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movl (%rax), %eax addl $1, %eax movl %eax, b(%rip) popq %rbp ret main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl $42, -12(%rbp) leaq -12(%rbp), %rax movq %rax, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call f(int const*) movl -12(%rbp), %eax leave ret 

如果我使用void f(const int * __ volatile__ p)编译它,我得到了

 f(int const*): pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movl (%rax), %eax addl $1, %eax movl %eax, b(%rip) popq %rbp ret main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl $42, -12(%rbp) leaq -12(%rbp), %rax movq %rax, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call f(int const*) movl -12(%rbp), %eax leave ret 

所以在实践中它们似乎也在C中被忽略了。

假设f的定义缺少restrict限定符,则代码应该是明确定义的。 C11(n1570)6.5.2.2(函数调用)p7 [emph。 我的,与C99 TC3相同的措辞(n1256)]

如果表示被调用函数的表达式具有包含原型的类型,则将参数隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型作为其声明的非限定版本类型

函数f使用非限定参数调用(因此,使用正确类型的参数),并且其所有声明都是兼容类型(根据问题中的引用):函数调用是明确定义的。 (如果标准中没有任何内容使其显式未定义。我认为没有。)

应用于函数定义中的参数的顶级volatile限定符的存在可能导致在某些情况下定义行为,否则不会。 最为显着地:

 int test(int volatile x) { if (setjmp(&someJumpBuff)) return x; x++; someFunction(); // A function that calls longjmp(&someJumpBuff, 1); x--; return x; } 

如果x未被声明为volatile ,则编译器可以优化x++x--因为它可以假设没有其他代码会检查这两个操作之间的x值。 但是, volatile声明会强制编译器假设在setjmp之后检查x代码可能在x++x--之间执行,从而观察当时保存的值x

有可能设计一个平台调用约定,其中一个对于函数定义一无所知的“聪明”优化器,除了它没有在参数上使用volatile限定符这一事实之外,它将能够生成不允许的代码。存在这样的限定符,但即使在这样的平台上,只看到函数原型缺少volatile限定符的编译器也没有基础来假设它的定义不包含一个。

在参数上使用’volatile’意味着每次使用时都重新读取参数,而不是仅仅使用一些先前读取的值。

对于传递的参数,这通常是“无用的”。

“volatile”的时间是指某些内容可以异步更改为代码执行,例如在中断或I / O值中修改的内容。

传递的参数是副本,不会异步更改。

‘Restrict’是编码器对编译器的承诺,编译器可以忽略某些可能的问题,

例如’我,编码器,承诺这次调用memcpy()的内存区域不重叠。

所以只要在相关时使用它们,否则不要使用它们。