萌新的c语言学习15
C语言的预处理2
这大概是萌新c学习最后一篇了,后面更新c语言的小项目练习了,感恩 我的文章更多的像是笔记
实用价值其实有点低。后面更新解析全面,更加方便阅读的版本
宏在书写的时候会有一些带有副作用的参数影响
int main()
{
int a = 10;
int b = a + 1;
int b = ++a;
//相对来说第二个a它会自增
return 0;
}
下面是举例
int main()
{
int a = 10;
int b = a + 1;
int b = ++a;
//相对来说第二个a它会自增
return 0;
}
其实函数和宏的方法各有各的好处
//函数-1
int Max(int x, int y)
{
return (x > y ? x : y);
}
//宏-2
#define MAX(x,y) (x)>(y)?(x):(y)
int main()
{
int a = 10;
int b = 2;
//万一是浮点型的float呢
//那么函数形式就不对了
int max = Max(a, b);
printf("max=%d\n", max);
max = MAX(a, b);
printf("max=%d\n", max);
return 0;
}
函数比较慢但是直观吧, 宏定义就是直接替换 各有各的好处
函数在调用时,会有函数调用和返回的开销
因为define 在预处理阶段就完成了替换
宏的效率更高
宏用法与类型无关 常常用在执行简单的运算
宏的劣势
可能会大幅增加代码的长度
宏是无法调试的 函数我可以一步一步进去试探,宏只是简单的替换 押韵押韵
宏传递什么都可以
甚至宏可以传递一个定义
sizeof(arr)这种也可以传递
inline 内联函数
#undef
条件编译
在编译一个程序的时候,如果要将一条语句编译或者不参与编译 ,其实很方便
这就是条件编译 顾名思义 按照对应的条件来编译
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0;i < 10;i++)
{
arr[i] = 0;
#ifdef DEBUG
printf("%d ", arr[i]);
#endif
//如果DEBUG定义过 这句话就参与编译
//否则不参与编译
}
return 0;
}
定义后的版本
#define DEBUG
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0;i < 10;i++)
{
arr[i] = 0;
#ifdef DEBUG
printf("%d ", arr[i]);
#endif
//如果DEBUG定义过 这句话就参与编译
//否则不参与编译
}
return 0;
}
#if 还可以跟其他语句
#if 1 表示恒真参与编译
记住#if 和#endif 是一对的
还有else if 的表达式
int main()
{
#if 1==1
printf("haha\n");
#elif 2==1
printf("haha\n");
#else
printf("heihei\n");
#endif
return 0;
}
3.判断是否被定义
#ifdef symbol
就是如果定义了就参与编译
这个形式相当于
#if defined(symbol)
还有
#ifndef (if no define)
#define DEBUG 1
int main()
{
#if defined(DEBUG)
printf("hehe\n");
#endif
return 0;
}
还有
int main()
{
#ifndef DEBUG
printf("hehe\n");
#endif
return 0;
}
4.嵌套指令
预处理 文件包含
我们在使用自己的定义的库函数要用#include
如果包含本地文件就用双引号
库函数用<>
因为我们每次使用都要去访问这些不管是库函数还是自己定义的函数 那么非常冗余 有重复包含的现象
所以我们在写头文件时候带上
#ifdef
#endif
这就像是单片机的.h文件一样 为什么写这个就是防止重复嵌套
一般来说就是这样子的形式
#ifdnef __TEST_H__
#define __TEST_H__
int Add(int x , int y);
#endif
就是我们每次进去都会扫描一下 如果没有定义 那就去定义一下 ,如果下次读入判读一下有没有定义过 这样就可以防止一段代码被重复包含多次
上面这种写法是比较古老
也可以这么写
#pragma once //比较新颖
小题
请编写宏 计算结构体中某变量相对于首地址的偏移,并给出说明
#include<stddef.h>
struct s
{
char c1;
int a;
char c2;
};
int main()
{
//struct s s;
printf("%d\n", offsetof(struct s, c1));
printf("%d\n", offsetof(struct s, a));
printf("%d\n", offsetof(struct s, c2));
return 0;
}
// 0 4 8
//我们想要模拟实现offsetof的功能
我们假设在0地址放了一成员 那么随后的地址 就是他的偏移量
struct s
{
char c1;
int a;
char c2;
};
#define OFFSETOF(struct_name , member_name) &((struct_name*)0)->member_name
int main()
{
//struct s s;
printf("%d\n", OFFSETOF(struct s, c1));
printf("%d\n", OFFSETOF(struct s, a));
printf("%d\n", OFFSETOF(struct s, c2));
return 0;
}
&((struct_name*)0)->member_name
就是说0是一个地址 强制类型转化成
struct s* 这是类型 (struct s*) 然后这个指针指向了c1 ((struct s*)0)->c1
这其实指的是c1的内容再取地址 不就是c1的地址 也是偏移量 再转化成整型