admin管理员组

文章数量:1794759

条件编译 #和##运算符

正文开始

前言: 本章为C语言语法完结撒花, 下文将进行C语言中#和##操作符以及条件编译的讲解, 来进一步让我们了解C语言. 作者主页: 酷酷学!!!

1. #运算符

#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。 #运算符所执⾏的操作可以理解为”字符串化“。

当我们有⼀个变量 int a = 10; 的时候,我们想打印出: the value of a is 10 . 就可以写:

代码语言:javascript代码运行次数:0运行复制
#define PRINT(n) printf("the value of "#n " is %d", n);

当我们按照下⾯的⽅式调⽤的时候:

PRINT(a);//当我们把a替换到宏的体内时,就出现了#a,⽽#a就是转换为"a",时⼀个字符串

代码就会被预处理为:

代码语言:javascript代码运行次数:0运行复制
printf("the value of ""a" " is %d", a);

运⾏代码就能在屏幕上打印:

代码语言:javascript代码运行次数:0运行复制
the value of a is 10

2. ##运算符

##可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的⽂本⽚段创建标识符。 ## 被称为记号粘合 这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的。

这⾥我们想想,写⼀个函数求2个数的较⼤值的时候,不同的数据类型就得写不同的函数。

代码语言:javascript代码运行次数:0运行复制
int int_max(int x, int y)
{
 return x>y?x:y;
}
float float_max(float x, float y)
{
 return x>y?x:y;
}

但是这样写起来太繁琐了,现在我们这样写代码试试:

代码语言:javascript代码运行次数:0运行复制
//宏定义 
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
 return (x>y?x:y); \
}

使⽤宏,定义不同函数

代码语言:javascript代码运行次数:0运行复制
GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名 
GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名 
int main()
{
 //调⽤函数 
 int m = int_max(2, 3);
 printf("%d\n", m);
 float fm = float_max(3.5f, 4.5f);
 printf("%f\n", fm);
 return 0;
}

输出:

代码语言:javascript代码运行次数:0运行复制
3
4.500000

3. 条件编译

在编译⼀个程序的时候我们如果要将⼀条语句(⼀组语句)编译或者放弃是很⽅便的。因为我们有条件编译指令。

⽐如说:

代码语言:javascript代码运行次数:0运行复制
调试性的代码,删除可惜,保留⼜碍事,所以我们可以选择性的编译。
代码语言:javascript代码运行次数:0运行复制
#include <stdio.h>
#define __DEBUG__

int main()
{
    int i = 0;
    int arr[10] = { 0 };
    for (i = 0; i < 10; i++)
    {
        arr[i] = i;
#ifdef __DEBUG__
        printf("%d\n", arr[i]);//为了观察数组是否赋值成功。  
#endif //__DEBUG__
    }
    return 0;
}

常⻅的条件编译指令:

代码语言:javascript代码运行次数:0运行复制
1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。 
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif

2.多个分⽀的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif

3.判断是否被定义
#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

4.嵌套指令
#if defined(OS_UNIX)
	#ifdef OPTION1
		unix_version_option1();
	#endif
	
	#ifdef OPTION2
		unix_version_option2();
	#endif
	
#elif defined(OS_MSDOS)
	#ifdef OPTION2
		msdos_version_option2();
	#endif
	
#endif

4. 题目分享

  1. 写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明 考察:offsetof宏的实现
代码语言:javascript代码运行次数:0运行复制
#include<stdio.h>
#define offset(structType, MemberName) ((size_t)&((structType*)0)->MemberName)

struct stu
{
	char name[10];
	int age;
	char sex[10];
};

int main()
{
	size_t ret = 0;
	ret = offset(struct stu, age);
	printf("%d\n", ret);
	return 0;
}

运行结果:

代码分析:

代码语言:javascript代码运行次数:0运行复制
//Struct是结构体类型名,MenberName是成员名.
//1、先将0转换为一个结构体类型的指针,
// 相当于某个结构体的首地址是0。
// 此时,每一个成员的偏移量就成了相对0的偏移量,
// 这样就不需要减去首地址了。
//
//2、对该指针用->访问其成员,并取出地址,
// 由于结构体起始地址为0,此时成员偏移量直接相当于对0的偏移量,
// 所以得到的值直接就是对首地址的偏移量。
//
//3、取出该成员的地址,强转成size_t并打印,就求出了这个偏移量。
  1. 写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。
代码语言:javascript代码运行次数:0运行复制
#define SWAP_BITS(num) (((num & 0xAAAAAAAA) >> 1) | ((num & 0x55555555) << 1))

代码分析:

代码语言:javascript代码运行次数:0运行复制
//交换奇偶位,需要先分别拿出奇偶位。
//既然是宏,分别拿出用循环不是很现实,
//那就用& 这些位的方式来做。奇数位拿出,
//那就是要& 上010101010101……,偶数位拿出,
//就是要& 上101010101010……,对应十六进制分别是555……和aaa……,
//一般我们默认是32位整数,4位对应一位16进制就是8个5,8个a。
//通过 & 0x55555555的方式拿出奇数位和 & 0xaaaaaaa的方式拿出偶数位。
//奇数位左移一位就到了偶数位上,偶数位右移一位就到了奇数位上,
//最后两个数字或起来,就完成了交换

总结

以上是本文的全部内容, 如果觉得有帮助还望点赞收藏, 如有错误恳请指正

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-04-20,如有侵权请联系 cloudcommunity@tencent 删除函数指针字符串变量编译

本文标签: 条件编译 和运算符