🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
🐰宏的缺点
1.每次使用宏的时候,会将宏定义的代码插入到程序当中,增加了程序的长度
2.宏没有办法调式
3.宏由于类型无关,也不够严谨
4.宏可能会带来运算符优先级的问题,导致程序容易出错。(可以适当添加圆括号来解决)
1. #include<stdio.h> 2. #define MAX(x,y) ((x)>(y)?(x):(y)) 3. int Max(int x,int y) 4. { 5. return x>y?x:y; 6. } 7. int main() 8. { 9. int a=2,b=3; 10. //函数 11. printf("%d\n",Max(a, b)); 12. //宏 13. printf("%d\n",MAX(a, b)); 14. return 0; 15. }
🐰用宏实现动态开辟的技巧
1. #include<stdio.h> 2. #include<stdlib.h> 3. #define MALLOC(x,type) (type*)malloc(x*sizeof(type)) 4. int main() 5. { 6. int *p=(int*)malloc(10*sizeof(int)); 7. //宏 8. //这样使得动态开辟空间更加方便 9. int* p2=MALLOC(10, int); 10. //只需传递开辟空间的大小,和类型 11. return 0; 12. }
🐰命名约定
标准并没有规定宏名必须全部大写,函数名不能全部大写,只是一种俗称的习惯,这样更容易区分宏和函数
把宏名全部大写
函数名不全部大写
🐰#undef
#undef用于移除一个宏定义
例如:
1. #undef用于移除一个宏定义 2. #include<stdio.h> 3. #define M 100 4. int main() 5. { 6. printf("%d\n",M); 7. #undef M//M就被移除了 8. printf("%d\n",M); 9. return 0; 10. }
🐰条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令
调试性的代码,删除可惜,保留碍事,我们选择性编译
🌸#ifdef
1. #ifdef symbol//如果symbol定义,则执行#ifdef symbol和endif的语句 2. 3. ... 4. 5. endif
例如:
1. #include<stdio.h> 2. #define __DEBUG__ 10 3. int main() 4. { 5. int arr[10]; 6. for(int i=0;i<10;i++) 7. { 8. arr[i]=i; 9. #ifdef __DEBUG__//如果没有定义__DEBUG__,则后面#endif之前的语句不编译 10. printf("%d ",arr[i]); 11. #endif 12. } 13. return 0; 14. } 15. 结果: 16. 0 1 2 3 4 5 6 7 8 9 17. 因为这里的__DEBUG__ 被定义了,所以会执行#ifdef和#endif之间的语句
🌸#ifndef
1. #ifndef symbol //如果symbol没定义,则执行#ifdef symbol和endif的语句 2. 3. ... 4. 5. #endif
1. #endif 2. #include<stdio.h> 3. #define __DEBUG__ 10 4. int main() 5. { 6. int arr[10]; 7. for(int i=0;i<10;i++) 8. { 9. arr[i]=i; 10. #ifndef __DEBUG__ 11. printf("%d ",arr[i]); 12. #endif 13. } 14. return 0; 15. } 16. 没有输出结果,因为__DEBUG__ 定义了,所以不会执行#ifndef和#endif之间的语句。这个和#ifdef和#endif执行的情况相反
🌸#if defined(symbol)
1. #if defined(symbol)//如果symbol定义,则执行#ifdef symbol和endif的语句 2. 3. ... 4. 5. endif
1. #include<stdio.h> 2. #define __DEBUG__ 10 3. int main() 4. { 5. int arr[10]; 6. for(int i=0;i<10;i++) 7. { 8. arr[i]=i; 9. #if defined(__DEBUG__) 10. printf("%d ",arr[i]); 11. #endif 12. } 13. return 0; 14. } 15. 结果:0 1 2 3 4 5 6 7 8 9 16. 因为定义了__DEBUG__,所以会执行#if defined(__DEBUG__)和#endif之间的代码,这个和#ifdef和endif使用情况一样
🌸#if !defined(symbol)
1. #if !defined(symbol)//如果symbol没定义,则执行#ifdef symbol和endif的语句 2. 3. ... 4. 5. endif
1. #include<stdio.h> 2. #define __DEBUG__ 10 3. int main() 4. { 5. int arr[10]; 6. for(int i=0;i<10;i++) 7. { 8. arr[i]=i; 9. #if !defined(__DEBUG__) 10. printf("%d ",arr[i]); 11. #endif 12. } 13. return 0; 14. }
🐰常见的条件编译指令
🌸#if
1. #if 常量表达式 2. 3. ... 4. 5. #endif
如果常量表达式的值为真,则执行#if和#endif之间的代码
1. #include<stdio.h> 2. int main() 3. { 4. #if 1==1//如果常量表达式的值为真,则执行#if和#endif之间的代码 5. printf("hehe\n"); 6. #endif 7. return 0; 8. } 9. 结果: 10. hehe 11. 因为1==1表达式的值为真
🌸多分支的条件编译
1. #if 常量表达式 2. 3. ... 4. 5. #elif 常量表达式 6. 7. ... 8. 9. #elif 常量表达式 10. 11. ... 12. 13. endif
1. #include<stdio.h> 2. int main() 3. { 4. #if 1==1//这里可以与if 和else if的联系,只会执行一个(一组)语句 5. printf("hehe1\n"); 6. #elif 1==1// 7. printf("hehe2\n"); 8. #elif 1==1// 9. printf("hehe3\n"); 10. #endif 11. return 0; 12. } 13. 结果:hehe1 14. 因为第一个常量表达式1==1的值为真,所以执行printf("hehe2\n");,不会执行以下的分支语句了,这里与if和else if执行情况差不多
🐰文件包含
头文件的包含的方式
1.本地文件的包含
#include"test.h"
查找策略:先找在源文件所在的目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件
如果找不到就提示编译错误
2.库文件的包含
#include<stdio.h>
查找策略:直接去标准位置查找头文件,如果找不到就提示编译错
如果找不到就提示编译错误
注意:虽然库函数的头文件可以用双引号查找,但是进行了两次查找,降低了代码的速率
头文件多重包含(C++和类的多重继承类造成成员数据的重复)
解决方法:
添加#pragma once,可以防止头文件多刺被包含
🐰atoi
用于将字符串转换成整形
atoi的原型:
int atoi (const char * str);
const char * str:字符串的首地址
1. #include<stdio.h> 2. #include<stdlib.h> 3. int my_atoi(char* ptr) 4. { 5. int sum=0; 6. int flag=1; 7. if(*ptr=='+') 8. { 9. flag=1; 10. ptr++; 11. } 12. if(*ptr=='-') 13. { 14. flag=-1; 15. ptr++; 16. } 17. while(*ptr) 18. { 19. sum=sum*10+(*ptr-'0'); 20. ptr++; 21. } 22. sum*=flag; 23. return sum; 24. } 25. int main() 26. { 27. int ret=atoi("-1234"); 28. int rat=my_atoi("-1234");//模拟实现atoi 29. printf("%d\n",ret); 30. printf("%d\n",rat); 31. return 0; 32. } 33. 结果: 34. -1234 35. -1234
🐰offsetof
offsetof宏用于与求偏移量(结构体,联合体...)
offsetof的原型:
offsetof (type,member)
type:类型(结构体,联合体...)
member:成员(结构体成员...)
1. #include<stdio.h> 2. #include<stddef.h> 3. typedef struct S 4. { 5. int a;//0-3 6. int b;//4-7 7. char c;//8 8. double d;//16-23 9. }S; 10. int main() 11. { 12. printf("%d\n",offsetof(S, a)); 13. printf("%d\n",offsetof(S, b)); 14. printf("%d\n",offsetof(S, c)); 15. printf("%d\n",offsetof(S, d)); 16. return 0; 17. } 18. 结果: 19. 0 20. 4 21. 8 22. 16 23. 分别对应着a,b,c,d的偏移量
🐰模拟实现offsetof
1. #include<stdio.h> 2. #include<stddef.h> 3. typedef struct S 4. { 5. int a;//0-3 6. int b;//4-7 7. char c;//8 8. double d;//16-23 9. }S; 10. #define OFFSETOF(t,m) (int)(&((S*)0)->m)//把0地址处强制转化为结构体指针,然后取出成员的地址,在以整形的形式输出,这样就可以得到偏移量了,(换句话说,就是结构体的成员假设从0地址处进行存储) 11. //注意:强制类型转化,不会真的原来的类型转化了,而是把原来的类型进行临时拷贝,然后再转化为自己想要的类型,所以没有真正把系统0地址转化为结构体指针,而是把0地址的拷贝转化为了结构体指针类型。 12. int main() 13. { 14. printf("%d\n",OFFSETOF(S,a)); 15. printf("%d\n",OFFSETOF(S,b)); 16. printf("%d\n",OFFSETOF(S,c)); 17. printf("%d\n",OFFSETOF(S,d)); 18. return 0; 19. } 20. 结果: 21. 0 22. 4 23. 8 24. 16
🐰有关宏的习题
🌸写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。
1. 解题思路: 2. 3. 首先保存奇数位 4. 5. 保存的方法:例如:10(00000000000000000000000000001010) 6. 7. 10: 00000000000000000000000000001010 8. 9. 0x55555555 01010101010101010101010101010101 10. 11. 只要让10按位与上0x55555555就能保留奇数位(相同为相同的,不同则为0) 12. 13. 奇数位结果:00000000000000000000000000000000 14. 15. 保存偶数位 16. 17. 保存的方法:例如:10(00000000000000000000000000001010) 18. 19. 10: 00000000000000000000000000001010 20. 21. 0xaaaaaaaa 10101010101010101010101010101010 22. 23. 只要让10按位与上0xaaaaaaaa就能保留奇数位(相同为相同的,不同则为0) 24. 25. 偶数位结果:00000000000000000000000000001010 26. 27. 让奇数位左移一位 28. 29. 00000000000000000000000000000000<<1得到00000000000000000000000000000000 30. 31. 32. 让偶数位右一位 33. 34. 00000000000000000000000000001010>>1得到00000000000000000000000000000101 35. 36. 然后移位后的奇数位和偶数位相加00000000000000000000000000000000+00000000000000000000000000000101 37. 38. 最后的到00000000000000000000000000000101为5
1. #include<stdio.h> 2. #define SWAP(n) n=(((n&0x55555555)<<1)+((n&0xaaaaaaaa)>>1)) 3. int main() 4. { 5. int a=10; 6. SWAP(a); 7. printf("%d\n",a); 8. return 0; 9. } 10. 结果: 11. 5 12. 10的二进制补码:00000000000000000000000000001010 13. 转化后的补码:00000000000000000000000000000101 14. 所以为5
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸