杂货边角(5):预编译指令#家族define/pragma

2023-10-12 22:32

本文主要是介绍杂货边角(5):预编译指令#家族define/pragma,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

编译器提供的预处理指令主要是以#作为首字符的,如本文的两个主角#define和#pragma。合理的使用预处理指令可以使得编写的程序更便于修改(#define MAX_SUM 10000)、平台兼容性(#ifdef WIN32 ... #else...#endif)和调试(#define __DEBUG)。如下的例子

/*********根据操作系统不同设置不同的处理方式,以达到同样的功能效果************/
#ifndef WIN32
//Linux brk system call
static int brk(void* end_data_segment) {int ret = 0;//brk system call number:45//in /usr/include/asm-i386/unistd.h://#define __NR_brk 45asm("movl $45, %%eax \n\t""movl %1, %%ebx  \n\t""int $0x80       \n\t""movl %%eax, %0  \n\t":"=r"(ret):"m"(end_data_segment) );
}
#endif#ifdef WIN32
#include <Windows.h>
#endif
/***************用于在程序中启用debug定点输出信息的功能*******************/
#ifdef __DEBUG
#define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)
#define MY_ASSERT(value)  if(!(value)) { PRINT_ERROR("\"%s\" failed at %s:%d %s \n",\#value,__FILE__,__LINE__,__FUNCTION__);}
#define SHOW_LOCA()     PRINT_ERROR(" current line_NO = %d \n", __LINE__ );
#else
#define PRINT_ERROR(...) 
#define MY_ASSERT(value)  
#define SHOW_LOCA()    
#endif
/*
**如果编译时,输入编译参数/D __DEBUG则会启用带有错误信息输出信息的debug版本,如果
**不定义__DEBUG宏则会编译处正常的release版
*/

1. #define宏定义和宏参数形式

关于 #define讲解较好的文章请查考这里。其实#define宏定义的形式还是蛮简单的,毕竟不是Lisp这种把宏作为语言核心特性的顶级玩法。简单的通过一个代码例子即可以说明地差不多。

#include <stdio.h>
#include <string.h>
#include <climits>/*#define宏定义分为无参数宏定义和有参数宏定义两种*/
#define A 2       //无参数宏定义,属于简单的字符串替换,格式为 “#define 标识符   字符串”/*有参数宏定义常用来做简单的代数替换
**有参数宏定义最值得讲的便是它的几种宏参数特殊操作符#, ##, #@
**字符串化操作符#,符号连接操作符##,字符化操作符#@
**使用示例:#define makechar(x)    #@x
**        a = makechar(b);
**             ------->  a='b'
*/#define STRCPY(a,b) strcpy(a##_p, #b) //把第一参数后边加上字符_p,把第二个参数变成字符串//转换宏,类Lisp宏编译功能,多一层转换宏,这样宏定义就不再是仅仅的字符替换了
//转换宏是指将所有的宏参数在这一层全部展开
#define _STR(s)  #s //将参数s按照符号名解析成字符串,但并不求值
#define STR(s) _STR(s) //转换宏,两层宏定义,便可以对参数进行求值了
//STR(INT_MAX) ->  _STR(0x7fff ffff) -> 0x7fffffff#define _Conju(a, b) a##*##b
#define Conju(a,b) _Conju(a,b) //转换宏,这样科学计数法便被求值了,而不是停留在字符串上
//Conju(A,A) -> _Conju((2), (2)) -> int(2e2)int main()
{char var1_p[20];char var2_p[30];strcpy(var1_p, "weapon");strcpy(var2_p, "tank");STRCPY(var1, var2); //等价strcpy(var1_p, "var2");STRCPY(var2, var1); //等价strcpy(var2_p, "var1");printf("%s\n", var1_p);//输出var2printf("%s\n", var2_p);//输出var1printf("%d\n", Conju(A, A)); //输出4printf("int max: %s\n", STR(INT_MAX));//INT_MAX定义在climits中,输出2147483647 ~ 2Greturn 0;
}

2. #define配合条件编译完成的健壮性程序实践

1. 提高程序平台兼容性和可移植性
在很多硬件系统中都有自己的自定义数据类型,比如Windows32位操作系统下的 int概念可能和16位低位机的字长完全不一样,而编译器默认的数据类型是 word dword long这一类按照字节数严格划分的修饰符,而随着当前64位系统和32位系统混用的现状,也很容易出现数据类型不匹配导致的移植性问题。所以为了提升程序的可移植性,很可能出现如下的的操作

#ifdef _WIN64typedef  __int64  LONG_PTR; 
#elsetypedef  long  LONG_PTR;
#endif

事实上这种通过条件编译来约束数据类型的操作是很常见的

#ifdef WIN32typedef unsigned char boolean;       /* Boolean value type. */typedef unsigned long int uint32;    /* Unsigned 32 bit value */typedef unsigned short uint16;       /* Unsigned 16 bit value */typedef unsigned char uint8;         /* Unsigned 8 bit value */typedef signed long int int32;       /* Signed 32 bit value */typedef signed short int16;          /* Signed 16 bit value */typedef signed char int8;            /* Signed 8 bit value */
#endif

2. 开关调试信息

#ifdef DEBUGprintf("程序中的DEBUG错误信息函数全部启动\n");
#endif

3. 防止头文件被重复编译加载

#ifndef _LIST_H_
#define _LIST_H_
//不管头文件会不会被多个文件引用,都要加上条件编译开关来避免重复包含。 #ifdef __cplusplus
extern "C" {
#endifpList List_new( void );void List_free( pList oList );int List_getLength( pList oList );int List_put( pList oList, const char* pcKey, const void* pvValue );...
...
#endif

3. #pragma指令

#pragma作为编译器开关指令,其实是最为复杂的预编译指令,但是目前随着IDE和编译器功能的发展,已经很少再用 #pragma指令了,一般出现在嵌入式开发的情况较多,毕竟要精打细算。这里主要说三个较为常见的 #pragma使用方法,其余地操作请参考编译器的手册。

1 . #pragma message (xxx)
用于在编译窗口中输出信息文本,和printf()函数功能差不多,但是对于源代码信息的控制还是蛮重要的

#ifdef WIN32
#pragma message("WIN32 --macro activated!")
#endif

2 . #pragma once
只要在头文件最开始加入这条指令,就可以保证头文件被编译一次,也是最常用的pragma指令。

3 . #pragma comment(lib, “libname.lib”)
表示在工程文件添加.lib库文件,这样就不用再ld链接命令中再次添加要使用的库了。

$ld  -static -e mini_crt_entry minicrt.a -o test

只需要在可执行文件中添加如下指令即可

#pragma  comment(lib, "minicrt.lib")

其余的pragma指令使用方式可以参考这里。

这篇关于杂货边角(5):预编译指令#家族define/pragma的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/198893

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

工作常用指令与快捷键

Git提交代码 git fetch  git add .  git commit -m “desc”  git pull  git push Git查看当前分支 git symbolic-ref --short -q HEAD Git创建新的分支并切换 git checkout -b XXXXXXXXXXXXXX git push origin XXXXXXXXXXXXXX

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

Android中如何实现adb向应用发送特定指令并接收返回

1 ADB发送命令给应用 1.1 发送自定义广播给系统或应用 adb shell am broadcast 是 Android Debug Bridge (ADB) 中用于向 Android 系统发送广播的命令。通过这个命令,开发者可以发送自定义广播给系统或应用,触发应用中的广播接收器(BroadcastReceiver)。广播机制是 Android 的一种组件通信方式,应用可以监听广播来执行

Golang test编译使用

创建文件my_test.go package testsimport "testing"func TestMy(t *testing.T) {t.Log("TestMy")} 通常用法: $ go test -v -run TestMy my_test.go=== RUN TestMyTestMy: my_test.go:6: TestMy--- PASS: TestMy (0.

mysql中导入txt文件数据的操作指令

1 表tt的格式:    CREATE TABLE `tt` (   `ind` int NOT NULL auto_increment,   `name` char(100) default NULL,   PRIMARY KEY  (`ind`)  )   2 文件d.txt的内容示例:  1,a  2,b  3,c

可选择的反思指令微调

论文:https://arxiv.org/pdf/2402.10110代码:GitHub - tianyi-lab/Reflection_Tuning: [ACL'24] Selective Reflection-Tuning: Student-Selected Data Recycling for LLM Instruction-Tuning机构:马里兰大学, Adobe Research领

C++/《C/C++程序编译流程》

程序的基本流程如图:   1.预处理        预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理将所有的“#define”删除,并且展开所有的宏定义处理所有的条件编译指令,如:“#if”、“

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte