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);
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.)
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.
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.
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.