目录
一.移位 *** 作符
二.位 *** 作符
三.sizeof *** 作符
四.结构体
五.表达式求值
1) 整型提升
2)算术转换(隐式转换)
3) *** 作符的属性
一.移位 *** 作符
1.整数的二进制表示有三种形式:
2.正数的原码,反码,补码是相同的
负数的原码,反码,补码是要计算的
3.整数在内存中存储的是补码的二进制
4. a:5的补码00000000000000000000000000000101
将该补码向左移动一位,左边丢去一位,右边补上一位0
打印为补码
5.a:-5的原码10000000000000000000000000000101
-5的反码111111111111111111111111111111111010
补码111111111111111111111111111111111011
<<1后的补码变为111111111111111111111111111111110110
反码111111111111111111111111111111110101
打印出的值为原码 10000000000000000000000000001010
6.原码,反码与补码
7.右移
a:5的补码00000000000000000000000000000101
右移 *** 作符:
(1)算术右移:右边丢弃,左边补原符号位
(2)逻辑右移:右边丢弃,左边补0
a:-5的原码10000000000000000000000000000101
-5的反码111111111111111111111111111111111010
补码111111111111111111111111111111111011
>>1后的补码变为111111111111111111111111111111111101
反码111111111111111111111111111111111100
打印出的值原码 10000000000000000000000000000011
>> 当前编译器,在右移采用算术右移
>>到底算术右移还是逻辑右移取决于编译器
8.警告
对于移位 *** 作符,不要移动负数,这个是标准未定义的
二.位 *** 作符&
按二进制位与
如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
|
按二进制位或
两个相应的二进制位中只要有一个为1,该位的结果值为1
^
按二进制位异或
若参加运算的两个二进制位值相同则为0,否则为1a^a=0 0^a=a
~
按二进制取反
~是一元运算符,用来对一个二进制的补码数按位取反,即将0变1,将1
1.为什么while (~scanf("%d",&n));可以终止循环?
while (scanf("%d", &n) != EOF) { ..... } scanf()读取失败的时候,返回EOF EOF---> -1 ~(-1)=0
2.交换两个数的值 (仅整数可以使用)
a = a ^ b; b = a ^ b ^ b; a = a ^ b ^ a;三.sizeof *** 作符
1.sizeof不是函数
2.sizeof求数组,求的是数组元素的类型*元素个数
int main() { int arr[10] = { 0 }; printf("%dn", sizeof(arr)); return 0; }
3.sizeof()内表达式不参与计算
int main() { int a=10; short s=0; printf("%dn", sizeof(s=a+2)); printf("%dn", sizeof(s)); return 0; } 2 0
test.c--->编译(求sizeof的结果,所以编译期间sizeof(s=a+2)变成了2)--->链接(s=a+2不存在了) --->test.exe
4.sizeof和函数
字符的地址仍然是48个字节,地址总是48个字节
void test1(int arr[]) { printf("%dn", sizeof(arr)); } void test2(int ch[]) { printf("%dn", sizeof(ch)); } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%dn", sizeof(arr)); printf("%dn", sizeof(ch)); test1(arr); test2(ch); return 0; } 40 10 4 4四.结构体
struct Stu { char name[20]; int age; float score; }; void print1(struct Stu ss) { printf("%s %d %fn", ss.name, ss.age, ss.score); } 结构体变量.成员名 void print2(struct Stu* ps) { printf("%s %d %fn", (*ps).name, (*ps).age, (*ps).score); printf("%s %d %fn", ps->name, ps->age, ps->score); } 结构体指针->成员名 int main() { struct Stu s = {"张三", 20, 90.5f}; strcpy(s.name, "张三丰"); scanf("%s", s.name); print1(s); print2(&s); return 0; }
五.表达式求值 1) 整型提升*(s.name) = "张三丰"// err
s.name首字符的地址,解地址后只有一个字符
*** 作方式:C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型 *** 作数在使用之前被转换为普通整型,这种转换称为整型提升1.正数:高位补1 2.负数:高位补0 3.无符号数:高位补0
意义:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的 *** 作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型 *** 作数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算
int main() { char c1 = 3; //00000000000000000000000000000011 //00000011 - c1 char c2 = 127; //00000000000000000000000001111111 //01111111 - c2 char c3 = c1 + c2; //00000000000000000000000000000011 - c1 //00000000000000000000000001111111 - c2 //00000000000000000000000010000010 - c3 //10000010 - c3 //11111111111111111111111110000010 (-1) //11111111111111111111111110000001 //10000000000000000000000001111110 //-126 printf("%dn", c3); return 0; }
int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if (a == 0xb6) printf("a"); if (b == 0xb600) printf("b"); if (c == 0xb6000000) printf("c"); return 0; } 打印:c //a和b发生了整型提升
int main() { char c = 1; printf("%un", sizeof(c));//1 printf("%un", sizeof(+c));//4 printf("%un", sizeof(-c));//4 return 0; } //在sizeof里发生运算,发生了整型提升后,就变为整型2)算术转换(隐式转换)
3) *** 作符的属性定义:如果某个 *** 作符的各个 *** 作数属于不同的类型,那么除非其中一个 *** 作数的转换为另一个 *** 作数的类型,否则 *** 作就无法进行。
看数据取值范围,最终是表达式中取值范围最大的那个类型
复杂表达式的求值有三个影响的因素。
1. *** 作符的优先级
2. *** 作符的结合性
3. 是否控制求值顺序。
从上到下优先级降低
结合型N/A: 无结合性 L-R : 从左向右
一些问题表达式
a*b+c*d+e*f (错误代码,无法确定唯一计算路径)
注释:代码1在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不能决定三个*哪一个比第一个+早执行。
c+--c (错误)注释:同上, *** 作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得知,+ *** 作符的左 *** 作数的获取在右 *** 作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
总结:我们写出的表达式如果不能通过 *** 作符的属性确定唯一的计算路径,那这个表达式就是存在问题的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)