跳到主要内容
🔍

代码示例:1.2.3 运算符与优先级

全部示例均标注编译命令、适用环境与实测状态;完整验证记录见《03.5-代码编译运行验证记录》。

  • examples/ex1-bit-macros.c · 四个位操作宏(SET/CLEAR/TOGGLE/IS_SET)

    /* ex1-bit-macros.c —— 四个位操作宏(1.2.3)
     * 适用环境:Linux / Windows(MinGW) 标准:C11(纯标准,无 GNU 扩展)
     * 编译:gcc ex1-bit-macros.c -o ex1 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./ex1(Windows: ex1.exe)
     * 说明:宏参数与整体都加括号;用 1u(unsigned)而非 1,避免 int 符号位左移的 UB。
     */
    #include <stdio.h>
     
    #define SET_BIT(reg, n)    ((reg) | (1u << (n)))
    #define CLEAR_BIT(reg, n)  ((reg) & ~(1u << (n)))
    #define TOGGLE_BIT(reg, n) ((reg) ^ (1u << (n)))
    #define IS_SET(reg, n)     (((reg) >> (n)) & 1u)
     
    int main(void)
    {
        unsigned int reg = 0;
        reg = SET_BIT(reg, 3);          /* 0b1000 = 8 */
        printf("SET_BIT(0,3)    = 0x%X\n", reg);
        reg = SET_BIT(reg, 0);          /* 8 | 1 = 9 */
        printf("再 SET_BIT(8,0) = 0x%X\n", reg);
        reg = CLEAR_BIT(reg, 3);        /* 9 & ~8 = 1 */
        printf("CLEAR_BIT(9,3)  = 0x%X\n", reg);
        reg = TOGGLE_BIT(reg, 7);       /* 1 ^ 128 = 129 (0x81) */
        printf("TOGGLE_BIT(1,7) = 0x%X\n", reg);
        printf("IS_SET(0x81,7)  = %u\n", IS_SET(reg, 7));
        printf("IS_SET(0x81,3)  = %u\n", IS_SET(reg, 3));
        return 0;
    }
     
  • examples/ex2-precedence.c · 优先级/结合性/短路/三目/逗号演示

    /* ex2-precedence.c —— 优先级/结合性/短路/三目/逗号(1.2.3)
     * 适用环境:Linux / Windows(MinGW) 标准:C11(纯标准,无 GNU 扩展)
     * 编译:gcc ex2-precedence.c -o ex2 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./ex2(Windows: ex2.exe)
     */
    #include <stdio.h>
     
    static int one(void)  { printf("调用 one\n");  return 1; }
    static int zero(void) { printf("调用 zero\n"); return 0; }
     
    int main(void)
    {
        int a = 2, b = 3, c = 4;
        int x, y;
     
        x = a + b * c;              /* 2 + 12 = 14:* 高于 + */
        printf("a + b * c = %d(* 高于 +)\n", x);
        x = (a + b) * c;            /* 5 * 4 = 20:括号改变结合 */
        printf("(a + b) * c = %d(括号改变结合)\n", x);
     
        a = b = c;                  /* 赋值右结合:从右向左,a=b=4 */
        printf("a = b = c 之后: a=%d b=%d c=%d\n", a, b, c);
     
        x = 5 < 3 && one();         /* 短路:左边为假,one() 不调用 */
        printf("5 < 3 && one() = %d(one 未被调用)\n", x);
        y = 1 || zero();            /* 短路:左边为真,zero() 不调用 */
        printf("1 || zero()  = %d(zero 未被调用)\n", y);
     
        x = (a > b) ? 100 : 200;    /* 三目:a=4,b=4 → 假 → 200 */
        printf("(a > b) ? 100 : 200 = %d\n", x);
     
        x = (a = 1, b = 2, a + b);  /* 逗号表达式:依次求值,取最右 a+b */
        printf("(a=1, b=2, a+b) = %d(逗号表达式取最右值)\n", x);
     
        printf("sizeof(int) = %zu(sizeof 是运算符不是函数)\n", sizeof(int));
        return 0;
    }
     
  • examples/ex3-ub-static.txt · UB 与求值顺序辨析(conceptual:静态讲解,不运行)

    # 1.2.3 未定义行为与求值顺序(conceptual 静态讲解——仅讲解,不运行)
     
    ## 一、未定义行为(UB):不要写,也不要试图预测输出
     
    \`\`\`c
    int i = 1;
    int r  = i++ + i++;   /* UB:同一表达式内多次修改同一变量且中间无序列点 */
    int r2 = ++i * ++i;   /* UB:同理 */
    \`\`\`
     
    - 标准(C11 §6.5p2)说"标准不保证任何结果"——编译器有权生成任意行为;
    - 某次运行恰好"对了"不代表代码正确;换编译器/优化级别结果可能不同;
    - 正确做法:拆成多行,一个表达式内同一变量最多修改一次。
     
    ## 二、未指定顺序(合法,但顺序不保证)
     
    \`\`\`c
    int r3 = f() + g() * h();   /* 合法。f/g/h 的调用先后顺序未指定 */
    \`\`\`
     
    - 优先级只决定结构(先算 g()*h() 再与 f() 相加),不决定 f/g/h 谁先被调用;
    - 如果 f/g/h 有副作用且互相影响,结果依赖实现——顺序敏感必须拆行。
     
    ## 三、保证求值顺序的少数运算符(C11)
     
    - \`&&\`、\`||\`:短路,先左后右,右边可能不求值;
    - \`?:\`:只求值被选中的分支;
    - 逗号 \`,\`:从左到右依次求值。
     
    ## 四、一句话纪律
     
    > 一个表达式里,同一变量最多修改一次;需要顺序就拆行;拿不准就加括号。
     
    (本示例按 C11 标准 N1570 §6.5p2、§6.5.2.2p10、§6.5.13/6.5.14/6.5.15/6.5.17 核对,未运行——运行 UB 代码没有意义。)