问题
Is there any practical difference between the following prototypes?
void f(const int *p);
void f(const int *restrict p);
void f(const int *volatile p);
The section C11 6.7.6.3/15 (final sentence) says that top-level qualifiers are not considered for the purposes of determining type compatibility, i.e. it is permitted for the function definition to have different top-level qualifiers on its parameters than the prototype declaration had.
However (unlike C++) it does not say that they are ignored completely. In the case of const
this is clearly moot; however in the case of volatile
and restrict
maybe there could be a difference.
Example:
void f(const int *restrict p);
int main()
{
int a = 42;
const int *p = &a;
f(p);
return a;
}
Does the presence of restrict
in the prototype allow the compiler to optimize out the read of a
for return a;
?
(Related question)
回答1:
If there is nothing in the standard, then it's up to the compilers, but it seems that at least for gcc 4.9 (for x86) they are ignored. Check this small snippet that I've used to tease the compiler:
static int b;
void f(const int *p) {
b = *p + 1;
}
int main()
{
int a = 42;
const int *p = &a;
f(p);
return a;
}
If I compile it as is, I get
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
If I compile it using void f(const int *__restrict__ p) I get
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
Anf finally if I compile it using void f(const int *__volatile__ p) I get
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
So it seems that in practice they are ignored in C as well.
回答2:
Assuming a definition of f
lacking the restrict
qualifier, the code should be well-defined. C11 (n1570) 6.5.2.2 (Function calls) p7 [emph. mine, identical wording in C99 TC3 (n1256)]
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.
The function f
is called with unqualified arguments (and thus, with arguments of the correct types), and all its declarations are of compatible type (as per the quote in the question): The function call is well-defined. (If there isn't anything in the standard making it explicitly undefined. I don't think there is.)
回答3:
The presence of a top-level volatile
qualifier applied to a parameter in a function's definition may cause behavior to be defined in some cases where it otherwise would not. Most notably:
int test(int volatile x)
{
if (setjmp(&someJumpBuff)) return x;
x++;
someFunction(); // A function that calls longjmp(&someJumpBuff, 1);
x--;
return x;
}
If x
were not declared volatile
, a compiler could optimize out the x++
and x--
since it could assume that no other code would ever examine the value of x
between those two operations. The volatile
declaration, however, would force the compiler to presume that code which examines x
after the setjmp
might execute between the x++
and x--
and thus observe the value x
held at that time.
It may be possible to contrive a platform calling convention where a "clever" optimizer that knew nothing about a function's definition beyond the fact that it did not use a volatile
qualifier on an argument would be able to generate code that would not be allowable in the presence of such a qualifier, but even on such a platform, a compiler that only saw that a function's prototype lacked a volatile
qualifier would have no basis for assuming that its definition wouldn't include one.
回答4:
using 'volatile' on a parameter means to re-read the parameter each time it is used rather than just using some previously read value.
this is 'usually' useless on a passed parameter.
The time for 'volatile' is when the something can change asynchronously to the code execution, such as something modified in an interrupt or I/O value.
Passed parameters are copies and do not change asynchronously.
'Restrict' is a promise by the coder, to the compiler, that certain possible problems can be ignored by the compiler,
such as 'I, the coder, promise that the memory areas of this call to memcpy() do not overlap.
So just use them when they are relevant and don't use them otherwise.
来源:https://stackoverflow.com/questions/28819855/is-top-level-volatile-or-restrict-significant-in-a-function-prototype