一、++、--操作
- 后置++,先使用,后++
int a = 10; int b = a++;//a = 11, b = 10
前置++,先++,后使用
int a = 10; int b = ++a;//a = 11, b = 11
- a++,++a若没有对象接受,他们都只完成自增,没有任何区别
- for (i = 0; i < 10; i++) 与 for (i = 0; i < 10; ++i)没有区别
a ++ 完整的含义是先使用,在自增。如果没有变量接收,那么直接自增 ( 或者所谓使用,就是读取进寄存器,然后没有然后 ) 。
二、深度理解取余\取模&&取整
A、取整
①、0向取整trunc()
int a = 1.9, b = -2.9; printf("%d %d\n", trunc(a), trunc(b));//a = 1, b = -2
C语言除法默认0向取整
②、地板取整(向负无穷取整)floor()
#include <stdio.h> #include <math.h> int main() { double a = 1.9, b = -2.9; printf("%.1f %.1f\n", floor(a), floor(b));//a = 1.0, b = -3.0 return 0; }
③、向上取整(向正无穷取整)ceil()
#include <stdio.h> #include <math.h> int main() { double a = 1.9, b = -2.9; printf("%.1f %.1f\n", ceil(a), ceil(b));//a = 2.0, b = -2.0 return 0; }
④、四舍五入取整round()
#include <stdio.h> #include <math.h> int main() { double a = 1.2, b = -2.9; printf("%.1f %.1f\n", round(a), round(b));//a = 1.0, b = -3.0 return 0; }
B、取余\取模
- 如果a、b是两个自然数,可以证明存在唯一整数q、r使得a = q * d + r,且0 <= |r| <= |d|
- C语言:-10 = (-3) * 3 + (-1) Python:(-10) = (-4) * 3 + 2
- 所以,在不同语言中,同一个表达式,负数取模结果是不同的,我们叫正余数,负余数
取余和取模一样吗?
- 不严格等价,取余:取整时尽可能让商0向取整,取模:取整时尽可能让商向负无穷取整故在c中,%的本质是取余,Python中,%的本质是取模
- 理解链:对任何一个大于0的数,对其进行0向取整和负无穷取整方向是一致的,故取模等于取余,对任何一个小于0的数,对其进行0向取整和负无穷取整方向是相反的,取模不等于取余
同符号数据相除,得到的商,一定是正数(正数 vs 正整数),即大于 0 !故,在对其商进行取整的时候,取模等价于取余。 本质 2 符号: 参与取余的两个数据,如果同符号,取模等价于取余 余数的大小是取决于商的,而上商的大小取决于取整规则
- 计算时,先把数据从内存中加载到CPU中再做计算,在CPU内部寄存器中对数据做修改,不影响内存中的值,除非把数据写回内存中,换言之,寄存器中变量的值和内存当中变量的值在某一时间段可能是不一致的
三、预处理
一、宏替换
- :宏定义代表字符串的时候,一定要带上双引号,可以用\续行
- 程序翻译:将文本代码翻译成二进制代码
- 为什么要翻译:因为计算机只认识二进制
- 如何翻译:预处理——>编译——>汇编——>链接
-
预处理:头文件展开,去注释,宏替换,条件编译
编译:将二进制语言翻译成汇编语言
汇编:将汇编语言翻译成二进制文件(可重定向文件【可被链接,不可被执行】)
链接:自身程序+库文件进行关联,形成可执行程序
- 预处理去注释和宏替换的先后问题:先去注释再宏替换
二、#define定义表达式
#include <stdio.h>
#define INIT_VALUE(a, b)\
a = 0;\
b = 0;
int main()
{
int flag = 0;
scanf("%d", &flag);
int a = 100;
int b = 200;
printf("before: %d, %d\n", a, b);
if (flag)
INIT_VALUE(a, b);
else
printf("error!\n");
printf("after: %d, %d\n", a, b);
return 0;
}
此代码编译器直接报错,为何报错?查看预处理代码:
#include <stdio.h>
int main()
{
int flag = 0;
scanf("%d", &flag);
int a = 100;
int b = 200;
printf("before: %d, %d\n", a, b);
if(flag)
a = 0; b = 0;; //if倘若没有带{},那么if后面只能跟一条语句
else //else匹配if时,直接报错了
printf("error!\n");
printf("after: %d, %d\n", a, b);
return 0;
}
结论:如果想用一个宏替换不是一条一行简单语句,而是多条复杂语句,推荐使用do—while—zero结构
#include <stdio.h>
#define INIT_VALUE(a,b) do\
{\
a = 0;\
b = 0;\
}while(0)
int main()
{
int flag = 0;
scanf("%d", &flag);
int a = 100;
int b = 200;
printf("before: %d, %d\n", a, b);
if (flag) {
INIT_VALUE(a, b);
}
else {
printf("error!\n");
}
printf("after: %d, %d\n", a, b);
return 0;
}
宏的定义中,尽可能用小括号括起来,避免会有优先级的问题
二、#undef
- 宏可以在代码的任何地方定义,但习惯在最上面
- 宏的作用范围:从定义处开始往后都是有效的
-
undef是取消宏的意思,可以用来限定宏的有效范围
-
#include <stdio.h> #define X 3 #define Y X*2 #undef X #define X 2 int main() { int z = Y;//z = x*2(x = 2而不是3) printf("%d\n", z);//4 return 0; }
- 推荐:尽量使用普通函数而不要使用宏函数①、普通函数在编译时会进行语法检查②、使用宏函数若无明显错误,可能不会报错
三、条件编译
宏是否被定义 VS 宏为真or假
- #define BUG 1//宏被定义,且为真
- #define BUG 0//宏被定义,且为假
①、#ifdef(判断宏是否被定义)#ifndef + #else + #endif
②、#if(判断宏是否为真)+ #elif + #elif + #else + #endif
#if c 如果c为真则执行,如果定义了不给初始值会报错,如果没定义,会执行#else部分
③、#ifdef c = #if defined c(判断c是否被定义)
④、每个if必须以#endif结尾
#include <stdio.h>
#define DEBUG
int main()
{
#ifndef DEBUG
printf("hello debug\n");
#else
printf("hello release\n");
#endif
return 0;
}
#define DEBUG 3
int main()
{
#if DEBUG==0
printf("hello bit 0\n");
#elif DEBUG==1
printf("hello bit 1\n");
#elif DEBUG==2
printf("hello bit 2\n");
#else
printf("hello else\n");
#endif
return 0;
}
#define DEBUG
int main()
{
#if defined(DEBUG)
printf("hello debug\n");
#else
printf("hello release\n");
#endif
return 0;
}
四、文件包含
防止头文件被多次包含,方法1:#pragma once
方法2:#ifndef _TEST_H_ (XXX) 第一次:_TEST_H_没有被定义
#deine _TSET_H_ _TEST_H_立即被定义
................................
................................
#endif 第二次,第三次,n次不再执行
- 什么叫做头文件展开?把头文件的内容拷贝至目标源文件
- 如果头文件被重复包含一定会报错吗?不会,主要影响编译效率
- 宏替换和条件编译谁先执行?宏替换先执行
五、# 和 ##
- printf(" "" "" "" "),相邻字符串具有自动连接功能
- printf("A" "B" "C" "D") == printf("ABCD")
- char* str = "hello bit" printf(str)此写法正确
- #define STR(S) #S 将S对应的内容转化成字符串
- #:将宏参转化成字符串 ##:将##两侧的符号连接成新的符号