• C语言操作符详解(2)


    赋值操作符

    赋值操作符是一个很棒的操作符,他可以改变你不喜欢的值。如你的体重,如果不喜欢就可直接改变:

    int weight = 120;//体重
    weight = 89;//不满意就赋值
    double salary = 10000.0;//如果你不喜欢你的薪资水平,也可以用赋值操作符改变
    salary = 20000.0;//使用赋值操作符赋值
    
    • 1
    • 2
    • 3
    • 4

    赋值操作符也可以连续赋值,代码如下:

    #include 
    int main()
    {
    	int a = 10;
    	int x = 0;
    	int y = 20;
    	a = x = y + 1;//连续赋值
    	printf("%d %d", a, x);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    但是,在日常编程中我们不建议进行连续赋值的操作,因为连续赋值会使代码可读性降低。
    我们再来看一下复合赋值符:

    +=
    -=
    *=
    /=
    %=
    >>=
    <<=
    &=
    |=
    ^=
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这些操作符都可以写成复合的效果,比如:

    int x=10;
    x=x>>1//它与x>>=1效果一样
    
    • 1
    • 2

    单目操作符

    单目操作符代表只作用于一个变量或表达式。
    在这里插入图片描述
    !是逻辑非,若变量为真,那么!变量为假,具体代码如下:

    #include 
    int main()
    {
    	int a = 10;
    	if (!a)
    	{
    		printf("1");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    如上图,a为10代表真,!a就为假,所以不执行if语句。
    &作为单目操作符时代表取地址(查看变量的存储位置),代码示例如下:

    #include 
    int main()
    {
    	int a = 10;
    	printf("%p",&a);//取址
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    如上图为地址(16进制),%p代表打印地址。
    sizeof求变量所占空间大小,单位是字节。

    #include 
    int main()
    {
    	short h = 2;
    	int a = 10;
    	printf("%zu\n",sizeof(h=a+2));//%zu是打印内存大小,也可以用%d,但是%zu更加规范
    	printf("%d", h);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    如上图,h为short为类型占两个两个字节,这里的h是被赋值变量,所以要对a进行截断即将其变成2字节,如下:

    int a=10;
    //00000000000000000000000000001010
    //截断后:0000000000001010,从右至左截取16位(2字节)
    
    • 1
    • 2
    • 3

    而这里经过sizeof(h=a+2)后h的值并没有改变,原因是sizeof操作符只返回变量类型的内存大小,而不会对其括号内的表达式计算。

    sizeof与数组

    数组名:
    数组名是数组首元素的地址但是有2个例外:

    1. sizeof(数组名),数组名表示整个数组,不是首元素的地址。sizeof(数组名)计算的是整个数组的大小,单位是字节。
      2.&数组名,数组名表示整个数组,不是首元素的地址。&数组名取出的是整个数组的地址。

    代码示例如下:

    #include 
    void test1(int arr[])
    {
    	printf("%d\n", sizeof(arr));//指针
    }
    void test2(char ch[])
    {
    	printf("%d\n", sizeof(ch));//指针
    }
    int main()
    {
    	int arr[10] = { 0 };
    	char ch[10] = { 0 };
    	printf("%d\n", sizeof(arr));
    	printf("%d\n", sizeof(ch));
    	test1(arr);//调用函数
    	test2(ch);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    这里可以先了解,所有指针在x86的环境下,所占空间的大小都为4个字节(指针这里先做了解,后续博客会讲)。

    ~操作符

    ~是按位取反操作符,将一个整数的二进制按位取反,代码样例如下:

    #include 
    int main()
    {
    	int a = 1;
    	//00000000000000000000000000000001,原码
    	//00000000000000000000000000000001 - 内存中-补码
    	//00000000000000000000000000000001,反码
    	printf("%d\n", ~a);
    	//10000000000000000000000000000010
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    ++,–,强制类型转换

    ++,–代表为变量+1,-1,具体来看代码:

    #include 
    int main()
    {
    	int a, b;
    	a = 5;
    	b = a++;
    	printf("%d\n", a);
    	printf("%d\n", b);
    	b = ++a;
    	printf("%d", b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    如图所示a++与++a是有区别的,b=a++是先a赋值给b,然后a在加1。而b=++a是a先加1在赋值给b。这里还要注意对一个变量使用++,–是改变了其本身的值,如a+1这里a本身的值没有改变,而a++是a本身的值改变了,–操作符和++是一样的作用,这里不在过多赘述。
    强制类型转换直接看代码:

    #include 
    int main()
    {
    	int a;
    	char ch;
    	a = 5;
    	ch = (char)a;//强制类型转换
    	printf("%c\n", a);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    如图强制类型转换为(转换类型)变量,把整数5转化为其ASC Ⅱ对应的字符,当然在日常编程中,不到万不得已的情况下,尽量不要用强制类型转化,强扭的瓜不甜,代码也是如此,强制类型转化有时可能导致得不到自己想要的值,也可能会使计算机报错。

    关系操作符

    =
    <
    <=
    != 用于测试“不相等”
    == 用于测试“相等”

    这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
    在编程的过程中== 和=不小心写错,导致的错误。

    逻辑操作符

    逻辑操作符有哪些:
    && 逻辑与
    || 逻辑或
    逻辑操作符多用于判断条件,如if((表达式1)&&(表达式2))这里如果表达式1,2都为真则执行if语句,有一个为假则不会执行if语句。
    if((表达式1)||(表达式2))这里表达式1,2有一个为真则执行if语句,若两个都为假则不会执行if语句。
    这里要注意区分逻辑与和按位与 ,区分逻辑或和按位或。
    这里给大家看一道360的笔试题:

    #include 
    int main()
    {
      int i = 0,a=0,b=2,c =3,d=4;
      i = a++ && ++b && d++;
      //i = a++||++b||d++;
      printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    大家可以先算一算执行结果,再看答案。
    在这里插入图片描述
    这里要补充一个知识点,逻辑操作符会遵循短路判断,例如a++ && ++b && d++;上述代码中a为0,且是a++不是++a所以这里编译器看到a为0且后面是逻辑与操作符就不会在执行后面的++b,d++但是会执行完a++,若改变成++a那编译器则会执行完这句代码。

    条件操作符

    条件操作符为(表达式1)?(表达式2):(表达式3),注意中间是冒号不是分号。
    它与if…else相同,具体来看代码:

    if (a > 5)
        b = 3;
    else
        b = -3;
    
    • 1
    • 2
    • 3
    • 4
    b=a>5?3:-3;
    
    • 1

    以上量代码执行效果是相同的:

    #include 
    int main()
    {
    	int a=4, b;
    
    	b=a > 5 ? 3 :  -3;
    	printf("%d", b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    这里会先判断a是否大于5,若大于则返回3,不大于就返回-3。

    逗号表达式

    exp1, exp2, exp3, …expN(exp代表表达式)。
    逗号表达式,就是用逗号隔开的多个表达式。
    逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
    代码如下:

    #include 
    int main()
    {
    	int a = 1;
    	int b = 2;
    	int c = (a > b, a = b + 10, a, b = a + 1);
    	printf("%d", c);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    这里可以看到逗号表达式(a > b, a = b + 10, a, b = a + 1)会依次执行到最后,返回最后一个表达式的执行结果。

    表达式求值

    表达式求值的顺序一部分是由操作符的优先级和结合性决定。
    同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

    隐式类型转换

    C的整型算术运算总是至少以缺省整型类型的精度来进行的。
    为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型
    提升

    整型提升的意义:
    表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
    一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
    因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
    度。
    通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
    中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
    换为int或unsigned int,然后才能送入CPU去执行运算。

    char a,b,c;
    a = b + c;
    
    • 1
    • 2

    b和c的值被提升为普通整型,然后再执行加法运算。加法运算完成之后,结果将被截断,然后再存储于a中。
    如何进行整体提升呢?
    整形提升是按照变量的数据类型的符号位来提升的。

    //负数的整形提升
    char c1 = -1;//-1是ASCⅡ
    变量c1的二进制位(补码)中只有8个比特位:
    1111111
    因为 char 为有符号的 char
    所以整形提升的时候,高位补充符号位,即为1
    提升之后的结果是:
    11111111111111111111111111111111
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //正数的整形提升
    char c2 = 1;
    变量c2的二进制位(补码)中只有8个比特位:
    00000001
    因为 char 为有符号的 char
    所以整形提升的时候,高位补充符号位,即为0
    提升之后的结果是:
    00000000000000000000000000000001
    //无符号整形提升,高位补0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码示例:

    #include 
    int main()
    {
    	char a = 0xb6;//16进制
    	short b = 0xb600;//16进制
    	int c = 0xb6000000;//16进制
    	if (a == 0xb6)
    		printf("a");
    	if (b == 0xb600)
    		printf("b");
    	if (c == 0xb6000000)
    		printf("c");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    示例中a,b是char和short类型,所以在使用时会被整形提升与原值不符,所以不会打印a,b但c是int型不会改变,所以输出c。
    另一示例:

    #include 
    int main()
    {
    	char c = 1;
    	printf("%u\n", sizeof(c));
    	printf("%u\n", sizeof(+c));
    	printf("%u\n", sizeof(-c));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    示例中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字
    节.
    表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof© ,就是1个字节.
    所以总结发生整形提升的条件:1.只有short,char类型的变量会发生整形提升。
    2.当变量参加表达式运算才会发生整形提升。

    算术转换

    如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
    型,否则操作就无法进行。下面的层次体系称为寻常算术转换

    long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运
    算。
    如一个unsigned int类型变量和一个float类型变量运算时,那么unsigned int就要先转换成float类型在执行运算。
    警告:
    但是算术转换要合理,要不然会有一些潜在的问题。

    #include 
    int main()
    {
    	float f = 3.14;
    	int num = f;//隐式转换,会有精度丢失
    	printf("%d", num);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    这里就发生了精度丢失。

    操作符属性

    复杂表达式的求值有三个影响的因素。

    1. 操作符的优先级
    2. 操作符的结合性
    3. 是否控制求值顺序。
      两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
      操作符优先级如下:
      在这里插入图片描述
      下面在介绍以下问题表达式:
    //表达式的求值部分由操作符的优先级决定。
    //表达式1
    a*b + c*d + e*f
    
    • 1
    • 2
    • 3

    注释:代码在计算的时候,由于*比+的优先级高,只能保证,的计算是比+早,但是优先级并不
    能决定第三个
    比第一个+早执行。
    所以表达式的计算机顺序就可能是:

    a*b
    c*d
    a*b + c*d
    e*f
    a*b + c*d + e*f
    或者:
    a*b
    c*d
    e*f
    a*b + c*d
    a*b + c*d + e*f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    像这种问题表达式可能不同的编译器就会计算不同的结果。所以大家在编程时尽量避免问题表达式。

    //表达式2
    c + --c;
    
    • 1
    • 2

    注释:同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得
    知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义
    的。
    再来看一看非法表达式:

    #include 
    int main()
    {
    	int i = 10;
    	i = i-- - --i * (i = -3) * i++ + ++i;
    	printf("i = %d\n", i);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    相信这种代码在大多数学校学习时,老师都会演示一遍来炫技(笔者的学校就是这样的)。
    但表达式3在不同编译器中测试结果:非法表达式程序的结果
    在这里插入图片描述
    如图,曾有人做过实验,这种代码在各种不同的编译器上都会有不同的结果。

    #include 
    //代码4
    int fun()
    {
    	static int count = 1;
    	return ++count;
    }
    int main()
    {
    	int answer;
    	answer = fun() - fun() * fun();
    	printf("%d\n", answer);//输出多少?
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    又像这种函数代码是否有问题?
    有问题!
    虽然在大多数的编译器上求得结果都是相同的。
    但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,
    再算减法。
    函数的调用先后顺序无法通过操作符的优先级确定。
    所以在日常编程中,大家不要偷懒而导致编了错误的代码,要避免写bug,当然要是一个程序猿铁了心写bug那是谁也拦不住。
    最后期待您的三连,如有错误,欢迎私信或在评论区指正。

  • 相关阅读:
    git学习笔记 | 版本管理 - 分支管理
    私服下载失败,下载不下来
    基于深度学习的小学语文“输出驱动”教学研究课题方案
    【SVM分类】基于鸽群算法优化支持向量机SVM实现分类附matlab的代码
    Java中RPC框架及常用序列化协议
    探索RESTful API开发,构建可扩展的Web服务
    Python学习小组课程P3-Python爬虫(1)HTML与Json解析
    java - 序列化
    MyBatis-plus:代码生成器(狂)
    【运维日常】运维必备的 免费 在线画图工具,真的很好用!
  • 原文地址:https://blog.csdn.net/lzmyyds/article/details/126072230