正整数的原、反、补码都相同。
负整数的三种表⽰⽅法各不相同。
在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理
2进制转10进制举例:
2进制转8进制:
从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算.
0开好头的数组,会被当做8进制。
2进制转16进制:
从2进制序列中右边低位开始向左每4个2进制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
16进制的数字每⼀位是0~9,a ~f 的,16进制表⽰的时候前⾯加0x
操作数只能是整数
对于移位运算符,不要移动负数位
int main()
{
int a = 10;
int b = a << 1;
printf("a=%d ", a);//a没变
printf("b=%d ", b);
a = -10;
int c = a << 1;
printf("c=%d \n", c);
return 0;
}
逻辑右移:右边丢弃,左边补0
算术右移:右边丢弃,左边补原符号位
用上面哪一个右移取决于编译器,大部分编译器采用算术右移。
int main()
{
int num = -1;
int n = num >> 1;
printf("%d",n);
return 0;
}
& 按位与
| 按位或
^ 按位异或
按位:按二进制位
操作数只能是整数
规则:都为1才是1,有0就是0
int main()
{
int a = 5;
int b = -6;
int c = a & b;//写出a和b的二进制位都是1为1,有0为0
printf("%d",c);
return 0;
}
规则: 都是0才为0,有1就是1
int main()
{
int a = 5;
int b = -6;
int c = a | b;
printf("%d", c);
return 0;
}
规则:二进制相同为0,不同为1
int main()
{
int a = 5;
int b = -6;
int c = a ^ b;
printf("%d",c);
return 0;
}
不创建第三个变量,实现两个变量值交换
原理:
(1)一个变量按位异或自己结果是0,即a^a=0;
(2)一个变量按位异或0结果为自己,即a^0=a;
int main()
{
int a = 4;
int b = 6;
a = a^b;
b = a^b;//b=a^b^b=a^0=a
a = a^b;//a=a^b^a=0^b=b
printf("%d %d",a,b);
return 0;
}
a = a^b;
b = a^b;//b=a^b^b=a^0=a
a = a^b;//a=a^b^a=0^b=b
异或只能用作整数交换,代码可读性较低,效率低于创建变量的方法
求21的二进制第4位
int main()
{
int a = 21;
int b = (a >> 4 & a);
printf("%d", b);
return 0;
}
第一种方法:
#include
int main()
{
int num = 0;
scanf("%d",&num);
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if ((num >> i) & 1)
count++;
}
printf("%d的二进制中1的个数是%d",num,count);
return 0;
}
#include
int main()
{
int num = 0;
scanf("%d",&num);
int i = 0;
int count = 0;
while (num)
{
count++;
num = num & (num - 1);
}
printf("二进制中1的个数是%d",count);
return 0;
}
规则:用逗号隔开的多个表达式,从左到右执行,整个表达式结果是最后一个表达式的结果。
#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;
}
操作数:一个数组名 + 一个索引值
int arr[100];
arr[19]=10;
[ ]两个操作数是arr和9
接受一个或多个操作数:
第一个操作数:函数名
剩余操作数:传递给函数的参数
栗子:
#include
void test1()
{
printf("hehe\n");
}
void test2(int a)
{
printf("%d\n",a);
}
int main()
{
test1();
int x = 10;
test2(x);
return 0;
}
操作符的两个属性决定了表达式求值计算顺序
优先级
如果一个表达式有多个运算符,要看哪个运算符应该优先执行。
结合性
当运算符的优先级相同时,优先级无法决定先计算哪个,于是我们就要看是左结合还是右结合。大部分是左结合,从左到右。
我们需要记住这些操作符优先级,其他遇到查表就行:
3*2/5;
*和/的优先级一样,结合性都是从左到右,所以从左到右算。
表达式求值前要就行类型转换,当表达式的值换到适当的类型,才开始计算。
C语言整型算术运算总是至少以缺省整型类型的精度进行。
为了获得这个精度,表达式中的字符char 和短整型 short 操作数在使用之前被转换为普通整型(int 或 unsigned int),这种转换叫做整型提升
char和short类型太小了,不能直接拿来执行运算,得先变长成int字节长度,运算后再截断多余位变回原来字节长度。
栗子:
char a=3;
char b=127;
char c=a+b;
printf("c=%d",c);
3 : 00000000 00000000 00000000 00000011(补码)
127: 00000000 00000000 00000000 01111111(补码)
3+127: 00000000 00000000 00000000 10000010(补码)
%d:打印整型,整型提升
c发生截断: 10000010(补码)
又正数原码反码补码相同,
负数转码规则: 原码取反转反码 ,反码+1转补码
因为我们得到的是补码,所以提升要注意我们的是有符号还是符号进行区分补位。
10000010(有符号前补符号位1)
提升:1111 1111 1111 1111 1111 1111 1000 0010(补码)
提升完成,当我们想看最后结果,则进行下面转换成原码:
1111 1111 1111 1111 1111 1111 1000 0001(补码-1得到反码)
1000 0000 0000 0000 0000 0000 0111 1110(取反得到原码)
原码转换10进制是 -126
如果某个操作符的各个操作数属于不同的类型,
那么除非其中一个操作数转换为另一个操作数的类型,
否则操作就无法进行。
寻常算术转换如下:
1.long double
2.double
3.float
4.usigned long int
5.long int
6.unsigned int
7.int
排名越靠后的类型,首先要转换为另外一个操作数类型后执行运算。
a*b+c*d+e*f
优先级不能决定第三个*比第一个+早执行
2.
c+--c;
优先级只能保证自减–在+前面执行,但是+的左操作数在右操作数之前还是之后,并不确定。
3.
int main()
{
int i=10;
i=i-- - --i *(i=-3)* i++ + ++i;
printf("i = %d\n",i);
return 0;
}
在不同编译器下,有不同结果。
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();我们只能通过优先级知道✖号比➖号先执行,但是函数调用先后顺序不知道。
5.
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n",ret);
printf("%d\n",i);
return 0;
}
第一个在➕执行时,不知道第三个前置➕➕有没有执行,无法知道先后顺序。
所以它在VS2022结果是12 4,在gcc编译器是10 4。
所以,即使操作符有优先性和结合性,但是表达式的运算顺序不能确定唯一路径。
谢谢阅读,若有不足,望指正。