c语言学习之路————预处理2.0

  1.带副作用的宏参数

   如果宏的参数中带有改变传过来的参数,形式比如说自增、自减,它可能带有副作用exp:

#define MAX(x,y) ((x>y)?(x):(y))
int main()
{
	int a = 10;
	int b = 20;
	printf("%d\n", MAX(a++, b++));//21
	//printf("%d",(a++>b++)?(a++):(b++));
	//这里比较完之后a,b都自增了,输出的较大值b会是21,在输出之后b又自增
	printf("%d\n", a);//11
	//a在上面的式子中只自增了一次变成11
	printf("%d\n", b);//22
	//而b自增了两次,一次在比较之后,一次在输出之后,所以b现在等于22
	return 0;
}

 2.宏和函数的对比

函数的参数需要特定的参数,而宏在传参的时候不需要考虑数据类型,它只是进行替换;但在程序序调试的时候,宏出现的错误不容易被找出;函数在调用和传参还有返回需要大量的汇编代码,花费的时间可能更长,而宏在预处理阶段就发生了替换;但是在每次宏的替换都会将一段代码插入到代码中,而函数仅有一段代码,每次使用的时候仅需调用;函数参数是不能传数据类型的,而宏可以;

#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
#define SIZEOF(type) sizeof(type)
void put_data(int* pr)
{
	int i = 0;
    //动态连续空间放入数据
	for (i = 0; i < 10; i++)
	{
		pr[i] = i;
		
	}
    //打印
	for (i=0; i <10; i++)
	{
		printf("%d ", pr[i]);
	}
	printf("\n");
}
int main()
{
    //宏传类型参数,可行
	printf("%d\n", SIZEOF(int));//4

	//动态开辟空间
	int* ptr1 = (int*)malloc(10 * sizeof(int));
	put_data(ptr1);//0 1 2 3 4 5 6 7 8 9
	
	int* ptr = MALLOC(10, int);
	put_data(ptr);//0 1 2 3 4 5 6 7 8 9
    //释放空间
	free(ptr1);
	ptr1 = NULL;
	free(ptr);
	ptr = NULL;
	return 0;
}

 #undef用于取消符号定义的

#define MAX(x,y) ((x>y)?(x):(y))
int main()
{
	int a = 20;
	int b = 30;
	printf("%d", MAX(a, b));
#undef MAX(x,y)
	printf("%d", MAX(a, b));//error 未定义标识符
	return 0;
}

  3.命令行定义

gcc test.c -D SZ=10在程序运行之前就定义好,

4.条件编译

在一段程序中,也许可以遇见我们只在特定情况下需要的代码,或者只需要使用一次的代码,我们可以通过条件编译,进行判断选择这段代码需不需要编译

#define PRINT_S
int main()
{
	int a = 20;
	int b = 0;
#ifdef PRINT//这里的就是说之前定义过PRINT这个符号,程序就会编译这段代码,反之就会跳过
	printf("%d", a);//注意如果一段代码不会进行编译,就是灰色的(vs2017下)
#endif//这里的endif是条件编译的结束语句,与ifdef搭配
#ifdef PRINT_S
	printf("%d", a);//20
#endif
	//1.#if 常量表达式
#if 1 //为真进行编译
	printf("%d", a);//20
#endif
	//2.多分支判断
#if 20>30 //为假不进行编译
	printf("%d", a);
#elif 20==30
	printf("%d", a);
#else 
	printf("%d", a);//20
#endif
	//3.#if defined(符号)
#if defined(PRINT_S)//如果定义过
	printf("%d", a);//20
#endif
#if !defined(PRINT_S)//如果没有定义过(但定义过)
	printf("%d", a);//20
#endif
	//4.
	return 0;
}

5.文件包含

在头文件包含的过程,是将头文件的内容copy一份到现在.c程序中进行编译,在我们实际写工程的过程中,其实会有意无意的将一个头文件包含多次,那么整个项目当中的就会有很多的代码冗余,在我们的库函数有解决办法

//假设这是我们要包含的头文件,我们一般会采取以下方式,防止一个头文件被多次包含
#ifndef __PASS__
#define __PASS__
//这里面是头文件的内容
//.......
#endif
//这样头文件的内容只能被包含一次(编译)

//当然还有其他的解决办法(现在多采用)
#pragma once
//这里面是头文件的内容
//.......

  其他的预处理指令:#error(如果在编译程序的过程中遇到,会编译一个错误提示信息,并停止编译)、#pragma(指定一个跟踪选择)、#line(改变当前命令行数和文件名称)等                   

6.宏offsetof可以查偏移                                                                                                                       

#include <stddef.h>
struct S
{
	//默认对齐数为8
	int a;//4 占用了偏移量为0 1 2 3的地址共四个字节
	char b;//1 占用了偏移量为4的地址 共一个字节
	double c;//8 现在接下来使用的的空间地址的偏移量为5,但5不是八的倍数,所以要将指针移到偏移量为8的地址继续接下来的的存储,
	//前面浪费了3个字节的空间,再将c存进去,占8个字节
}s;
//这个结构体的大小为16  为最大对齐数的倍数
#define OFFSETOF(struct_typename,member_name) (int)&(((struct_typename*)0)->member_name)
                                               //解释:我们将结构体的开始放成员的地址为0(结 
                                              //构体类型的地址或者指针)当首地址为0,
                                               //那对应偏移量的成员的地址就等于偏移量,exp: 
                                               //上面b的地址就是偏移量4,最后的类型强转
                                               //的强制类型转化,将地址的转换为整型,地址是十 
                                               //六进制数字,而如果指针变量为整型常量,指针的
                                               //存放的地址将会被赋值为这个整型常量(((struct 
                                               //S*)0)->a)的地址为  0X00000000,在这个地址偏 
                                               //移4之后放下b,而此时b的地址为0x00000004,对 
                                               //这些地址强转成整型,就会得到0,4,8等成员的 
                                               //相较首地址的偏移量
int main()
{
	//printf("%d\n", sizeof(s));//16
    //使用c库里定义的宏
	//printf("a在结构体中的偏移量%d\n", offsetof(struct S,a));//0
	//printf("b在结构体中的偏移量%d\n", offsetof(struct S, b));//4
	//printf("c在结构体中的偏移量%d\n", offsetof(struct S, c));//8
 
    //使用上面自己定义的宏
	printf("a在结构体中的偏移量%d\n", OFFSETOF(struct S, a));//0
	printf("b在结构体中的偏移量%d\n", OFFSETOF(struct S, b));//4
	printf("c在结构体中的偏移量%d\n", OFFSETOF(struct S, c));//8

	return 0;
}

                                                                                                                                                                                                                      

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值