Search code examples
ccastingoperator-precedenceunary-operator

Do unary operators have higher precedence than the cast expression?


I've been learning C via the well-known book c premier plus the 6th edition, and I have some confusion about the precedence of unary operators and the cast expression.

I get the pdf version of the book here, and location of the table mentioned below in this book is B: Reference Section Section II: C Operators--Table RS.II.1.

According to the book's table, the cast expression seems to have lower precedence comparing with unary operators. So I ran a simple test about it and somehow it appears that they have the same precedence. Here's the test file below:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    /*we take advantage of the truncation when casting int to char(only remains the lower 8 bit of the int), if the cast executes first, the value is 0 so the final output is 1. Conversely, we get 0 as the final result.*/
    int test = 512;
    printf("test1-1 is %d\n",!(char)test); /*test1-1 is 1*/
    printf("test1-2 is %d\n",(char)!test); /*test1-2 is 0*/

    getchar();
    return 0;
}

In this test file, I choose unary operator ! as a representation. Of course, there are other unary operators but they all have good reasons to not to be involved in this test:

  • the ++ , -- and &: they can only use a lvalue as operand, that's not something the cast expression can arrange for
  • the -, + and ~: we can declare a long long int variable and cast it as an int, maybe the truncation can tell us something. Unfortunately, we can not find anything from it, do some basic math, you will find out whether unary operator executes first or cast executes first, it doesn't change the output anyway.
  • others: just make no sense to me

Here's my environment settings: gcc version: gcc (tdm-1) 5.1.0 OS: windows7

I've add the command options like -std=c90,-std=c99, -std=c11,they all produce the very same output like the comment in the test file. That's weird, so I turn to the C11 standard document for help, and relative infomation is here. So here's the information:

1.The syntax specifies the precedence of operators in the evaluation of an expression, which is the same as the order of the major subclauses of this subclause, highest precedence first.

2.The exceptions are cast expressions (6.5.4) as operands of unary operators and ...

This explains a lot but the behavior is the same as if they share the same precedence. So why not put them together and avoid misleading? Just because the cast expression doesn't belong to unary operators, and to keep the toc clean so we must give cast expression different precedence? That's a little bit of OCD.


Solution

  • Let's focus on the operators you are analyzing in your test. Logical not ! operator and casting operator (type) do have the same precedence.

    As you can see in this table, they both have priority #2.
    What you need to understand what's going on is also taking into account associativity. Associativity represents the order in which operators having the same priority will be evaluated.
    The operators we are taking about have Right-to-left associativity.

    I'll copy your test for clarity:

    int main(void)
    {
        /*we take advantage of the truncation when casting int to char(only remains the lower 8 bit of the int), if the cast executes first, the value is 0 so the final output is 1. Conversely, we get 0 as the final result.*/
        int test = 512;
        printf("test1-1 is %d\n",!(char)test); /*test1-1 is 1*/
        printf("test1-2 is %d\n",(char)!test); /*test1-2 is 0*/
    
        getchar();
        return 0;
    }
    
    1. In !(char)test, right-to-left associativity means that the casting is performed first. This leads the value to be "adapted" to the char size, resulting in value 0.

      Then you apply the logical negation, resulting in the value 1

    2. In (char)!test, right-to-left associativity means that the logical negation is performed first. This leads to value !512, resulting in value 0.

      Then you apply the casting, resulting again in the value 0

    For these reasons, you actually get the expected results.