当前位置:网站首页>C语言操作符详解(2)
C语言操作符详解(2)
2022-08-02 05:13:00 【蝼 蚁 望 天】
赋值操作符
赋值操作符是一个很棒的操作符,他可以改变你不喜欢的值。如你的体重,如果不喜欢就可直接改变:
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;//如果你不喜欢你的薪资水平,也可以用赋值操作符改变
salary = 20000.0;//使用赋值操作符赋值
赋值操作符也可以连续赋值,代码如下:
#include <stdio.h>
int main()
{
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1;//连续赋值
printf("%d %d", a, x);
return 0;
}
但是,在日常编程中我们不建议进行连续赋值的操作,因为连续赋值会使代码可读性降低。
我们再来看一下复合赋值符:
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些操作符都可以写成复合的效果,比如:
int x=10;
x=x>>1//它与x>>=1效果一样
单目操作符
单目操作符代表只作用于一个变量或表达式。
!是逻辑非,若变量为真,那么!变量为假,具体代码如下:
#include <stdio.h>
int main()
{
int a = 10;
if (!a)
{
printf("1");
}
return 0;
}
如上图,a为10代表真,!a就为假,所以不执行if语句。
&作为单目操作符时代表取地址(查看变量的存储位置),代码示例如下:
#include <stdio.h>
int main()
{
int a = 10;
printf("%p",&a);//取址
return 0;
}
如上图为地址(16进制),%p代表打印地址。
sizeof求变量所占空间大小,单位是字节。
#include <stdio.h>
int main()
{
short h = 2;
int a = 10;
printf("%zu\n",sizeof(h=a+2));//%zu是打印内存大小,也可以用%d,但是%zu更加规范
printf("%d", h);
return 0;
}
如上图,h为short为类型占两个两个字节,这里的h是被赋值变量,所以要对a进行截断即将其变成2字节,如下:
int a=10;
//00000000000000000000000000001010
//截断后:0000000000001010,从右至左截取16位(2字节)
而这里经过sizeof(h=a+2)后h的值并没有改变,原因是sizeof操作符只返回变量类型的内存大小,而不会对其括号内的表达式计算。
sizeof与数组
数组名:
数组名是数组首元素的地址但是有2个例外:
- sizeof(数组名),数组名表示整个数组,不是首元素的地址。sizeof(数组名)计算的是整个数组的大小,单位是字节。
2.&数组名,数组名表示整个数组,不是首元素的地址。&数组名取出的是整个数组的地址。
代码示例如下:
#include <stdio.h>
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;
}
这里可以先了解,所有指针在x86的环境下,所占空间的大小都为4个字节(指针这里先做了解,后续博客会讲)。
~操作符
~是按位取反操作符,将一个整数的二进制按位取反,代码样例如下:
#include <stdio.h>
int main()
{
int a = 1;
//00000000000000000000000000000001,原码
//00000000000000000000000000000001 - 内存中-补码
//00000000000000000000000000000001,反码
printf("%d\n", ~a);
//10000000000000000000000000000010
return 0;
}
++,–,强制类型转换
++,–代表为变量+1,-1,具体来看代码:
#include <stdio.h>
int main()
{
int a, b;
a = 5;
b = a++;
printf("%d\n", a);
printf("%d\n", b);
b = ++a;
printf("%d", b);
return 0;
}
如图所示a++与++a是有区别的,b=a++是先a赋值给b,然后a在加1。而b=++a是a先加1在赋值给b。这里还要注意对一个变量使用++,–是改变了其本身的值,如a+1这里a本身的值没有改变,而a++是a本身的值改变了,–操作符和++是一样的作用,这里不在过多赘述。
强制类型转换直接看代码:
#include <stdio.h>
int main()
{
int a;
char ch;
a = 5;
ch = (char)a;//强制类型转换
printf("%c\n", a);
return 0;
}
如图强制类型转换为(转换类型)变量,把整数5转化为其ASC Ⅱ对应的字符,当然在日常编程中,不到万不得已的情况下,尽量不要用强制类型转化,强扭的瓜不甜,代码也是如此,强制类型转化有时可能导致得不到自己想要的值,也可能会使计算机报错。
关系操作符
=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
在编程的过程中== 和=不小心写错,导致的错误。
逻辑操作符
逻辑操作符有哪些:
&& 逻辑与
|| 逻辑或
逻辑操作符多用于判断条件,如if((表达式1)&&(表达式2))这里如果表达式1,2都为真则执行if语句,有一个为假则不会执行if语句。
if((表达式1)||(表达式2))这里表达式1,2有一个为真则执行if语句,若两个都为假则不会执行if语句。
这里要注意区分逻辑与和按位与 ,区分逻辑或和按位或。
这里给大家看一道360的笔试题:
#include <stdio.h>
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;
}
大家可以先算一算执行结果,再看答案。
这里要补充一个知识点,逻辑操作符会遵循短路判断,例如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;
b=a>5?3:-3;
以上量代码执行效果是相同的:
#include <stdio.h>
int main()
{
int a=4, b;
b=a > 5 ? 3 : -3;
printf("%d", b);
return 0;
}
这里会先判断a是否大于5,若大于则返回3,不大于就返回-3。
逗号表达式
exp1, exp2, exp3, …expN(exp代表表达式)。
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
代码如下:
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d", c);
return 0;
}
这里可以看到逗号表达式(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;
b和c的值被提升为普通整型,然后再执行加法运算。加法运算完成之后,结果将被截断,然后再存储于a中。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的。
//负数的整形提升
char c1 = -1;//-1是ASCⅡ
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
代码示例:
#include <stdio.h>
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;
}
示例中a,b是char和short类型,所以在使用时会被整形提升与原值不符,所以不会打印a,b但c是int型不会改变,所以输出c。
另一示例:
#include <stdio.h>
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
示例中的,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
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运
算。
如一个unsigned int类型变量和一个float类型变量运算时,那么unsigned int就要先转换成float类型在执行运算。
警告:
但是算术转换要合理,要不然会有一些潜在的问题。
#include <stdio.h>
int main()
{
float f = 3.14;
int num = f;//隐式转换,会有精度丢失
printf("%d", num);
return 0;
}
这里就发生了精度丢失。
操作符属性
复杂表达式的求值有三个影响的因素。
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级如下:
下面在介绍以下问题表达式:
//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f
注释:代码在计算的时候,由于*比+的优先级高,只能保证,的计算是比+早,但是优先级并不
能决定第三个比第一个+早执行。
所以表达式的计算机顺序就可能是:
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
像这种问题表达式可能不同的编译器就会计算不同的结果。所以大家在编程时尽量避免问题表达式。
//表达式2
c + --c;
注释:同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得
知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义
的。
再来看一看非法表达式:
#include <stdio.h>
int main()
{
int i = 10;
i = i-- - --i * (i = -3) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
相信这种代码在大多数学校学习时,老师都会演示一遍来炫技(笔者的学校就是这样的)。
但表达式3在不同编译器中测试结果:非法表达式程序的结果
如图,曾有人做过实验,这种代码在各种不同的编译器上都会有不同的结果。
#include <stdio.h>
//代码4
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n", answer);//输出多少?
return 0;
}
又像这种函数代码是否有问题?
有问题!
虽然在大多数的编译器上求得结果都是相同的。
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,
再算减法。
函数的调用先后顺序无法通过操作符的优先级确定。
所以在日常编程中,大家不要偷懒而导致编了错误的代码,要避免写bug,当然要是一个程序猿铁了心写bug那是谁也拦不住。
最后期待您的三连,如有错误,欢迎私信或在评论区指正。
边栏推荐
- 区块元素、内联元素(<div>元素、span元素)
- 25K test old bird's 6-year experience in interviews, four types of companies, four types of questions...
- 金山云团队分享 | 5000字读懂Presto如何与Alluxio搭配
- leetcode一步解决链表反转问题
- Introduction to Grid Layout
- Meta公司内部项目-RaptorX:将Presto性能提升10倍
- C language entry combat (13): decimal number to binary
- 说好的女程序员做测试有优势?面试十几家,被面试官虐哭~~
- leetcode括号匹配问题——32.最长有效括号
- 提高软件测试能力的方法有哪些?看完这篇文章让你提升一个档次
猜你喜欢
关于web应用的目录结构
About the directory structure of the web application
A list of 300+ learning resources compiled by senior engineers of the Tao Department (the latest version in 2021)
How much does a test environment cost? Start with cost and efficiency
程序员写PPT的小技巧
JUC(二)原子类:CAS、乐观锁、Unsafe和原子类
Browser onload event
25K测试老鸟6年经验的面试心得,四种公司、四种问题…
Shell 脚本不同玩法
What is the most important ability of a programmer?
随机推荐
How H5 realizes evoking APP
leetcode括号匹配问题——32.最长有效括号
nacos registry
Constructors, member variables, local variables
C language: Check for omissions and fill in vacancies (3)
Difference and analysis of CPU usage and load
【C语言】LeetCode26.删除有序数组中的重复项&&LeetCode88.合并两个有序数组
提高软件测试能力的方法有哪些?看完这篇文章让你提升一个档次
leetcode 665. Non-decreasing Array 非递减数列(中等)
关于 VS Code 优化启动性能的实践
Meta公司新探索 | 利用Alluxio数据缓存降低Presto延迟
Shuttle + Alluxio 加速内存Shuffle起飞
测试环境要多少?从成本与效率说起
深度学习——CNN实现MNIST手写数字的识别
Mysql implements optimistic locking
golang's time package: methods for time interval formatting and output of timestamp formats such as seconds, milliseconds, and nanoseconds
Redis集群模式
25K测试老鸟6年经验的面试心得,四种公司、四种问题…
C竞赛训练
golang generics