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

后端 未结 4 1679
逝去的感伤
逝去的感伤 2020-12-11 21:19

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


        
相关标签:
4条回答
  • 2020-12-11 21:44

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

    0 讨论(0)
  • 2020-12-11 21:58

    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.

    0 讨论(0)
  • 2020-12-11 21:58

    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.

    0 讨论(0)
  • 2020-12-11 21:58

    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.

    0 讨论(0)
提交回复
热议问题