您的位置:首页 > 编程语言 > C语言/C++

C语言进阶学习5 逻辑运算符,位运算符,++和--操作,三目运算符和逗号表达式

2019-01-04 23:30 134 查看

本博客记录狄泰学院课程的学习
学习交流群:320628659
个人QQ:1367087622
欢迎大家交流学习
本章的学习目标:
逻辑运算符,位运算符,++和–操作,三目运算符和逗号表达式的学习分析
学习这些知识点使用需要注意的事项

逻辑运算符,位运算符,++和–操作,三目运算符和逗号表达式

逻辑运算符

逻辑运算符在条件判断时虽然看起来挺简单,但是因为使用的频率很高所以有很多需要注意的点需要记录学习

  • 逻辑运算符==&&,||,!==真的很简单吗?

下面的程序运行结束后i,j,k的值分别为多少?

#include <stdio.h>

int main()
{
int i = 0;
int j = 0;
int k = 0;

++i || ++j && ++k;

printf("%d\n", i);//i=1;
printf("%d\n", j);//j=0
printf("%d\n", k);//k=0

return 0;
}
  • 程序中的短路

    ||从左向右开始计算:

    当遇到为真的条件时停止计算,整个表达式为真
  • 所有条件为假时表达式才为假
  • &&从左向右开始计算:

      当遇到为假的条件时停止计算,整个表达式为假
    • 所有条件为真时表达式才为真

    分析后得知,在i为真后就停止了计算。发生了短路。

    • 在逻辑表达式中,==&&比||==具有更高的优先级

      在==&&和||==混合运算时

        整个表达式被看作==||==表达式,

      1. 编译器从左向右开始计算==&&表达式,当某个&&表达式的值为真时,停止计算,整个表达式的值为真==。

      2. 最后计算==||==表达式

    ++ i || ++j && ++k; ⇒ (++i)||(++j && ++k); ⇒ (true && ++i) || (++j && ++k);

    程序中的短路规则

    #include <stdio.h>
    
    int g = 0;
    
    int f()
    {
    printf("In f()...\n");
    
    return g++;
    }
    
    int main()
    {
    if( g || f() && f() )
    {
    printf("In if statement: %d\n", g);
    }
    
    printf("In main(): %d\n", g);
    
    return 0;
    }

    分析:第14行化为(true && g)||(f() && f())

    判断g时因为g为0为假,继续往右判断,到f()时打印了In f()…,到返回时因为是后置++所以先返回g的值为0在增1,g=1,那么右边的f()&&f()的逻辑表达式为假,就发生了短路后面的f()就不会计算了。

    所以if的结果为假不执行里面的语句,最后输出In main():1,

    逻辑非“!”究竟是什么?

    • 在C语言中的逻辑非“!” ! 只认的0,只知道见了0就返回1。因此当其碰见的值不是0时,其结果为0
    #include <stdio.h>
    
    int main()
    {
    printf("%d\n", !0);     //1
    printf("%d\n", !1);     //0
    printf("%d\n", !100);   //0
    printf("%d\n", !-1000); //0
    
    return 0;
    }

    位运算符

    • C语言中的位运算符

      位运算符直接对bit位进行操作,其效率最高

      & 按位与
      | 按位或
      ^ 按位异或
      ~ 取反
      << 左移
      >> 右移
  • 左移和右移注意点

      左移操作数必须为整数类型

      char和short被隐式转换为int后进行移位操作
  • 右移操作的范围必须为:[0, 31]

  • 左移运算符<<将运算数的二进制位左移

      规则:高位丢弃,低位补0
  • 右移运算符>>把运算数的二进制位右移

      规则:高位补符号位,低位丢弃(unsigned补0,signed补1)
    #include <stdio.h>
    
    int main()
    {
    printf("%d\n", 3 << 2); //11 << 2 = 1100 = 12
    printf("%d\n", 3 >> 1); //11 >> 1 =
    20000
    01 = 1
    printf("%d\n", -1 >> 1);
    //-1 = 0000 0001各位取反后加1 = 1111 1111
    //1111 1111 >> 1 = 0111 1111 补上符号位1111 1111结果没有变化还是-1
    printf("%d\n", 0x01 << 2 + 3);//加法的运算符优先于<<,所以0x01 << 5 = 32
    
    printf("%d\n", 3 << -1); // oops!
    //标准上只定义了右移操作的范围必须为:[0, 31],
    //超过这个范围,每个编译器的处理并不一样
    return 0;
    }
    • 小贴士

      防错准则:

      避免位运算符,逻辑运算符和数学运算符同时出现在一个表达式中
    • 当避免不了时,混合计算时尽量使用括号来表达计算次序
  • 嵌入式编程 有一个小技巧

      左移n位相当于乘以2的n次方,但效率比数学运算符高
    • 右移n位相当于除以2的n次方,但效率比数学运算符高

    编程实验:交换两个整型变量的值

    #include <stdio.h>
    
    #define SWAP1(a, b)    \
    {                      \
    int t = a;         \
    a = b;             \
    b = t;             \
    }
    
    #define SWAP2(a, b)    \
    {                      \
    a = a + b;         \
    b = a - b;         \
    a = a - b;         \
    }
    //最为高效
    #define SWAP3(a, b)    \
    {                      \
    a = a ^ b;         \
    b = a ^ b;         \
    a = a ^ b;         \
    }
    
    int main()
    {
    int a = 1;
    int b = 2;
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    
    SWAP3(a ,b);
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    
    return 0;
    }
    • 位运算与逻辑运算 位运算与逻辑运算的不同:
        位运算没有短路规则,每个操作数都参与运算
      1. 位运算的结果为整数,而不是0或1
      2. 位运算优先级高于逻辑运算优先级

    运算优先级:四则运算 > 位运算 > 逻辑运算

    ++ 和 – 操作符

    • ++和–操作符对应两条汇编指令

      前置

      变量自增(减)1
    • 取变量值
  • 后置

      取变量值
    • 变量自增(减)1
  • 一对令人头疼的兄弟

  • #include <stdio.h>
    
    int main()
    {
    int i = 0;
    int r = 0;
    
    r = (i++) + (i++) + (i++);
    
    printf("i = %d\n", i);//在gcc中 i = 3
    printf("r = %d\n", r);//r = 0
    
    r = (++i) + (++i) + (++i);
    
    printf("i = %d\n", i);// i = 6
    printf("r = %d\n", r);// r = 16
    
    return 0;
    }

    分析一下我们的想法

    (i++) + (i++) + (i++) ; //i -> 3

    0 + 1 + 2 =3

    (++i) + (++i) + (++i) ; //i -> 6

    4 + 5 +6 =15

    但是。。。在vs下是这样的结果

    在gcc下

    eclipse下

    • C语言中只规定了++和–对应指令的相对执行次序
    • ++ 和 – 对应的汇编指令不一定连续运行
    • 在混合运算中,++和–的汇编指令可能被打断执行
    • 不同的编译器在混合运算时处理并不一样

    所以结论是++和–参与混合运算结果是不确定的

    贪心法

    • 贪心法:++,–表达式的阅读技巧 编译器处理的每个符号应该尽可能多的包含字符
    • 编译器以从左向右的顺序一个一个尽可能多的读入字符
    • 当读入的字符不可能和已读入的字符组成合法符号为止
    • 编译器通过贪心法处理表达式中的子表达式
    #include <stdio.h>
    
    int main()
    {
    int i = 0;
    int j = ++i+++i+++i;//贪心法,++i++ -> 1++会报错
    
    int a = 1;
    int b = 4;
    int c = a+++b;//a++ +b
    
    int* p = &a;
    
    b = b/*p;// /*
    
    printf("i = %d\n", i);
    printf("j = %d\n", j);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    
    return 0;
    }

    空格可以作为C语言中一个完整符号的休止符

    编译器读入空格后立即对之前读入的符号进行处理

    三目运算符

    • 三目运算符(a ? b : c)可以作为逻辑运算符的载体
    • 规则:当a的值为真时,返回b的值;否则返回c的值
    • C语言中的三目运算符返回的是值,不是变量本身。
    #include <stdio.h>
    
    int main()
    {
    int a = 1;
    int b = 2;
    int c = 0;
    
    c = a < b ? a : b;
    
    (a < b ? a : b) = 3;//在C语言中,三目运算符不能做左值,
    //在C++中会有升级,如果有逻辑部分有常量才不能做左值
    *(a < b ? &a : b) = 3;//可以改变
    printf("%d\n", a);
    printf("%d\n", b);
    printf("%d\n", c);
    
    return 0;
    }
    • 三目运算符(a ? b : c)的返回类型 通过隐式类型转换规则返回b和c中的较高的类型
    • 当b和c不能隐式转换到同一类型时将编译出错
    #include <stdio.h>
    
    int main()
    {
    char c = 0;
    short s = 0;
    int i = 0;
    double d = 0;
    char* p = "str";
    
    printf( "%d\n", sizeof(c ? c : s) );//c为真?c=0为假返回s的值
    //char和short混合运算会转换为int的类型
    printf( "%d\n", sizeof(i ? i : d) );//i为假返回d的值,返回值类型是double
    printf( "%d\n", sizeof(d ? d : p) );//无法隐式的将double转为char*,报错
    
    return 0;
    }

    逗号表达式

    • 逗号表示式是C语言中”粘贴剂“

    • 逗号表达式用于将多个子表达式连接为一个表达式

    • 逗号表达式的值为最后一个子表达式的值

    • 逗号表达式中的前N-1个子表达式可以没有返回值

    • 逗号表达式按照从左向右的顺序计算每个子表达式的值

      exp1,exp2,exp3,…,expN

      逗号表达式的示例:

    #include <stdio.h>
    
    void hello()
    {
    printf("Hello!\n");
    }
    
    int main()
    {
    int a[3][3] = {
    (0, 1, 2),//这的逗号表达式的值为2
    (3, 4, 5),//这的逗号表达式的值为5
    (6, 7, 8)//这的逗号表达式的值为8
    };
    
    int i = 0;
    int j = 0;
    
    while( i < 5 )
    printf("i = %d\n", i),
    
    hello(),
    
    i++;
    
    for(i=0; i<3; i++)
    {
    for(j=0; j<3; j++)
    {
    printf("a[%d][%d] = %d\n", i, j, a[i][j]);
    //打印的是2,5,8
    }
    }
    
    return 0;
    }

    一行代码实现strlen

    #include <stdio.h>
    #include <assert.h>
    
    int strlen(const char* s)
    {
    return assert(s), (*s ? strlen(s + 1) + 1 : 0);
    }
    
    int main()
    {
    printf("len = %d\n", strlen("Delphi"));
    printf("len = %d\n", strlen(NULL));
    
    return 0;
    }
    • 三目运算符返回变量的值,而不是变量本身
    • 三目运算符通过隐式类型转换规则确认返回值类型
    • 逗号表达式按照从左向右的顺序计算每个子表达式的值
    • 逗号表达式的值为最后一个子表达式的值
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: