【速过C primer plus】第五章
一、运算符踩坑
1)赋值:const标记的常量是可以修改的
#include<stdio.h>
#pragma warning (disable:4996)
void sum(int *b)
{
*b = *b + 10;
printf("函数输出是:%d",*b);
}
int main()
{
const int a = 10;
int* c = &a;
sum(c);
printf("实际输出是:%d,%d",*c,a);
}
? 对于赋值符号来说,最基础的定义是符号的左值必须是可修改的。const int 类型不能作为左值出现在赋值符号左边,但是使用指针引用时,会绕过const标记,直接指向内存,所以直接对指针引用的内存数据进行修改会导致const int常量内存数据被直接改变。所以尽量使用define来标记常量。
2)除法:整型的除法出现小数时会触发截断
#include<stdio.h>
#pragma warning (disable:4996)
int main()
{
int a = 5;
int b = 3;
printf("实际输出是:%d",(a/b));
}
//输出<<实际输出是:1
? 截断会直接丢弃小数部分,不会存在四舍五入的情况。虽然C语言允许,但是还是尽量避免出现整型和浮点型同时出现在一个计算式里的情况(可以使用强制转换符转换为相同类型再使用)。
? 在C99之前,趋零截断是少数计算机共有的标准,而非ANSI C标准。所以负数除法截断时,会根据不同计算机产生不同结构。计算结果为-3.8时,它可能通过截断的定义(去除小数部分,趋于变为小于这个数的一个整型数)导致结果变为-4;有可能具有趋零截断的规则,导致截断结果为-3。趋零截断最终在C99中被采用。
3)求模运算结果的正负值确定
? 在C99拥有趋零截断规则后,求模运算结果的正负值就被规定好了:仅与求模运算的左值正负值有关。
int a = -23 %( 14%5) % 2;
printf("%d",a);
//输出结果:-1
4)递增递减符快速记忆
? 递增递减符最挠人的问题就是前接赋值符号后,搞不清加减顺序。
#include<stdio.h>
#pragma warning (disable:4996)
int main()
{
int a = 1;
int b = 2;
int c;
c = ++a + (b++) + (b++) + a++;
printf("%d", c);
}
//输出结果:8
? 搞不清递增递减优先顺序时,记住一下两点:
? ①加号和变量,谁在前面先执行谁:b=a++ 先执行b=a 再进行a++ ,c=++a 先执行++a 再执行c=a 。
? ②递增递减每进行一次,递增递减变量自身会变化!【90%翻车的原因】
#include<stdio.h>
#pragma warning (disable:4996)
int main()
{
int a = 1;
int c, b;
b = ++a;
c = a++;
printf("%d,%d",b, c);
}
//输出结果是:2,2
? ③对于长混合增减的表达式,按前后缀分类讨论:(1)先计算前缀计算,把所有的前缀计算加完,然后把结果赋值给所有同名变量。(2)后缀符号统计完之后可以抹去,不会影响表达式计算结果,表达式计算完成后拿后缀个数计算单变量的值。
#include<stdio.h>
#pragma warning (disable:4996)
int main()
{
int a=1;
int c = 1;
int b;
b = (++a)+(--a)+(a--);
printf("%d,%d", b,a);
}
? 最后记住,对于递增递减的融合计算,很多时候编译器计算结果都是不同的,因为单变量的混合前后递增递减这种计算行为是ANSI C未定义的。
二、表达式踩坑
1)任何表达式都有值
#include<stdio.h>
#pragma warning (disable:4996)
int main()
{
int a;
int b;
b = 5 + (a = -2);
printf("%d", b);
}
//输出结果:3
赋值表达式的值是赋值的结果
<、>、=、=、&&、||、!这些逻辑计算符的结果是表达式的真假
声明变量不是表达式
2)强制类型转换 · 降级
? 升级往往不会有任何问题,但是降级将会牵扯到高向低位的截断或者约数,这里有以下规则:
①浮点向整型的转换,会通过截断实现,负数部分遵循趋零截断。
②长整型转化为短整型,可以用短整型的总长求模,最后讨论正负(如果目标是无符号的,可以直接不讨论)
|