预编译命令大全
14.4 条件编译指令
• 条件编译指令用于对程序源代码的各部分有选择 地进行编译。采用条件汇编,可以提高程序的适 用性,缩小目标代码的大小。
• 在默认情况下,源程序中的所有行都要进行编译。 但是有时需要某些语句行在条件满足的情况下, 才进行编译,此时便用到条件编译指令。目前商 业软件公司广泛应用条件编译来制作某个程序的 许多不同用户版本。
14.4.3 #ifdef、#ifndef命令
• #ifdef与#ifndef命令用于判断宏名是否被定义, 并根据判断的情况进行条件编译。#ifdef命令的 一般形式是:
• #ifdef 宏名 • 语句段; • #endif • 其执行过程是,如果宏名在前面#define语句中已
定义过,则后面的语句段将被编译。#ifndef的一 般形式是: • #ifndef宏名 • 语句段; • #endif
• 启动代码文件 STARTUP.A51的主要功能包括如下几个方面: • 定义内部RAM大小、外部RAM大小、可重入堆栈位置。 • 清除内部、外部或者以此页为单元的外部存储器。 • 按存储模式初使化重入堆栈及堆栈指针。 • 初始化8051硬件堆栈指针。 • 向main()函数交权。
14.7.2 C51启动代码分析
第14章 预处理及用户配置文件
• 预处理命令通常在程序编译时进行一些符号处 理,其并不执行具体的硬件操作。C51语言中的预 处理命令主要有宏定义指令、文件包含指令和条 件编译指令,还有其他一些调试时使用的指令。 本章将详细介绍各种预处理命令以及C51的用户配 置文件,并结合一定的程序实例以加深理解。本 章包括:
表; • WARNINGLEVEL(WL):选择“警告”级别。
14.7 C51的用户配置文件
• C51的用户配置文件包括启动代码文件、变量初始 化文件、基本I/O函数文件、分组配置文件,这些 文件都是以源程序的形式存放在Keil\C51\LIB文 件夹中。用户根据需要适当修改配置文件以满足 不同的硬件环境需要。
14.5 其他编译指令
• 除了以上几种预处理指令外,在C51语言中还定义 了其他几种编译预处理指令,主要用于编译和调 试程序等。下面分别进行讲解。
14.5.1 #line命令
• #line命令用于改变_LINE_与_FILE_的内容。其中 _LINE_和_FILE_是在编译程序中预先定义的标识 符,分别表示行号和源文件。#line命令使用的一 般形式如下:
14.7.1 C51启动代码文件
• C51启动代码文件用于在主程序进入main()函数之前,完成 对8051单片机片内外RAM清零、开设常规堆栈以及再入函数 堆栈、设置堆栈指针等任务。Keil μVision3编译器针对 不同类型的8051单片机提供了多种启动代码配置文件,其 中最常用的启动代码文件是 STARTUP.A51,而其他的启动 代码都和其基本类似。
14.2.1 #define命令
• #define命令用于定义一个“宏名”。其中“宏名”是一个
标识符,在源程序中遇到该标识符时,均以定义的串的内
容替代该标识符。ANSI标准将标识符定义为“宏名”,这
个 替 换 过 程 称 为 “ 宏 替 换 ” 。 #define 命 令 用 于 定 义 宏 名
时,可以带参数,也可以不带参数,下面分别介绍这两种
//用于条件编译 • #ifdef
//用于条件编译 • #ifndef
//用于条件编译 • #undef
//用于宏定义 • #line
//用于更改行号 • #pragma
//用于传送控制指令
14.2 宏定义指令
• 宏定义指令是用一些标识符作为宏名来代替一些 符号或者常量的命令。宏定义指令可以带参数, 也可以不带参数。下面分别介绍用于宏定义的一 些预处理指令。
• #pragma指令后面的字符串,可以大写,也可以小 写。示例如下:
• #pragma sfr //在C51中使用SFR
• #pragma access //使用绝对地址
• #pragma asm //在C51中插入汇编语句
14.6 C51编译器的控制指令
• C51编译器的控制指令分为三类:源文件控制类、 目标文件控制类及列表控制类。这些指令可能需 要具体的编译器支持,因此这里仅进行简单的说 明,用户可以参考具体的编译器。对于Keil μVision3集成开发环境,可以在项目选项中进行 设置。下面分别介绍各类中常用的控制指令。
14.4.1 #if、#else、#endif命令
• #if、#else、#endif指令用于条件编译的一般形 式如下:
• #if 常数表达式 • 语句段; • #else • 语句段; • #endif • 其中,#if、#else、#endif为条件编译指令,常数
表达式为判断的条件,语句段为条件编译部分。 执行过程为,如果常量表达式为真,则编译其后 面的语句段;如果常量表达式为假,则编译#else 后面的语句段;#endif命令是一个条件编译的结束。
• 宏定义指令 • 文件包含指令 • 条件编译指令 • C51编译器的控制指令 • C51的用户配置文件
14.1 预处理命令概述
• C51语言中提供了各种预处理命令,类似于汇编程序中的伪指令。一般来说,在对源程序进行编译前,C51编译器需要 先对程序中的预处理命令进行处理,然后将预处理的结果与源程序一并进行编译,最后产生目标代码。通过这些预处 理命令,在很大程度上为C51提供功能和符号等方面的扩展,使用预处理命令也可以提高程序的可读性。
14.7.3 变量初始化文件
• 变量初始化文件用于对源程序中声明的变量进行 初始化赋值。在Keil μVision3编译环境中,主 要的变量初始化文件为INIT.A51,此外还提供了 INIT_TNY.A51文件用于不使用外部XDATA存储器的 实时系统。
名局限在仅需要的代码段中。示例如下: • #include <stdio.h>
//头文件 • #define COUNT 25
//宏定义
• void main() //主函数
•{ • printf("COUNT =%d\n", COUNT);
//输出COUNT =10 • #undef COUNT
//撤销宏定义 • //printf("COUNT =%d\n", COUNT);
• 2.带参数的宏定义
• 带参数的宏定义指令,其一般形式如下:
• #define 宏名(参数表) 字符串
14.2.2 #undef命令
• #undef命令用于取消前面已定义过的宏名。一般形式为: • #undef 宏名 • 其中,宏名为前面用#define定义过的标识符。使用#undef的目的是将宏
• C51编译器在对用户创建的项目进行编译连接时, 会自动将上述代码添加到用户程序中去。需要修 改配置文件时,可以通过Keil μVision3的项目 窗口,将需要修改的配置文件添加到自己的文件 组中,并在编辑窗口进行修改,然后进行总体编 译连接,这样就可以将修改后的配置文件代码连 接到自己的程序代码中。
用; • REGISTERBANK(RB):指定一个供绝对寄存器访问的寄存器
区名; • SRC:不生成目标文件只生成汇编源文件。
14.6.3 列表文件(Listing)控制类
• 比较常用的有如下几个,详细内容需要参加所使 用的编译器。
• CODE(CD):向列表文件加入汇编列表; • LISTINCLUDE(LC):显示include文件; • SYMBOLS(SB):列表文件包括模块内所有符号的列
• 前面介绍了C51的启动代码,下面详细介绍上面的 启动代码文件中常用的一些EQU语句的功能。
• 1.IDATALEN • 2.XDATASTART、XDATALEN • 3.PDATASTART、PDATALEN • 4.IBPSTACK、IBPSTACKTOP • 5.XBPSTACK、XBPSTACKTOP • 6.PBPSTACK、PBPSTACKTOP • 7.PPAGEENABLE、PPAGE
• #line 数字["文件名"] • 其中的数字为任意正整数,可选的文件名为任意
有效文件标识符。行号为源程序中当前行号,文 件名为源文件的名字。命令#line主要用于调试及 其他特殊应用。
14.5.2 #error
பைடு நூலகம்
• #error命令用于强迫编译程序停止编译,主要用于程序调试。其使用的一般形式如下:
// 如 果
宏GREED存在,则执行该处
• #else
•
printf("GREED is defined!\n");
//如果宏GREED不存在,则执行该语句
• #endif
•}
14.5.3 #pragma
• #pragma命令用于向编译程序传送各种C51编译器 的控制指令。根据#pragma指令后面的字符串,编 译系统将按照特定的方式来翻译C51的字符串和函 数。
//此时再引用是错误的 •}
14.3 文件包含指令
• 文件包含指令#include通常在C51程序的开头,将 另外一文件的内容引入当前文件。其中被包含的 文件通常是头文件、宏定义等,利用文件包含指 令可以有助于更好地调试文件。其一般形式如下:
• #include "头文件.h" • #include <头文件.h> • #include 宏定义标识符
• #error “message”
• 其中,message为错误消息。这里举例讲解#error命令在程序设计中的应用,示例如下:
• #include <stdio.h>
//头文件
• #define SCORE 85
//宏定义
•
• void main()
gcc 预编译指令
gcc 预编译指令
gcc预编译指令是指在编译源代码之前,对代码进行预处理的指令。
这些指令可以用来包含头文件、定义常量、宏和条件编译等。
预处理器会根据这些指令将代码转换为更适合编译器处理的形式。
常见的预编译指令有:
- #include:用于包含头文件。
- #define:用于定义常量和宏。
- #ifdef、#ifndef、#endif:用于条件编译,根据是否已定义某个宏来判断是否编译某段代码。
- #pragma:用于提供编译器特定的指令,例如优化选项等。
使用预编译指令可以提高代码的可读性和可维护性,同时也可以避免一些常见的错误。
但是,过度使用预编译指令也可能导致代码难以理解和调试。
因此,在使用预编译指令时,需要谨慎考虑其影响,并遵循良好的编码习惯。
- 1 -。
C语言第7章预编译命令
C语言程序设计教程
7.2带参宏定义
如果宏定义串中a 周围的括号被去掉,即: #define ABS(a) a<0?-a:a
则表达式: ABS( lO - 20) 将转换为: 1O – 20 < 0 ? -10 - 20 : 10 – 20
所以须特别注意宏定义中参数的括号问题!!
int a = 1308001,c = 1308002;
float b = 621,d = 612;
Print(Format,"Wangyi",a,b);
Print(Format,"Liwei",c,d);
return 0; 程序运行结果:
}
输出: Wangyi 1308001 621.00 14
Liwei 1308002 612.00
对语句:
printf("%f ",1T+H1R+E1E));;
12
C语言程序设计教程
7.1.2无参宏定义
如果串长于一行,可在行尾用反斜线“\”续行, 如:
#define LONG_STRING "this is a very long\ string that is used as an example"
习惯上宏名用大写字母表示,以便于与变量区 别。但也允许用小写字母。
13
C语言程序设计教程
7.1.2无参宏定义
例7.2 使用宏定义实现格式控制输出。
#include <stdio.h>
#define Print printf
#define Format "%10s%8d%8.2f\n"
预编译指令与宏定义
预编译指令与宏定义#if #elif [defined(), !defined()] #else#ifdef#ifndef #endif// 条件编译/* 头⽂件防⽌多次被包含 */#ifndef ZLIB_H#define ZLIB_H#endif /* ZLIB_H *//* ⽤C⽅式来修饰函数与变量 */#ifdef __cplusplusextern"C" {#endifint add(int a, int b);int g_nVal = 0;#ifdef __cplusplus}#endif/* 条件嵌套 */#ifdef __STDC_VERSION__# ifndef STDC# define STDC# endif# if __STDC_VERSION__ >= 199901L# endif# if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))# define STDC# endif#endif/* ⽇志输出 */#if defined( DEBUG ) && defined( _MSC_VER )# include <windows.h># define PRINT_LOG OutputDebugString#else# define PRINT_LOG printf#endif/* MFC中_AfxCopyString的实现*/inline LPOLESTR AFXAPI _AfxCopyString(LPCTSTR psz){if (psz == NULL)return NULL;int cch = lstrlen(psz) + 1;LPOLESTR pszCopy = NULL;if ((pszCopy = (LPOLESTR)CoTaskMemAlloc(cch * sizeof(OLECHAR))) != NULL){#ifdef _UNICODEwcscpy(pszCopy, psz);#elif !defined(OLE2ANSI)MultiByteToWideChar(CP_ACP, 0, psz, -1, pszCopy, cch);#elselstrcpy(pszCopy, psz);#endif}return pszCopy;}#define #undef // 宏定义、宏取消#define FAR#define _DEBUG#define MAX_PATH 260#define NULL ((void *)0)#define PASCAL __stdcall#define CALLBACK FAR PASCAL#define DEBUG_NEW new#define new DEBUG_NEW#define _PUC unsigned char *#define _CPUC const unsigned char *#define _PC char *#define _CPC const char *#define _UI unsigned int#define MAX(a,b) (((a) > (b)) ? (a) : (b))#define TRACE ::AfxTrace#define TRACE0(sz) ::AfxTrace(_T("%s"), _T(sz))#define TRACE1(sz, p1) ::AfxTrace(_T(sz), p1)#define TRACE2(sz, p1, p2) ::AfxTrace(_T(sz), p1, p2)#define TRACE3(sz, p1, p2, p3) ::AfxTrace(_T(sz), p1, p2, p3)// 重新定义FALSE、TRUE、NULL#undef FALSE#undef TRUE#undef NULL#define FALSE 0#define TRUE 1#define NULL 0/* TCHAR.H */#ifdef _UNICODEtypedef wchar_t TCHAR;#define __T(x) L ## x#define _tmain wmain#define _tprintf wprintf#elsetypedef char TCHAR;#define __T(x) x#define _tmain main#define _tprintf printf#endif#define _TEXT(x) __T(x)#define _T(x) __T(x)/* 使⽤OF宏来适配函数参数 */#ifndef OF# ifdef STDC# define OF(args) args# else# define OF(args) ()# endif#endifint add OF((int a, int b));/* 宏必须在⼀⾏写完,多⾏写时必须带上 \⾏连接符 *//* 注意##字符拼接符 */#define DECLARE_DYNAMIC(class_name) \public: \static const AFX_DATA CRuntimeClass class##class_name; \virtual CRuntimeClass* GetRuntimeClass() const;/* Debug时可使⽤THIS_FILE变量获得当前⽂件名*/#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif####@// 特殊符号/* ## 字符拼接符 */#define Conn(x,y) x ## yint n = Conn(123,456); //n = 123456;char* str = Conn("abc","def"); //str = "abcdef";/* # 加双引号 */#define ToString(x) #x#define PrintAge(age) printf("I am " #age " years old.\n") // 注:字符串是可以⽤空格分开写的,编译器会将其合成⼀个字符串;如:printf("Hello " "World" "!")等价于printf("Hello World!") char* str = ToString(12.345); //str= "12.345"PrintAge(20); // 输出 I am 20 years old./* #@ 加单引号 */#define ToChar(x) #@xchar ch = ToChar(a); //ch='a'#include #include_next【GNU的⼀个扩展,gcc下可使⽤】// ⽂件包含#include <stdio.h> // 只搜索系统⽬录#include "stdafx.h"// 先搜索⼯程当前⽬录,再搜索系统⽬录#include "../zlib/zconf.h"// 以当前⽂件作为参考来计算// #include_next为GNU的⼀个扩展,gcc下可使⽤// #include_next <xx.h> 包含在搜索路径列表中第2次找到的xx.h// 例如:搜索路径顺序为:/usr/local/include,/usr/include// 如果这两个⽬录下都有signal.h⽂件,将引⽤/usr/include中的那个#include_next <signal.h>// 另外#include_next不区分""与<>,都是先搜索⼯程当前⽬录,再搜索系统⽬录#line指⽰下⼀⾏的⾏号,及当前所在的⽂件;该命令会修改__FILE__、__LINE__的值该命令是提供给编译器使⽤的,程序员最好不要使⽤该命令,否则会导致调试异常(如:断点打不上等)#line 100 "myFile.cpp"printf("File: %s, Line: %d, Function: %s\n", __FILE__, __LINE__ ,__FUNCTION__);// 输出File: f:\vstest\vstest\myfile.cpp, Line: 101, Function: wmain#pragma#pragma once // 防⽌头⽂件重复包含【vc编译器版本:_MSC_VER > 1000时才⽀持#pragma once】#pragma message ("发布版本请去掉测试代码") // 编译时打印⾃定义信息#pragma warning(disable: 4102 4235) // 不提⽰C4102、C4235的warning#pragma warning(once:4385) // C4385警告仅报告⼀次#pragma warning(error:5038) // C5038警告信息视为错误强制保证:Initialization order(成员变量的初始化顺序要与它们的声明顺序⼀致)#pragma warning(default: 4310) // 将C4310的警告⾏为到默认状态#pragma warning(disable:4507 34; once:4385; error:164)#pragma warning(push) //保存现有警告状态#pragma warning(push, 3) //保存现有警告状态,并且把warning level设为3#pragma warning(pop) //弹出栈顶的⼀个警告信息,这对导致push和pop之间所作的⼀切改动取消掉#pragma comment(lib, "../zlib/zlib.lib") // 链⼊zlib.lib导出库// ⽬录结构如下:+-- testStdafx.h|- Debug+-- testLib.lib#pragma comment(lib, __FILE__ "\\..\\" "Debug\\" "testLib.lib") // 若当前⽂件为testStdafx.h,在testStdafx.h中包含⾃⼰的lib⽂件,可以避免外部⼯程⼿动包含test.lib// 外部⼯程只需要#include "testStdafx.h"即可,如果多个⼯程使⽤testLib.lib,这个做法⾮常⽅便// 后⾯路径分段个数没有限制,理论上任意多个都⾏/*#pragma data_seg指定函数存放在*.obj⽂件中的数据段,默认的代码段是.data*/int i = 0; // 存放在"data"段#pragma data_seg(".my_data1")int j = 1; // 存放在"my_data1"段#pragma data_seg(push, stack1, ".my_data2")int k = 2; // 存放在"my_data2"段#pragma data_seg(pop, stack1) // pop stack1 off the stackint m = 3; // 存放在"my_data1"段#pragma data_seg()int n = 4; // 存放在"data"段/*#pragma code_seg指定函数存放在*.obj⽂件中的代码段,默认的代码段是.text*/void func1() {} // 存放在text段#pragma code_seg(".my_data1")void func2() {} // 存放在my_data1段#pragma code_seg(push, r1, ".my_data2")void func3() {} // 存放在my_data2段#pragma code_seg(pop, r1)void func4() {} // 存放在my_data1段#pragma code_seg()void func5() {} // 存放在text段/* 除了data_seg和code_seg之外,还有bss_seg与const_seg *//* bss_seg与const_seg的使⽤与data_seg、code_seg⼀致*///shareddata ⼀般⽤于dll中;共享数据必须初始化,否则会放到.BSS段中#pragma data_seg("shareddata")int nTotalNum = 0; //nTotalNum可以被多个进程共享#pragma data_seg()/* #pragma comment(linker,...)主要⽤来设置链接参数 *//* 常见的链接参数详见: *//*/kekec/archive/2013/04/21/3007277.html中的Link.exe段的说明 */// 将flag_data数据段设置为可读、可写、可共享#pragma comment(linker,"/SECTION:flag_data,RWS")// 导出extern "C" fnSub函数#pragma comment (linker, "/EXPORT:_fnSub")// 导出extern "C" fnAdd函数,并将符号名修改为myfnAdd,同时将导出序号设为1#pragma comment (linker, "/EXPORT:myfnAdd=_fnAdd,@1")// 强制包含名为__mySymbol的符号;// 若要指定多个符号,请在符号名称之间键⼊逗号、分号或空格#pragma comment(linker, "/include:__mySymbol")/* 区别:源码的inline在编译器进⾏预处理的时候就展开了,*//* ⽽#Pragma intrinsic却是以⼆进制的⽅式inline进去的 */// 使⽤内联版本的memset及strlen#pragma intrinsic(memset, strlen)/* 使⽤回函数调⽤的strlen */#pragma function(strlen)/* 对于不同⽂件中的全局对象、变量,它们的构造函数调⽤顺序是未定义的,取决于具体的编译器,这就可能带来⼀些问题 *//* compiler、lib、user,初始化优先次序依次降低,但都先于普通的全局变量构造*//* 对于必须优先被初始化的对象或变量应使⽤这3个指令 *//* ⼀个源⽂件只能出现⼀次init_seg 指令 */#pragma init_seg(compiler)#pragma init_seg(lib)#pragma init_seg(user)#pragma init_seg("user_defined_segment_name") // ⽤户⾃定义名称// 详见:/kekec/archive/2012/10/31/2748955.html#pragma pack(push, 4)#pragma pack(pop)#pragma optimize("", off) // 关闭代码编译优化#pragma optimize("", on) // 开启代码编译优化#pragma inline_recursion(on) // 开启内联递归#pragma inline_recursion(off) // 关闭内联递归// 函数堆栈深度超过指定深度,就不进⾏函数内联#pragma inline_depth(0) // 不进⾏任何内联#pragma inline_depth(10) // 深度超过10层,不进⾏函数内联#pragma inline_depth() // 缺省深度值为254// myheader.h关闭最⼩重新⽣成【见/Gm编译选项】/* myheader.h起始处 */#pragma component(minrebuild, off)/* myheader.h */#pragma component(minrebuild, on)/* myheader.h结束处 */#error// c编译器error命令#ifndef __cplusplus#error MFC requires C++ compilation (use a .cpp suffix)#endifextern"C"{#error "printf error!"};__VA_ARGS__【变参】新的C99规范中增加的,gcc及vs2005版本以上的ms编译器⽀持该宏/* vs2008测试 */#define debug1(...) printf(__VA_ARGS__)#define debug2(fmt, ...) printf(fmt, __VA_ARGS__)#define debug3(fmt, ...) printf(fmt, ## __VA_ARGS__)int a = 10;const char* chs = "Hello";debug1("%d %s\n", a, chs); // printf("%d %s\n", a, chs);debug2("%d %s\n", a, chs); // printf("%d %s\n", a, chs);debug3("%d %s\n", a, chs); // printf("%d %s\n",a, chs); a前会少⼀个空格/* 安全调⽤对象的⽅法 */#define SafeCallFunc(ptr, retType, func, ...) ( (ptr!=NULL)?(ptr->func(__VA_ARGS__)):(retType(0)) )/* 安全获取对象的成员变量 */#define SafeGetValue(ptr, val, valType) ( (ptr!=NULL)?(ptr->val):(valType(0)) )注:__VA_ARGS__前加上##的作⽤在于,当可变参数的个数为0时,将##前⾯多余的逗号去掉,否则会编译不过(vs不加##,也可以编译过)__FILE__、__LINE__、__FUNCTION__、__DATE__、__TIME__、__TIMESTAMP__// File: f:\vsconsole1\vsconsole1\vsconsole1.cpp, Line: 9, Function: wmain// vc6不⽀持__FUNCTION__宏printf("File: %s, Line: %d, Function: %s\n",__FILE__,__LINE__,__FUNCTION__);// Date: Sep 19 2013 Time: 22:38:51 TimeStamp: Thu Sep 19 22:38:50 2013printf("Date: %s Time: %s TimeStamp: %s\n",__DATE__,__TIME__, __TIMESTAMP__);注:__DATE__、__TIME__、__TIMESTAMP__会被替换成该源⽂件编译成obj⽂件时的⽇期、时间、和unix时间戳__STDC__、__STDC_VERSION__、__cplusplus__STDC__ // 当前编译器符合c标准,则该宏的值为1__STDC_VERSION__ // 当前编译器符合C89, 该宏的值为199409L, 符合C99, 该宏的值为199901L__cplusplus // 当前编译器符合c++标准,则该宏值为编译器版本号注:vs2017及以后的版本才⽀持,需要加/Zc:__cplusplus编译选项,否则该宏始终为199711L// C++ pre-C++98: __cplusplus is 1// C++98: __cplusplus is 199711L// C++98 + TR1: This reads as C++98 and there is no way to check that I know of// C++11: __cplusplus is 201103L// C++14: __cplusplus is 201402L// C++17: __cplusplus is 201703L注:ue4.26在windows平台缺省使⽤c++14进⾏编译_MSC_VER_MSC_VER = 1928// MS VC++ 16.0 Visual Studio 2019_MSC_VER = 1910// MS VC++ 15.0 Visual Studio 2017_MSC_VER = 1900// MS VC++ 14.0 Visual Studio 2015_MSC_VER = 1800// MS VC++ 12.0 Visual Studio 2013_MSC_VER = 1700// MS VC++ 11.0 Visual Studio 2012_MSC_VER = 1600// MS VC++ 10.0 Visual Studio 2010_MSC_VER = 1500// MS VC++ 9.0 Visual Studio 2008_MSC_VER = 1400// MS VC++ 8.0 Visual Studio 2005_MSC_VER = 1310// MS VC++ 7.1 Visual Studio .NET 2003 _MSC_VER = 1300// MS VC++ 7.0 Visual Studio .NET (2002) _MSC_VER = 1200// MS VC++ 6.0 Visual Studio 6.0_MSC_VER = 1100// MS VC++ 5.0 Visual Studio 97Product name Codename InternalversionSupported .NETFramework versionsRelease dateVisual Studio N/A 4.0N/A April 1995Visual Studio 97Boston 5.0N/A February 1997Visual Studio 6.0Aspen 6.0N/A June 1998Visual Studio .NET (2002)Rainier7.0 1.0February 13, 2002Visual Studio .NET 2003Everett7.1 1.1April 24, 2003Visual Studio 2005Whidbey8.0 2.0, 3.0November 7, 2005Visual Studio 2008Orcas9.0 2.0, 3.0, 3.5November 19, 2007Visual Studio 2010Dev10/Rosario10.0 2.0, 3.0, 3.5, 4.0April 12, 2010Visual Studio 2012Dev1111.0 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2September 12, 2012Visual Studio 2013Dev1212.0 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2October 17, 2013Visual Studio 2015Dev1414.0 2.0 - 4.6.12015-07-20Visual Studio 2017Dev1515.0 3.5 - 4.7.22017-03-07Visual Studio 2019Dev1616.0 3.5 - 4.82019-04-02WINVERwin32具有良好的向下兼容性,低版本的win32程序可以在后续⾼版本的windows系统上正确运⾏随着windows版本的升级,更多Windows API被加⼊,为了能在程序中使⽤这些新增的API函数,可以将WINVER宏设定到更⾼的版本,保证程序能编译链接到windows SDK中的这些API;但与此同时,也带来了新的问题,意味着你的程序只能在更⾼版本的windows上运⾏Name ReleaseversionWINVER Architecture NT 4.0 0x0400, ,,NT 5.0 0x0500,NT 5.1; NT 5.2(2003 and x64)0x05010x0502, ,NT 5.2 0x0502, ,NT 6.0 0x0600,NT 6.0 0x0600, ,NT 6.1 0x0601,NT 6.2 0x0602, , (ARMv7) NT 6.3 0x0603, , (ARMv7) NT 10.0 0x0A00编译时打印出宏的内容//测试宏#define PI 3.1415926#define MAX(a,b) (a)>(b) ? (a) :(b)//⾸先定义两个辅助宏#define PRINT_MACRO_HELPER(x) #x#define PRINT_MACRO(x) #x"="PRINT_MACRO_HELPER(x)//编译时打印出宏的内容#pragma message(PRINT_MACRO(PI)) // PI=3.1415926#pragma message(PRINT_MACRO(PI2)) // PI2=PI2 注:PI2没定义#pragma message(PRINT_MACRO(MAX(a,b)))// MAX(a,b)=(a)>(b) ? (a) :(b)#pragma message(PRINT_MACRO(MAX(x,y))) // MAX(x,y)=(x)>(y) ? (x) :(y)宏展开输出到⽂件在vs2008中,设置项⽬Property -- C/C++ -- Preprocessor,输出宏展开后的⽂件(后缀名为.i)1. 按F7编译项⽬会输出所有c/cpp⽂件的.i⽂件2. 设置后,只会输出.i⽂件,不会输出obj中间⽂件,所以在Link时会报找不到obj⽂件的错误:INK : fatal error LNK1104: cannot open file '.\Debug\MacroTest.obj'3. 也可以只编译⽣成单个c/cpp⽂件的.i⽂件MacroHeader.h的内容如下:1 #ifndef MACRO_HEADER_H2#define MACRO_HEADER_H34#define Conn(x,y) x ## y56#define Add(a,b) ((a)+(b))78int Func()9 {10char* szTS = __TIMESTAMP__;11return100;12 }1314#endifMacroTest.cpp的内容如下:1// MacroTest.cpp : Defines the entry point for the console application.2//34 #include "MacroHeader.h"56int main(int argc, char* argv[])7 {8int n = Conn(123,456); //n = 123456;9char* str = Conn("abc","def"); //str = "abcdef";1011int sum = Add(1,2);121314char* szFile = __FILE__;15int line = __LINE__;1617return0;18 }① Generate Preprocessed File设置为Without Line Numbers (/EP /P)时,宏展开的MacroTest.i⽂件内容如下:12345678910111213int Func()14 {15char* szTS = "Wed Oct 14 14:34:46 2020";16return100;17 }1819202122int main(int argc, char* argv[])23 {24int n = 123456;25char* str = "abc""def";2627int sum = ((1)+(2));282930char* szFile = "g:\\vstest\\macrotest\\macrotest\\macrotest.cpp";31int line = 15;3233return0;34 }② Generate Preprocessed File设置为With Line Numbers (/P)时,宏展开的MacroTest.i⽂件内容如下:1#line 1 "g:\\vstest\\macrotest\\macrotest\\macrotest.cpp"2345#line 1 "g:\\vstest\\macrotest\\macrotest\\macroheader.h"678910111213int Func()14 {15char* szTS = "Wed Oct 14 14:34:46 2020";16return100;17 }1819#line 15 "g:\\vstest\\macrotest\\macrotest\\macroheader.h"20#line 5 "g:\\vstest\\macrotest\\macrotest\\macrotest.cpp"2122int main(int argc, char* argv[])23 {24int n = 123456;25char* str = "abc""def";2627int sum = ((1)+(2));282930char* szFile = "g:\\vstest\\macrotest\\macrotest\\macrotest.cpp";31int line = 15;3233return0;34 }使⽤vs的IntelliSense(智能感知)的Auto Quick Info功能来实时查看宏展开的内容(需要vs2017及以上版本)。
c51的预编译
Keic C51中几个预处理指令【转】2008-11-15 19:42Keic C51中几个预处理指令1.#message用法:#message "message"用途:当编译器编译到#message处会在编译输出栏中输出message消息,这时程序员可以知道编译器已经并且编译了该处,可以用来检查程序是否有编译了此处,或者在此设置一些消息告知程序员。
此条预编译指令相当于C中的#pragma message("消息文本") ,此预编译命令在Keil Compiler也可以支持,效果与#message例如:#ifdef DEBUG_ENABLE#message "IN DEBUG MODE"//可以用来告知程序员程序处于DEBUG模式下#endif这样的message会在uVision的Output Window上显示,而无论有多少个message 输出,编译输出栏都不会有warning或error。
2.#warning用法:#warning "message"用途:与#message类似,区别是当遇到这样的预编译命令会抛出一个警告,若有多个警告则编译输出栏中的warning个数累加。
3.#error用法:#error "message"用途:程序员会在source code中的重要部分加上这个预编译指令,因为编译到了这个地方程序可能会有问题而编译器却无法发现,这时这条预编译指令就产生了作用。
碰到此指令会抛出一个错误,程序将无法通过编译。
以上三条指令用法类似,只是编译的时候抛出的warning级别不一样,程序员可以自己根据严重程度自己设置,三条指令显示的消息可以不用加双引号"",这三条预编译指令只在Keil Compiler上有效。
4.#ifndef/#define/#endif组合作用:防止该头文件被重复引用。
编译预处理命令
编译
说明: 说明:
① 宏定义的作用域是从定义处开始到源文件结束, 宏定义的作用域是从定义处开始到源文件结束, 但根据需要可用undef命令终止其作用域。形式为: undef命令终止其作用域 但根据需要可用undef命令终止其作用域。形式为: #undef 宏名
②为了增加程序的可读性,建议宏名用大写字母,其 为了增加程序的可读性,建议宏名用大写字母,
带参数的宏定义
【例】分析下面程序运行后的输出结果。 分析下面程序运行后的输出结果。 #define MA(x) x*(x-1) 特别注意: 特别注意: 由于替换文本中的x 由于替换文本中的x main( ) 没有用括号括起, 没有用括号括起, { int a=1,b=2; 因此,1+a+b也不能 因此,1+a+b也不能 printf("%d\n", MA(1+a+b)); 用括号括起。 用括号括起。 } 程序输出结果:8 程序输出结果: 分两次替换: 分两次替换: ①MA(1+a+b) 用x*(x-1) 替换。 替换。 MA(1+a+b) x*(x1+a+b替换 替换x ②用1+a+b替换x。 printf语句被展开为 语句被展开为: printf语句被展开为: printf("%d\ 1+a+b*(1+a+bprintf("%d\n", 1+a+b*(1+a+b-1));
编译预处理命令
源文件 *.c
目标文件 编译编译 编译 编译 编译 连接 连接 *.obj
运行文件 *.exe
编译预处理
编译预处理包括: 编译预处理包括: 宏定义 编 译 文件包含 条件编译
预编译指令 - pcn
一、预处理的由来:在C++的历史发展中,有很多的语言特征(特别是语言的晦涩之处)来自于C语言,预处理就是其中的一个。
C++从C 语言那里把C语言预处理器继承过来(C语言预处理器,被Bjarne博士简称为Cpp,不知道是不是C Program Preprocessor 的简称)。
二、常见的预处理功能:预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有:文件包含,条件编译、布局控制和宏替换4种。
文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
布局控制:#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
宏替换: #define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
三、预处理指令:预处理指令的格式如下:# directive tokens#符号应该是这一行的第一个非空字符,一般我们把它放在起始位置。
如果指令一行放不下,可以通过\进行控制,例如:#define Error if(error) exit(1) 等价于#define Error \if(error) exit(1)不过我们为了美化起见,一般都不怎么这么用,更常见的方式如下:# ifdef __BORLANDC__if_true<(is_convertible<Value,named_template_param_base>::value)>::template then<make_named_arg, make_key_value>::type Make;# elseenum { is_named = is_named_parameter<Value>::value };typedef typename if_true<(is_named)>::templatethen<make_named_arg, make_key_value>::type Make;# endif下面我们看一下常见的预处理指令:#define 宏定义#undef 未定义宏#include 文本包含#ifdef 如果宏被定义就进行编译#ifndef 如果宏未被定义就进行编译#endif 结束编译块的控制#if 表达式非零就对代码进行编译#else 作为其他预处理的剩余选项进行编译#elif 这是一种#else和#if的组合选项#line 改变当前的行数和文件名称#error 输出一个错误信息#pragma 为编译程序提供非常规的控制流信息下面我们对这些预处理进行一一的说明,考虑到宏的重要性和繁琐性,我们把它放到最后讲。
预编译
Visual Studio 2005中:?要编译的工程上点击 右键 / 属性 / 配置属性 / C/C++ / 预编译头 右边的菜单中选择 “创建/使用预编译头”中的“不使用预编译头文件”即可。
VC6.0:要编译的工程上点击右键/Settings.../ C/C++ /Category中选则:precompiled Headers,选择 Not Using Precompliled Headers 或者 Automatic use of precompiled Headers
注意:
A,实际上,由Appzard项目向导生成的默认的头文件及CPP文件StdAfx.h和StdAfx.cpp可以是任何名字的.原因很简单。但如果你要这样做就要记得修改相应的Project->setting...下的几个预编译指(/Yc,/Yu,/Yx,/Fp)的参数。
B.在任何一个包括了将要预编译的头文件而使用了.PCH文件的工程文件的开头,一定必须要是在最开头,你要包含那个指定生成.PCH文件的.H文件(通过.CPP文件包括,默认为StdAfx.cpp),如果没包括将产生我最开头产生的错误.如果不是在最开头包括将产生让你意想不到的莫名其妙错误,如若不信,盍为试之?
预编译指令
图1. STM32F10xxx标准外设库体系结构先说这个问题,大家都知道,我们在选择使用哪些外围的的时候,是去更改从官方模版中拷贝过来的stm32f10x_conf.h文件的27-48行,把我们要用的外围的头文件包含进来,不用的外围的头文件注释掉,但是你是否发现我们根本没有在用户主程序文件中包含stm32f10x_conf.h 这个文件。
那我们的修改这个文件怎么进入主程序文件的呢?答案只有一个,它包含在stm32f10x.h这个文件中,因为在用户主文件中我们第一句就使用了#include "stm32f10x.h",所以应该是stm32f10x.h这个文件包含了stm32f10x_conf.h,间接的包含进了用户主程序文件中。
(其实大家去看看固件库的文件结构图就明白了确实是这样,我们推测是对的,参见下图1)图1. STM32F10xxx标准外设库体系结构2. 有了上面的认识我们打开stm32f10x_conf.h中搜索“stm32f10x_conf.h”,我们在8291-8293行看见这么几句代码:#ifdef USE_STDPERIPH_DRIVER#include "stm32f10x_conf.h"#endif大家看到问题所在了吧?这个条件编译就是说:如果定义了USE_ STDPERIPH_DRIVER标记,那么就包含stm32f10x_conf.h,没有定义就不包含。
那不用说了,我们想使用固件库,所以就应该预定义US E_STDPERIPH_DRIVER这个标记,KEIL提供了编译前预定义标记的办法,就是上面提到的在项目设置“option for targe”=>“C/C++”标签=>“define”处加USE_STDPERIPH_DRIVER。
如下图2所示:图2. Keil开发环境(C/C++预设置窗口)3. 大家肯定看见上面标记预设置区我还加入了另一个标记STM32F10 X_HD,是因为大家会发现49-58行:需要我们指定期间的型号#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_V L) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_ VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)/* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices *//* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM3 2 Low density Value Line devices *//* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medi um density devices *//* #define STM32F10X_MD_VL */ /*!< STM32F10X_MD_VL: STM 32 Medium density Value Line devices *//* #define STM32F10X_HD */ /*!< STM32F10X_HD: STM32 High density devices *//* #define STM32F10X_HD_VL */ /*!< STM32F10X_HD_VL: STM 32 High density value line devices *//* #define STM32F10X_XL */ /*!< STM32F10X_XL: STM32 XL-de nsity devices *//* #define STM32F10X_CL */ /*!< STM32F10X_CL: STM32 Conn ectivity line devices */#endif:请问一下, STM32的STM32F10X_MD_VL和STM32F10X_MD有什么区别呀?- startup_stm32f10x_ld_vl.s: for STM32 Low density Value line d evices- startup_stm32f10x_ld.s: for STM32 Low density devices- startup_stm32f10x_md_vl.s: for STM32 Medium density Value li ne devices- startup_stm32f10x_md.s: for STM32 Medium density devices- startup_stm32f10x_hd.s: for STM32 High density devices- startup_stm32f10x_xl.s: for STM32 XL density devices- startup_stm32f10x_cl.s: for STM32 Connectivity line devices cl:互联型产品,stm32f105/107系列vl:超值型产品,stm32f100系列xl:超高密度产品,stm32f101/103系列ld:低密度产品,FLASH小于64Kmd:中等密度产品,FLASH=64 or 128hd:高密度产品,FLASH大于128转载内容,具体根据自己的情况设置。
预编译指令endif
条件指示符的值为真,于是从#inndef 到#endif 之间的所有语句都被包含进来进行处理。相反,如果#inndef 指示符的值为假,则它与#endif 指示符之间的行将被忽略。条件指示符 #ifndef 的最主要目的是防止头文件的重复包含和编译。 如果#ifndef...#endif 在头文件中,千万不要忽略了头件的中的#ifndef,这是一个很关键的东 西。比如你有两个 C 文件,这两 个 C 文件都 include 了同一个头文件。而编译时,这两个 C 文件要一同编译成一个可运行文 件,于是问题来了,大量的声明冲突。
#ifndef x 先测试 x 是否被宏定义过 程序段 1 如果 x 没有被宏定义过,那么就编译程序段 1 #else //这个可以没有 程序段 2 如果 x 被定义过则编译程序段 2 的语句,“忽视”程序段 1。 #endif
if 就是判断语句,不是预编译指令 一般情况下,源程序中所有的行都参加编译。但是有时 希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条 件,这就是“条件 编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组 语句。 条件编译命令最常见的形式为: #ifdef 标识符 程序段 1 #else 程序段 2 #endif 它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译, 否则编译程序段 2。 其中#else 部分也可以没有,即: #ifdef 程序段 1 #denif 这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高 C 源程序的通
第7章 C语言, 预编译命令
next
教材P246: 习题7 1-(11)
#include <stdio.h> #define MOD(x,y) x%y int main() { int z,a=15; float b=100; z=MOD(b,a); printf("%d\n",z++); return 0; }
该程序的输出为: A. 11 B. 10 C. 6 D. 有语法错误 √
int max(int x,int y) { return(x>y?x:y); } int main() { int a,b,c,d,t; ……. t=max(a+b,c+d); ……… }
next
带参的宏与函数的区别小结:
带参宏 处理时间 参数类型 处理过程 程序长度 运行速度 编译时 无类型 替换(不分配内存) 变长 不占用运行时间
4
C语言程序设计
2. 宏定义
(1) 宏定义的语法格式
① 不带参数的宏定义 ② 带参数的宏定义
#define 宏名 [宏体]
next part
example example go go
#define 宏名(参数表) 宏体
(2) 有关宏定义的一般说明 (3) 有关带参数的宏定义的特别说明
example
return
C语言程序设计
(2) 有关宏定义的一般说明 例: 简化程序,用定义的宏名替换宏体出现 1 ① 功能: #define YES main() ② 定义位置: 任意 (一般定义在函数外) { 从定义命令到文件结束; ③ 作用域:…….. YES原作用域 } 或者,碰到#undef终止宏定义 #undef YES ④ 宏展开: 预编译时用宏体替换宏名 #define 系统不做语法检查) (注意: YES 0 max() YES新作用域 ⑤ 宏可以嵌套定义,但不能递归定义! {…….. ⑥ 宏定义中使用必要的括号! } ⑦ 引号中的内容与宏名相同不作替换!
