




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C语言基本数据类型简介1.概述C 语言包含的数据类型如下图所示:2.各种数据类型介绍2.1整型整形包括短整型、整形和长整形。短整形short a=1;整形一般占4个字节(32位),最高位代表符号,0表示正数,1表示负数,取值范围是-21474836482147483647,在内存中的存储顺序是地位在前、高位在后,例如0x12345678在内存中的存储如下:地址:0x0012ff780x0012ff790x0012ff7a0x0012ff7b数据:78563412定义:用int关键字,举例如下:int a=6;长整形long a=10;2.2浮点型浮点型包括单精度型和双精度型。单精度型浮点型,又
2、称实型,也称单精度。一般占4个字节(32位),float a=4.5;地址:0x0012ff780x0012ff790x0012ff7a0x0012ff7b数据:00009040双精度型一般占8个字节(64位)double a=4.5;地址:0x0012ff780x0012ff790x0012ff7a0x0012ff7b0x0012ff7c0x0012ff7d0x0012ff7e0x0012ff7f数据:00000000000012402.3字符类型在各种不同系统中,字符类型都占一个字节(8位)。定义如下:char c='a'也可以用字符对应的ASCII码赋值,如下:
3、char c=97;3.数据类型与“模子”short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。怎么去理解它们呢? 举个例子:见过藕煤球的那个东西吧?(没见过?煤球总见过吧)。那个东西叫藕煤器,拿着它在和好的煤堆里这么一咔,一个煤球出来了。半径12cm,12 个孔。不同型号的藕煤器咔出来的煤球大小不一样,孔数也不一样。这个藕煤器其实就是个模子。现在我们联想一下,short、int、long、char、float、double 这六个东东是不是很像不同类型的藕煤器啊?拿着它们在内存上咔咔咔,不同大小的内存就分配好了,当然别忘了给它们取个好
4、听的名字。在32 位的系统上short 短整型的内存2 byte;int 整型的内存4 byte;long 长整型的内存4 byte;float 单精度浮点型的内存4byte;仅能接收7位有效数字double 双精度浮点型的内存8 byte;可以接收16位有效数字char字符型的内存1 byte。fabs单精度浮点型内存4byte.(注意这里指一般情况,可能不同的平台还会有所不同,具体平台可以用sizeof 关键字测试一下)很简单吧?咔咔咔很爽吧?是很简单,也确实很爽,但问题就是你咔出来这么多内存块,你总不能给他取名字叫做x1,x2,x3,x4,x5或者长江1 号,长江2 号吧。它们长得这么像
5、(不是你家的老大,老二,老三),过一阵子你就会忘了到底哪个名字和哪个内存块匹配了(到底谁嫁给谁了啊?_)。所以呢,给他们取一个好的名字绝对重要。下面我们就来研究研究取什么样的名字好。4.变量的命名规则1)命名应当直观且可以拼读,可望文知意,便于记忆和阅读。标识符最好采用英文单词或其组合,不允许使用拼音。程序中的英文单词一般不要太复杂,用词应当准确。2)命名的长度应当符合“min-length && max-information”原则。C 是一种简洁的语言, 命名也应该是简洁的。例如变量名MaxVal 就比MaxValueUntilOverflow 好用。标识符的长度一般不要过
6、长,较长的单词可通过去掉“元音”形成缩写。另外,英文词尽量不缩写,特别是非常用专业名词,如果有缩写,在同一系统中对同一单词必须使用相同的表示法,并且注明其意思。3)当标识符由多个词组成时,每个词的第一个字母大写,其余全部小写。比如: int CurrentVal;这样的名字看起来比较清晰,远比一长串字符好得多。4)尽量避免名字中出现数字编号,如Value1,Value2 等,除非逻辑上的确需要编号。比如驱动开发时为管脚命名,非编号名字反而不好。初学者总是喜欢用带编号的变量名或函数名,这样子看上去很简单方便,但其实是一颗颗定时炸弹。这个习惯初学者一定要改过来。5)对在多个
7、文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符)。(GUI_ ,etc)标识符的命名规则:6)标识符名分为两部分:规范标识符前缀(后缀) + 含义标识。非全局变量可以不用使用范围限定符前缀。7)作用域前缀命名规则。8)数据类型前缀命名规则。9)含义标识命名规则,变量命名使用名词性词组,函数命名使用动词性词组。例如:变量含义标识符构成:目标词+ 动词(的过去分词)+ 状语 + 目的地;函数含义标识符构成:动词(一般现时)+目标词+状语+目的地;10)程序中不得出现仅靠大小写区分的相似的标识符。例如: int x, X; 变量x 与X
8、容易混淆 void foo(int x); 函数foo 与FOO 容易混淆 void FOO(float x);这里还有一个要特别注意的就是1(数字1)和l(小写字母l)之间,0(数字0)和o(小写字母o)之间的区别。这两对真是很难区分的,我曾经的一个同事就被这个问题折腾了一次。11)一个函数名禁止被用于其它之处。例如:#include "c_standards.h"void foo(int p_1) int x = p_1;void static_p(void) int foo
9、 = 1u;12)所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。例如:const int MAX_LENGTH = 100; /这不是常量,而是一个只读变量,具体请往后看 #define FILE_PATH “/usr/tmp”13)考虑到习惯性问题,局部变量中可采用通用的命名方式,仅限于n、i、j 等作为循环变量使用。一定不要写出如下这样的代码: int p; char i; int c; char * a;一般来说习惯上用n,m,i,j,k 等表示in
10、t 类型的变量;c,ch 等表示字符类型变量;a 等表示数组;p 等表示指针。当然这仅仅是一般习惯,除了i,j,k 等可以用来表示循环变量外,别的字符变量名尽量不要使用。14)定义变量的同时千万千万别忘了初始化。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据。这个问题在内存管理那章有非常详细的讨论,请参看。15)不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度数据扩展。格式字符串列表在c语言中 %p ,%d和%x 的区别%p 是以16进制的形式输出内存地址 %x 也是以16进制的形式输出内存地址不过%p的输出字符为8个前2个为00%x只有6个他们有什
11、么区别吗?%d 可以输出整数也可以以10进制的形式输出指针这里输出的是地址吗?%d 是有符号的%x 是无符号的%p 呢?当我用%d表示-2的时候 输出结果为-2但我用%u表示-2的时候 输出结果却是4294967294d以十进制输出带符号整数o以八进制输出无符整形x,X以十六进制输出无符整数u,以十进制输出无符整数f.以小数形式输出单,双精度实数e,E以指数形式输出单,双精度实数g,G以%f或%ec,s,P,变量声明和定义的区别我们在程序设计中,时时刻刻都用到变量的定义和变量的声明,可有些时候我们对这个概念不是很清楚,知道它是怎么用,但却不知是怎么一会事,下面我就简单的把他们的区别介绍如下:(
12、望我的指点对你受益)变量的声明有两种情况:1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。2、另一种是不需要建立存储空间的。 例如:extern int a 其中变量a是在别的文件中定义的。前者是“定义性声明(defining declaration)”或者称为“定义(definition)”,而后者是“引用性声明(referncing declaration)”,从广义的角度来讲声明中包含着定义,即定义是声明的一个特例,所以并非所有的声明都是定义,例如:int a 它既是声明,同时又是定义。然而对于 extern a 来讲它只是声明不是定义。一般的情况下我们
13、常常这样叙述,把建立空间的声明称之为“定义”,而把不需要建立存储空间的声明称之为“声明”。很明显我们在这里指的声明是范围比较窄的,即狭义上的声明,也就是说非定义性质的声明,例如:在主函数中:int main() extern int A;/这是个声明而不是定义,声明A是一个已经定义了的外部变量注意:声明外部变量时可以把变量类型去掉如:extern A;dosth(); /执行函数int A; /是定义,定义了A为整型的外部变量外部变量的“定义”与外部变量的“声明”是不相同的,外部变量的定义只能有一次,它的位置是在所有函数之外,而同一个文件中的外部变量声明可以是多次的,它可以在函数之内(哪个函数
14、要用就在那个函数中声明)也可以在函数之外(在外部变量的定义点之前)。系统会根据外部变量的定义(而不是根据外部变量的声明)分配存储空间的。对于外部变量来讲,初始化只能是在“定义”中进行,而不是在“声明”中。所谓的“声明”,其作用,是声明该变量是一个已在后面定义过的外部变量,仅仅是为了“提前”引用该变量而作的“声明”而已。extern 只作声明,不作任何定义。(我们声明的最终目的是为了提前使用,即在定义之前使用,如果不需要提前使用就没有单独声明的必要,变量是如此,函数也是如此,所以声明不会分配存储空间,只有定义时才会分配存储空间。)用static来声明一个变量的作用有二:(1)对于局部变量用sta
15、tic声明,则是为该变量分配的空间在整个程序的执行期内都始终存在。(2)外部变量用static来声明,则该变量的作用有符号整数与无符号整数有符号整数:其最高位被C编译器解释为符号位(0为正,1为负)。无符号整数:其最高位被C编译器解释为数据位。注意1,对具有相同字节数的整数,有符 能表示的最大整数是无符 所表示的一半2,补码:负数在计算机中都是二进制补码来表示和储存。3,补码的计算:保持符号位不变,将源码中的 1换为0, 0换为1 为该数的反码 再加1的结果就是该数的补码。C语言中有两种方式可以表示指数:1、 直接用浮点数表示:10的N次方为 1e10 也可写成1e+10(如果是负N次方的话就
16、把加号变成减号)。e大小写都可以,需要注意的是e前面必须有一个数字,不然的话就非法表达。2、用幂函数表示:在c语言中,求x的y次方可用pow(x,y)表示,所以10的N次方也可表示为pow(10,N)。其中pow函数在头文件math.h中,所以调用该函数的时候,必须将math.h加进来,即#include <math.h>原型:extern float pow(float x, float y);功能:计算x的y次幂。可以用循环(for, while, do.while)函数类型所占的字节short 短整型的内存2 byte;int 整型的内存4 byte;long 长整型的内存4
17、 byte;float 单精度浮点型的内存4byte;仅能接收7位有效数字double 双精度浮点型的内存8 byte;可以接收16位有效数字char字符型的内存1 byte。fabs单精度浮点型内存4byte.(C中的CONST) 编辑const是一个C语言(ANSI C)的关键字,它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助。另外CONST在其他编程语言中也有出现,如C+、PHP5、B#.net、HC08 C、C#。(1)可以定义const常
18、量,具有不可变性。例如:const int Max=100; Max+会产生错误;(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。例如: void f(const int i) . 编译器就会知道i是一个常量,不允许修改;(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错;例如: void f(c
19、onst int i) i=10;/error! (5) 可以节省空间,避免不必要的内存分配。 例如:#define PI 3.14159 /常量宏const double Pi=3.14159; /此时并未将Pi放入RAM中 .double i=Pi; /此时为Pi分配内存,以后不再分配!double I=PI; /编译期间进行宏替换,分配内存double j=Pi; /没有内存分配double J=PI; /再进行宏替换,又一次分配内存!const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份
20、拷贝,而#define定义的常量在内存中有若干份拷贝。(6) 提高了效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高函数类型的高低Floatchar,shortdoublelong unsignedint高自动类型转换规则低Switch(只能是int, char或是它俩的变量) case (只能是int或char 不能出现运算符如:(mark>1)):break;case 1:.break;case #: .break;while , dowhile 和for的区别未知循环次数时:W
21、hile(条件)先判断,满足条件后循环。dowhile(条件)先循环一次,在判断,满足条件再循环。已知循环次数时:for(初始值;条件;#+)先判断,满足条件后break和continuebreak跳出整个循环,循环结束。continue跳出本次循环,循环继续。变量(全局变量和局部变量)全局变量:不在函数中定义的变量。局部变量:在函数中定义的变量。注意:在一个函数中定义过的变量(如:sum),在下一函数还可以用它定义,之间不冲突。说明函数中的变量的值只在本函数中使用,不在其它函数中使用。所以全局变量也可以被定义为局部变量(因为,他们之间的值并不冲突)。例如:#include”stdio.h”V
22、oid Swap(int a,int b);Int main(); 宏与全局变量的区别1 作用时间不同。宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以。2 本质类型不同。宏定义的只是一段字符,在编译的时候被替换到引用的位置。在运行中是没有宏定义的概念的。而变量在运行时要为其分配内存。3 宏定义不可以被赋值,即其值一旦定义不可修改,而变量在运行过程中可以被修改。4 宏定义只有在定义所在文件,或引用所在文件的其它文件中使用。 而全局变量可以在工程所有文件中使用,只要再使用前加一个声明就可以了。换句话说,宏定义不支持extern。#define 宏名 宏体 #define PI 3.14
23、15926#define SET_PROC(proc) int temp; temp = get_temp(); temp.a = (proc); set_temp(temp);宏函数假设有这样一个需求:一个项目需要250个函数,但这些函数体内容完全相同,仅函数名不同,例如Func0、Func1、Func2.等等,这样的要求怎么实现呢?(先不要问有没有如此变态的需求)当然可以使用手工先写Func0函数体,再写Func1函数体,.,一直写到函数体Func
24、250。假设写完了后,项目需求有变,函数体内有变化,还要从头开始一个一个改.!宏代换函数 函数式宏定义:#define MAX(a,b) (a)>(b)?(a):(b)普通函数 : MAX(a,b) return a>b?a:b;尽管函数式宏定义和普通函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。格式:#define + 名+(形参)功能:代换出一个字符型内存变量的内容。若<字
25、符型内存变量>与后面的字符无空格分界,则&函数后的“.”必须有。 例如,m="4*32+5" 这样的使用需求会碰到的:一个项目创建了N个线程,每个线程访问相同的一个回调函数,并传递进入相同的参数,但是这个回调函数不能只写一个,应该是一个线程对应一个回调函数,就是这样一个问题。C语言宏定义使用技巧写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等。下面列举一些成熟软件中常用得宏定义。1,防止一个头文件被重复包含#ifndef COMDEF_H#define COMDEF_H /头文件内容#e
26、ndif2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。typedef unsigned char boolean; /* Boolean value type. */typedef unsigned long int uint32; /* Unsigned 32 bit value */typ
27、edef unsigned short uint16; /* Unsigned 16 bit value */typedef unsigned char uint8; /* Unsigned 8 bit value */typedef
28、0;signed long int int32; /* Signed 32 bit value */typedef signed short int16; /* Signed 16 bit value */typedef signed char
29、0; int8; /* Signed 8 bit value */下面的不建议使用typedef unsigned char byte; /* Unsigned 8 bit value type. */ty
30、pedef unsigned short word; /* Unsinged 16 bit value type. */typedef unsigned long dword; /* Unsigned 32 bit value type. */type
31、def unsigned char uint1; /* Unsigned 8 bit value type. */typedef unsigned short uint2; /* Unsigned 16 bit value type. */t
32、ypedef unsigned long uint4; /* Unsigned 32 bit value type. */typedef signed char int1; /* Signed 8
33、0;bit value type. */typedef signed short int2; /* Signed 16 bit value type. */typedef long int int4; &
34、#160; /* Signed 32 bit value type. */typedef signed long sint31; /* Signed 32 bit value */typedef signed short sint15;
35、160; /* Signed 16 bit value */typedef signed char sint7; /* Signed 8 bit value */3,得到指定地址上的一个字节或字#define MEM_B( x ) ( *( (byte *) (x) ) )#define M
36、EM_W( x ) ( *( (word *) (x) ) )4,求最大值和最小值 #define MAX( x, y ) ( (x) > (y) ? (x) : (y) ) #define MIN( x, y ) ( (x) < (y) ? (x) : (y) )5,得到一个field在结构体(struct)中的偏移量#define FPOS( type, field ) /*lint -e545 */ ( (dword) &( type
37、*) 0)-> field ) /*lint +e545 */6,得到一个结构体中field所占用的字节数#define FSIZ( type, field ) sizeof( (type *) 0)->field )7,按照LSB格式把两个字节转化为一个Word#define FLIPW( ray ) ( (word) (ray)0) * 256) + (ray)1 )8,按照LSB格式把一个Word转化为两个字节#define FLOPW( ray, val ) (ray)0 = (val) / 256); &
38、#160; (ray)1 = (val) & 0xFF)9,得到一个变量的地址(word宽度)#define B_PTR( var ) ( (byte *) (void *) &(var) )#define W_PTR( var ) ( (word *) (void *) &(var) )10,得到一个字的高位和低位字节#define WORD_LO(*) (byte) (word)(*) & 255)#define
39、60; WORD_HI(*) (byte) (word)(*) >> 8)11,返回一个比X大的最接近的8的倍数#define RND8( x ) (x) + 7) / 8 ) * 8 )12,将一个字母转换为大写#define UPCASE( c ) ( (c) >= 'a' && (c) <= 'z') ? (c) - 0x20) : (c) )13,判断字符是不是10进值的数字#de
40、fine DECCHK( c ) (c) >= '0' && (c) <= '9')14,判断字符是不是16进值的数字#define HEXCHK( c ) ( (c) >= '0' && (c) <= '9') |
41、160; (c) >= 'A' && (c) <= 'F') |(c) >= 'a' && (c) <= 'f') )15,防止溢出的一个方法#define INC_SAT( val ) (val = (val)+1 > (val) ? (val)+1 : (val)16,返回数组元素的个数#define ARR_SIZE( a ) (
42、sizeof( (a) ) / sizeof( (a0) ) )17,返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2n)#define MOD_BY_POWER_OF_TWO( val, mod_by ) ( (dword)(val) & (dword)(mod_by)-1) )18,对于IO空间映射在存储空间的结构,输入输出处理 #define inp(port)
43、; (*(volatile byte *) (port) #define inpw(port) (*(volatile word *) (port) #define inpdw(port) (*(volatile dword *)(port) #define outp(port, val
44、) (*(volatile byte *) (port) = (byte) (val) #define outpw(port, val) (*(volatile word *) (port) = (word) (val) #define outpdw(port, val) (*(volatile dword *) (port) = (dword) (val)2005-9-9添加19,使用一些宏跟踪调试A N S I标准说明了五个预定义的宏名。它们是:_ L I N E _ F I L E _ D
45、 A T E _ T I M E _ S T D C _如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。_ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。_ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是非标准的。可以定义宏,例如:当定义了_DEBUG,输出数据信息和所在
46、文件所在行#ifdef _DEBUG#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)#else #define DEBUGMSG(msg,date)#endif20,宏定义防止使用是错误用小括号包含。例如:#define ADD(a,b) (a+b)用dowhile(0)语句包含多语句防止错误例如:#difne DO(a,b) a+b;
47、; a+;应用时:if(.) DO(a,b); /产生错误 else C语言中如何使用宏 C(和C+)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单
48、总结。 宏使用中的常见的基础问题 #符号和#符号的使用 .符号的使用 宏的解释方法 我们能碰到的宏的使用 宏使用中的陷阱 常见的基础性问题 关于#和#在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作
49、(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏: #define WARN_IF(EXP) do if (EXP)
50、160; fprintf(stderr, "Warning: " #EXP "n"); while(0)那么实际使用中会出现下面所示的替换过程: WARN_IF (divider = 0);被替换为do if (divider = 0)fprintf(stderr, "
51、Warning" "divider = 0" "n"); while(0);这样每次divider(除数)为0的时候便会在标准错误流上输出一个提示信息。 而#被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那么下面的代码就非常实用: struct commandchar * name;void (*function) (v
52、oid);#define COMMAND(NAME) NAME, NAME # _command / 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:struct command commands = COMMAND(quit),COMMAND(help),.COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心所造成的错误。我们还可以n个#符号连接 n+1个Token,这个特性也是#符号所不具备的。比如: #define LINK_MULTIPLE(a,b,c,d) a#_#b#_#c#_#dtypede
53、f struct _record_type LINK_MULTIPLE(name,company,position,salary);/ 这里这个语句将展开为:/ typedef struct _record_type name_company_position_salary; 关于.的使用.在C宏中称为Variadic Macro,也就是变参宏。比如: #define myprintf(templt,.) fprintf(stderr,templt,_V
54、A_ARGS_)/ 或者#define myprintf(templt,args.) fprintf(stderr,templt,args)第一个宏中由于没有对变参起名,我们用默认的宏_VA_ARGS_来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以用args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最有一项出现。当上面的宏中我们只能提供第一个参数templt时,C标准要求我们必须写成: myprintf(templt,);的形式。这时的替换过程为: myprintf("Error!n",);
55、 替换为:fprintf(stderr,"Error!n",);这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成: myprintf(templt);而它将会被通过替换变成: fprintf(stderr,"Error!n",);很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式: #define myprintf(templt, .
56、) fprintf(stderr,templt, #_VAR_ARGS_)这时,#这个连接符号充当的作用就是当_VAR_ARGS_为空的时候,消除前面的那个逗号。那么此时的翻译过程如下: myprintf(templt);被转化为:fprintf(stderr,templt);这样如果templt合法,将不会产生编译错误。 宏是如何解释的宏在日常编程中的常见使用宏使用中的陷阱这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。 错误的嵌
57、套Misnesting宏的定义不一定要有完整的、配对的括号,但是为了避免出错并且提高可读性,最好避免这样使用。由操作符优先级引起的问题Operator Precedence Problem由于宏只是简单的替换,宏的参数如果是复合结构,那么通过替换之后可能由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,如果我们不用括号保护各个宏参数,可能会产生预想不到的情形。比如: #define ceil_div(x, y) (x + y - 1) / y那么 a = ceil_div( b & c, sizeof(int) );将被转化为:
58、0;a = ( b & c + sizeof(int) - 1) / sizeof(int);/ 由于+/-的优先级高于&的优先级,那么上面式子等同于:a = ( b & (c + sizeof(int) - 1) / sizeof(int);这显然不是调用者的初衷。为了避免这种情况发生,应当多写几个括号: define ceil_div(x, y) (x) + (y) - 1) / (y)消除多余的分号Semicolon Swallowing通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C
59、语言调用一样,通常情况下我们在宏的后面加上一个分号,比如下面的带参宏: MY_MACRO(x);但是如果是下面的情况: #define MY_MACRO(x) /* line 1 */ /* line 2 */ /* line 3 */ /.if (condition()
60、 MY_MACRO(a);else .这样会由于多出的那个分号产生编译错误。为了避免这种情况出现同时保持MY_MACRO(x);的这种写法,我们需要把宏定义为这种形式: #define MY_MACRO(x) do /* line 1 */ /* line 2 */ /* line 3 */ while(0)这样只要保证总是使用分号,就不会有
61、任何问题。 Duplication of Side Effects这里的Side Effect是指宏在展开的时候对其参数可能进行多次Evaluation(也就是取值),但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如: #define min(X,Y) (X) > (Y) ? (Y) : (X)/.c = min(a,foo(b);这时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏: #define
62、min(X,Y) ( typeof (X) x_ = (X); typeof (Y) y_ = (Y); (x_ < y_) ? x_ : y_; )(.)的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。 补充:1、#define命令#define定义了一个标识符及一个串。在源程序中每次遇到该标识符时,均以定义的串代换它
63、。ANSI标准将标识符定义为宏名,将替换过程称为宏 替换。命令的一般形式为:#define identifier string注意:? 该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束。? 宏名定义后,即可成为其它宏名定义中的一部分。? 宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,否则不进行替换。例如: #define XYZ this is a test,使用宏printf("XYZ");/该段不打印"this is a test"而打印"XYZ"。因为预编译器
64、识 别出的是"XYZ"? 如果串长于一行,可以在该行末尾用一反斜杠' '续行。2、#error 处理器命令#error强迫编译程序停止编译,主要用于程序调试。 3、i nclude 命令i nclude使编译程序将另一源文件嵌入带有i nclude的源文件,被读入的源文件必须用双引号或尖括号括起来。例如: i nclude"stdio.h"或者i nclude 这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。 将文件嵌入i nclude命令中的文件内是可
65、行的,这种方式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。 如果显式路径名为文件标识符的一部分,则仅在哪些子目录中搜索被嵌入文件。否则,如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件, 则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。 如果没有显式路径名且文件名被尖括号括起来,则首先在编译命令行中的目录内检索。 如果文件没找到,则检索标准目录,不检索当前工作目录。4、条件编译命令 有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司广泛应用条件编译来提供和维护某一程
66、序的许多顾客版本。 #if、#else,#elif及#endif #if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的 结束。 #if constant-expression statement sequence #endif 跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译 时求值)。 #else命令的功能有点象C语言中的
67、else;#else建立另一选择(在#if失败的情况下)。 注意,# else属于# if块。 #elif命令意义与ELSE IF 相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。 #elif 后跟一个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测试。否则,顺序测试下一块。 #if expression statement sequence #elif expression1 statement sequence #endif 在嵌套的条件编译中#endif、#else或#elif与最近#if或#elif匹配。 # ifdef 和# ifndef 条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示"如果有定义"及"如果无定义"。 # ifdef的一般形式是: # ifdef macroname statement sequence #endi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- T/SHPTA 094-2024动力电池用有机硅灌封胶
- T/BJHWXH 002-2024路用低氯低钠融雪剂
- 挂牌合作办学协议书7篇
- 黄芩收购合同8篇
- 上海中考滑轮试题及答案
- 厦门市城市房屋拆迁补偿安置协议书范本6篇
- 2025专利申请代理合同3篇
- 房产继承协议书6篇
- 测量呼吸护理
- 台站测风仪项目绩效评估报告
- 2024年江苏中考地理试卷(带有答案)
- 江苏省江阴市普通高中2023-2024学年物理高一第二学期期末统考试题含解析
- 唐诗宋词人文解读智慧树知到期末考试答案章节答案2024年上海交通大学
- 小学四年级奥数-还原问题
- 江苏省2024年中职职教高考文化统考财会专业综合理论试卷
- 《电力安全工器具预防性试验规程》
- GB/T 43731-2024生物样本库中生物样本处理方法的确认和验证通用要求
- 建筑装饰装修工程消耗量定额
- 排水工程毕业设计哈工大
- 北京市2023年中考备考语文专题复习 名著阅读题(解析)
- 黄太吉融资商业计划书
评论
0/150
提交评论