Is top-level volatile or restrict significant in a function prototype?

為{幸葍}努か 提交于 2019-11-27 08:08:39

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!