第八章 预处理
cpp源文件——(预编译)》预处理文件——(编译)》目标文件——(连接)》可执行文件
预处理:源文件在进行编译时第一遍扫描之前做的工作(词法分析和语法分析)
程序员与预处理器进行交互的工具是一种被称作预处理器指示的命令(一些以“#”号开头的单行命令)
编译的源文件test.cpp
#include <stdio.h> #define PI 3.1415926 int main( ) { int r = 3; float girth, area; girth = 2*PI*r; area = PI*r*r; printf("周长为:%f 面积为: %f\n",girth, area ); }
与预编译文件test.i文件
#line 713 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h" __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_tempnam" ". See online help for details.")) __declspec(dllimport) char * __cdecl tempnam( const char * _Directory, const char * _FilePrefix); #line 719 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h" __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fcloseall" ". See online help for details.")) __declspec(dllimport) int __cdecl fcloseall(void); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fdopen" ". See online help for details.")) __declspec(dllimport) FILE * __cdecl fdopen( int _FileHandle, const char * _Format); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fgetchar" ". See online help for details.")) __declspec(dllimport) int __cdecl fgetchar(void); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fileno" ". See online help for details.")) __declspec(dllimport) int __cdecl fileno( FILE * _File); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_flushall" ". See online help for details.")) __declspec(dllimport) int __cdecl flushall(void); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fputchar" ". See online help for details.")) __declspec(dllimport) int __cdecl fputchar( int _Ch); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_getw" ". See online help for details.")) __declspec(dllimport) int __cdecl getw( FILE * _File); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_putw" ". See online help for details.")) __declspec(dllimport) int __cdecl putw( int _Ch, FILE * _File); __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_rmtmp" ". See online help for details.")) __declspec(dllimport) int __cdecl rmtmp(void); #line 731 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h" } #line 735 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h" #pragma pack(pop) #line 739 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h" #line 3 "c:\\users\\hzp\\documents\\visual studio 2010\\projects\\test03\\test03\\test03.cpp" int main( ) { int r; float girth, area; girth = 2*3.1415926*r; area = 3.1415926*x*x; printf("周长为:%d 面积为: %f\n",girth, area ); }
对比两个文件的差别,得出预编译所做的工作:
1)引入:删除#include指示,并将stdio.h的内容加入到程序中来对其进行响应,
2)替换:删除恶劣#define指示,并将该文件所有PI替换为指定值
3)把注释替换成一个空格字符
4)删除一些不必要的空格
Ps:这个.i文件有助于调试错误程序
一、宏定义:
I.无参宏定义
#define PI 3.1415926 //指定PI的值为3.1415926 #define ARRAY_LEN 80 //指定数组长度为80 #define CR '\r' //指定CR表示换行符
作用:无参宏定义经常被用于给数值、字符、和字符串命名
优点:
A.简化输入并防止输入出错
B.可读性
C.易维护,做到一改全改
II.带参宏定义
#include <stdio.h> #define MAX(x,y) (((x)>(y)? (x):(y))) int main( ) { int i = 0; int j = 1; printf("the larger of two:%d", MAX(i,j)); }
作用:带参宏定义经常被用作模板:如上例中 (((x)>(y)? (x):(y)))如果要经常使用(或是某些语句)
若使用宏替换可以设法得到更多的结果
#include <stdio.h> #define MAX_MIN(x,y,max,min) max =(((x)>(y)? (x):(y))); min = (((x)<(y)? (x):(y))); int main( ) { int i = 0; int j = 1; int max = 0; int min = 0; MAX_MIN(i, j, max,min); printf("两个数中的最大数是:%d\n",max); printf("两个数中的最小数是:%d\n",min); }
说明
1) 函数调用是在程序运行时处理的,必须为形参分配临时空间,而宏的展开则是编译前进行(不分配存储单元,也不进行值传递);
2)宏名没有类型,其参数也没有类型,而函数的参数和返回值都是有特定类型的;
3)无法用一个指针来指向一个宏,但可以使用一个指针指向一个函数;
III.使用宏时注意事项
1)宏定义中的各种符号都被视为替换文本,可能会引起一些错误(编译器会直接找出错误的地方,而不会找到错误的根源——宏定义本身)
#define LENGTH = 100;//加入了分号 int a[LENGTH]; 预编译后: int a[= 100;];
2)#define的有效范围是从其定义命令开始到此源文件结束。但可以使用#undef指示宏定义的作用域
#undef PI //取消前面定义的圆周率的宏但如果前面没有定义,那么此句不起任何作用
3)宏可以嵌套定义
#define R 1.5 #define PI 3.1415926 #define L 2*PI*R
4)宏名作为整体被替换
#define LENGTH 10 int ARRAY_LENGTH //只会替换LENGTH;而不会替换ARRAY_LENGTH
5) 必要的括号
#define MUL(x, y) x*y result = MUL(5+3,6); //被替换成5+3*6 //正确的写法加括号 #define MUL(x, y) ((x)*(y))//因为圆括号的运算级别最高,这样替换可以按照编程者的思路进行,而不会因为其他运算符的优先级而出现非预期的结果 //且参数的每一出现都要加括号,而不是只给替换文本加括号
#define PER(x) (x/100) j = PER(i+1 ); //预编译后: j = i+1/100;
6)太长的情况下使用“\”:
#define PF(one, two,three,four,five) printf("%d,%d,%d,%d,%d,%d"\n), \
one ,two,three, four,five)
7) 宏不允许重复定义
IV.控制版本的宏
#include <stdio.h> int main( ) { printf("编译日期为: %d\n", __LINE__); printf("编译日期为: %s\n", __FILE__); printf("编译日期为: %s\n", __DATE__); printf("编译日期为: %s\n", __TIME__); }
二.条件编译
1.第一种形式
#ifdef 标识符 程序片段1 #else 程序片段2 #endif
说明 如果 标识符已经被#define指示定义过,则对程序片段1进行编译,否则对程序片段2进行编译
2.第二种形式
#ifndef 标识符 程序片段1 #else 程序片段2 #endif
说明:如果标识符未被define命令定义过,则对程序片段1进行编译,否则对程序片段2进行编译
3.第三种形式
#if 常量表达式1
由于克隆虚拟机,vmware只是修改了虚拟机的名字等信息,并没有修改虚拟硬盘中的任何信息,导致克隆后网卡的MAC地址和操作系统中记录的mac地址不符,导致eth0启动不起来。操作系统记录了一个新网卡的添加,新网卡的名字eth1,mac地址就是vmware分配给的新的mac地址 解决方法: 修改 /etc/udev/rules.d/70-persistent-net.rules 文件 删除掉 关于 eth0 的信息。修改 第二条 eth1 的网卡的名字为 eth0. 修改 /etc/sysconfig/network-scripts/ifcfg-eth0 中mac地址为 /etc/udev/rules.d/70-persistent-net.rules 修改后的eth0的mac地址。
以上内容摘自:http://www.2cto.com/os/201210/159434.html
给Centos虚拟机配置固定ip
vim /etc/sysconfig/network-scripts/ifcfg-eth0
以下是网卡eth0的信息
DEVICE="eth0" #BOOTPROTO="dhcp" BOOTPROTO="static" HWADDR="00:0c:29:74:0b:15" IPADDR="192.168.9.130" GATEWAY="192.168.9.1" NETMASK="255.255.255.0" NETWORK="192.168.9.0" NM_CONTROLLED="yes" ONBOOT="yes" TYPE="Ethernet" UUID="3d8d42e3-c409-4f19-9553-aac34782711c"
最近在做一个GIS系统, 在读GIS数据时采用了动态数组,突然读一个数据时SetLength报错!Out of memory
仔细研究了代码, 发现代码没有问题。问题应该动态数组的问题。
接下来查看各种资料,发现也有一些人发现了类似的问题,一般认为是频繁SetLength造成的,
具体为什么为造成错误呢?
我们来做一个测试
procedure TForm1.Button1Click(Sender: TObject);
var
I,J : Integer;
begin
for I := 1 to 10 do begin
SetLength(giDatas,I*10);
giDatas[I*10-1] := I;
//
Memo1.Lines.Add(Format('I=%.2d, Addr = %d',[I,Integer(@giDatas[0])]));
end;
end;
这个一段非常简单的代码,一般会认为@giDatas[0]会是定值(至少我以前是这样认为的)
I=01, Addr = 27139384
I=02, Addr = 26525640
I=03, Addr = 26525640
I=04, Addr = 26395448
I=05, Addr = 26395448
I=06, Addr = 26395448
I=07, Addr = 26395448
I=08, Addr = 26694280
I=09, Addr = 26694280
I=10, Addr = 26694280
根据得出的结果来看,@giDatas[0]在不断改变, 也就是giDatas在不断开辟新的内存
原来占用的内存没有及时释放,所以多次SetLength后会报Out of memory.
问题找出来了, 解决办法还是问题。 仍然查找了许多资料, 发现用指针来解决
比如类似的程序
type
duilie=record
item_name:integer;
level:integer;
count:integer;
father:integer;
children:array of integer;
end;
var
leaf:array of duilie;
其中leaf的定义采用
type
Pduilie = ^duilie;
var
leaf:array of pduilie;
应该就可以了
------------------------------------------------------------------
WebXone
Delphi生成B/S程序新平台!