from:
http://blog.csdn.net/wuliming_sc/archive/2006/11/04/1365923.aspx1. 什么是标准预定义宏?
2. 连接运算符“##”和字符串化运算符"#"有什么作用?
3. 怎样删去字符串尾部的空格?
4. 怎样删去字符串头部的空格?
5. 怎样打印字符串的一部分?
6. 用malloc()函数更好还是用calloc()函数更好?
7. NULL和NUL有什么不同?
8. 在程序退出main()函数之后,还有可能执行一部分代码吗?
9. 数组作为函数的常数时,可以通过sizeof运算符得到函数数组的大小吗?
10. array_name和&array_name有什么不同?
11. 怎样判断一个字符是数字、字母或其它类别的符号?
什么是标准预定义宏?
ANSIC标准定义了以下6种可供C语言使用的预定义宏:
----------------------------------------------------------------------------
宏 名 作 用
----------------------------------------------------------------------------
__LINE__ 在源代码中插入当前源代码行号
__FILE__ 在源代码中插入当前源代码文件名
__DATE__ 在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
__TIME__ 在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕
__STDC__ 当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
----------------------------------------------------------------------------
标识符__LINE__和__FILE__通常用来调试程序;标识符__DATE__和__TIME__通常用来在编译后的程序中加入一个时间标志,以区分程序的不同版本;当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1;当用C++编译程序编译时,标识符__cplusplus就会被定义。
1 #include <stdio.h>
2
3 int main ()
4 {
5 printf("该输出行在源程序中的位置:%d\n", __LINE__ );
6 printf("该程序的文件名为:%s\n", __FILE__ );
7 printf("当前日期为:%s\n", __DATE__ );
8 printf("当前时间为:%s\n", __TIME__ );
9
10 return 0;
11 }
连接运算符“##”和字符串化运算符"#"有什么作用?
连接运算符“##”可以把两个独立的字符串连接成一个字符串。在C的宏中,经常用到“##”运算符,请看下例:
1 #include <stdio.h>
2
3 int main ()
4 {
5 printf("该输出行在源程序中的位置:%d\n", __LINE__ );
6 printf("该程序的文件名为:%s\n", __FILE__ );
7 printf("当前日期为:%s\n", __DATE__ );
8 printf("当前时间为:%s\n", __TIME__ );
9
10 return 0;
11 }
在该例中,宏SORT利用“##”运算符把字符串sort_function和经参数x传递过来的字符串连接起来,这意味着语句 SORT(3)(array,elemnts,element_size) 将被预处理程序转换为语句:
sort_function3(array,elements,element_size);
从宏SORT的用法中你可以看出,如果在运行时才能确定要调用哪个函数,你可以利用“##”运算符动态地构造要调用的函数的名称。
1
2 #include<stdio.h>
3
4 #define FUNC(X) func ## X
5
6 int func1(int a, int b)
7 {
8 return 1;
9 }
10
11 int func2(int a, int b)
12 {
13 return 2;
14 }
15
16 int main()
17 {
18 int a, b, result;
19 result = FUNC(1)(a, b);
20 printf("---%d---\n", result);
21
22 result = FUNC(2)(a, b);
23 printf("---%d---\n", result);
24
25 getchar();
26
27 return 0;
28 }
字符串化运算符"#"运算符能将宏的参数转换为带双引号的字符串,请看下例:
define DEBUG_VALUE(v) printf(#v"is equal to %d. \n", v)
你可以在程序中用 DEBUG_VALUE 宏检查变量的值,请看下例:
int x=20;
DEBUG_VALUE(x);
上述语句将在屏幕上打印"x is equal to 20"。这个例子说明,宏所使用的“#”运算符是一种非常方便的调试工具。
1 #include <stdio.h>
2
3 #define DEBUG_VALUE_INT(v) printf(#v" is equal to %d.\n",v )
4
5 #define DEBUG_VALUE_STR(v) printf(#v" is equal to %s.\n",v )
6
7 int main(int)
8 {
9 int x = 0;
10 char str[] = "asdfqweqwfdvasdf";
11
12 DEBUG_VALUE_INT(x);
13
14 DEBUG_VALUE_STR(str);
15
16 getchar();
17
18 return 0;
19 }
20
怎样删去字符串尾部的空格?
C语言没有提供可删去字符串尾部空格的标准库函数,但是,编写这样的一个函数是很方便的。请看下例:
1 #include <stdio.h>
2 # include <string.h>
3
4 char * rtrim( char * );
5
6 int main()
7 {
8
9 //char * trail_str = "0123456789 "; 把字符串定义成这种形式时,运行程序的时候会出现异常
10
11 char trail_str[21] = "0123456789 ";
12
13 printf( "Before calling rtrim(), trail_str is '%s'\n" , trail_str );
14 printf( "and has a length of %d. \n" , strlen( trail_str ) );
15
16 rtrim(trail_str);
17
18 printf( "After calling rttim(), trail_ str is '%s'\n", trail_str );
19 printf( "and has a length of %d. \n" , strlen( trail_str ) ) ;
20
21 getchar();
22
23 return 0;
24 }
1 /* The rtrim() function removes trailing spaces from a string. */
2
3 char * rtrim( char * str )
4 {
5 int n = strlen(str) - 1;
6
7 while( n > 0 )
8 {
9 if( *( str + n ) != ' ' )
10 {
11 //在windows下,将str字符串的某一位设置为值 '\0' 时,会出现异常..
12 *( str + n + 1 ) = '\0';
13 break ;
14 } else {
15 printf("%c, %d\n", str[n], str[n] );
16 n--;
17 }
18 }
19
20 return str;
21 }
在上例中,rtrim()是用户编写的一个函数,它可以删去字符串尾部的空格。函数rtrim()从字符串中位于null字符前的那个字符开始往回检查每个字符,当遇到第一个不是空格的字符时,就将该字符后面的字符替换为null字符。因为在C语言中null字符是字符串的结束标志,所以函数rtrim()的作用实际上就是删去字符串尾部的所有空格。
怎样删去字符串头部的空格?
C语言没有提供可删去字符串头部空格的标准库函数,但是,编写这样的一个函数是很方便的。请看下例:
1 #include <stdio.h>
2 #include <string.h>
3
4 char *ltrim(char * );
5 char *rtrim(char * );
6
7 void main (void)
8 {
9 …
10 }
11
12 char * ltrim(char * str)
13 {
14
15 strrev(str); /* Call strrev to reverse the string. */
16 rtrim(str); /* Call rtrim to remvoe the "trailing" spaces. */
17
18 strrev(str); /* Restore the string's original order. */
19
20 return str ; /* Return a pointer to the string. */
21 }
22
23 char* rtrim(char* str)
24 {
25 …
26 }
在上例中,删去字符串头部空格的工作是由用户编写的ltrim()函数完成的,该函数调用了6.2的例子中的rtrim()函数和标准C库函数strrev()。ltrim()函数首先调用strrev()函数将字符串颠倒一次,然后调用rtrim()函数删去字符串尾部的空格,最后调用strrev()函数将字符串再颠倒一次,其结果实际上就是删去原字符串头部的空格。
怎样打印字符串的一部分?
使用printf()函数打印字符串的任意部分,请看下例:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int main()
6
7 {
8
9 char * source_str = "THIS IS THE SOURCE STRING" ;
10
11 /* Use printfO to print the first 11 characters of source_str. */
12 printf("First 11 characters: ' %11.11s'\n" , source_str);
13
14 /* Use printf() to print only the last 13 characters of source _str. */
15 printf("Last 13 characters:'%13.13s'\n", source_str+(strlen(source_str)-13));
16 }
17
输出结果为:
First 11 characters: 'THIS IS THE'
Last 13 characters:'SOURCE STRING'
在上例中,第一次调用printf()函数时,通过指定参数"%11.11s",迫使printf()函数只打印11个字符的长度,因为源字符串的长度大于11个字符,所以在打印时源字符串将被截掉一部分,只有头11个字符被打印出来。第二次调用printf()函数时,它将源字符串的最后13个字符打印出来,其实现过程为:
(1)用strlen()函数计算出source_str字符串的长度,即strlen(source_str)。
(2)将source_str的长度减去13(13是将要打印的字符数),得出source_str中剩余字符数,且pstrlen(source_str)-13。
(3)将strlen(source_str)-13和source_str的地址相加,得出指向source_str中倒数第13个字符的地址的指针;即source_str+(strlen(source_str)-13)。这个指针就是printf()函数的第二个参数。
(4)通过指定参数“%13.13s”,迫使printf()函数只打印13个字符的长度,其结果实际上就是打印源字符串的最后13个字符。
用malloc()函数更好还是用calloc()函数更好?
函数malloc()和calloc()都可以用来分配动态内存空间,但两者稍有区别。malloc()函数有一个参数,即要分配的内存空间的大小:
void *malloc(size_t size);
calloc()函数有两个参数,分别为元素的数目和每个元素的大小,两个参数的乘积就是要分配的空间的大小:
void *calloc(size_t numElements,size_t sizeOfElement);
如果调用成功,函数malloc()和calloc()都将返回所分配的内存空间的首地址。
malloc()函数和calloc()函数的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题。
calloc()函数会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为零;如果你是为指针类型的元素分配内存,那么这些元素通常(但无法保证)会被初始化为空指针;如果你是为实数类型的元素分配内存,那么这些元素可能(只在某些计算机中)会被初始化为浮点型的零。
malloc()函数和calloc()函数的另一点区别是calloc()函数会返回一个由某种对象组成的数组,但malloc()函数只返回一个对象。为了明确是为一个数组分配内存空间,有些程序员会选用calloc()函数。但是,除了是否初始化所分配的内存空间这一点之外,绝大多数程序员认为以下两种函数调用方式没有区别:
calloc( numElements,sizeOfElement );
malloc( numElements * sizeOfElement );
需要解释的一点是,理论上(按照ANSIC标准)指针的算术运算只能在一个指定的数组中进行,但是在实践中,即使C编译程序或翻译器遵循这种规定,许多C程序还是冲破了这种限制。因此,尽管malloc()函数并不能返回一个数组,它所分配的内存空间仍然能供一个数组使用(对realloc()函数来说同样如此,尽管它也不能返回一个数组)。总之,当你在calloc()函数和malloc()函数之间作选择时,你只需考虑是否要初始化所分配的内存空间,而不用考虑函数是否能返回一个数组。
NULL和NUL有什么不同?
NULL是在<stddef.h>头文件中专门为空指针定义的一个宏。NUL是ASCII字符集中第一个字符的名称,它对应于一个零值。C语言中没有NUL这样的预定义宏。注意:在ASCII字符集中,数字0对应于十进制值80,不要把数字0和'\0'(NUL)的值混同起来。
NULL可以被定义为(void *)0,而NUL可以被定义为'\0'。NULL和NUL都可以被简单地定义为0,这时它们是等价的,可以互换使用,但这是一种不可取的方式。为了使程序读起来更清晰,维护起来更容易,你在程序中应该明确地将NULL定义为指针类型,而将NUL定义为字符类型。
在程序退出main()函数之后,还有可能执行一部分代码吗?
可以,但这要借助C库函数atexit()。利用atexit()函数可以在程序终止前完成一些“清理”工作——如果将指向一组函数的指针传递给atexit()函数,那么在程序退出main()函数后(此时程序还未终止)就能自动调用这组函数。在使用atexit()函数时你要注意这样两点:
第一: 由atexit()函数指定的要在程序终止前执行的函数要用关键字void说明,并且不能带参数;
第二: 由atexit()函数指定的函数在入栈时的顺序和调用atexit()函数的顺序相反,即它们在执行时遵循后进先出(LIFO)的原则。
1 #include<stdlib.h>
2 #include<stdio.h>
3
4 void my_exit1(void)
5 {
6 printf("my_exit1() function !\n");
7 }
8
9 void my_exit2(void)
10 {
11 printf("my_exit2() function !\n");
12 }
13
14
15 int main()
16 {
18 atexit ( my_exit1 );
19 atexit ( my_exit2 );
20 printf("now, eixt this program\n");
21 exit(0);
22 }
输出结果为:
now, eixt this program...
my_exit2() function !
my_exit1() function !
数组作为函数的常数时,可以通过sizeof运算符得到函数数组的大小吗?
不可以。当把数组作为函数的参数时,你无法在程序运行时通过数组参数本身告诉函数该数组的大小,因为函数的数组参数相当于指向该数组第一个元素的指针。这意味着把数组传递给函数的效率非常高,也意味着程序员必须通过某种机制告诉函数数组参数的大小。为了告诉函数数组参数的大小,人们通常采用以下两种方法:
第一种方法是将数组和表示数组大小的值一起传递给函数,例如memcpy()函数就是这样做的:
memcpy( dest,source,length );
第二种方法是引入某种规则来结束一个数组,例如在C语言中字符串总是以ASCII字符NUL('\0')结束,而一个指针数组总是以空指针结束。请看下述函数,它的参数是一个以空指针结束的字符指针数组,这个空指针告诉该函数什么时候停止工作:
1 void printMany( char *strings[] )
2 {
3 int i = 0;
4 while( strings[i] != NULL )
5 {
6 puts(strings[i]);
7 ++i;
8 }
9 }
正象9.5中所说的那样,C程序员经常用指针来代替数组下标,因此大多数C程序员通常会将上述函数编写得更隐蔽一些:
1 void printMany( char *strings[] )
2 {
3 while( *strings )
4 {
5 puts(*strings++);
6 }
7 }
尽管你不能改变一个数组名的值,但是strings是一个数组参数,相当于一个指针,因此可以对它进行自增运算,并且可以在调用puts()函数时对strings进行自增运算 [注意辨别这种情况:对“int array[10] 数组的array不能进行自加运算”]。
array_name和&array_name有什么不同?
前者是指向数组中第一个元素的指针,后者是指向整个数组的指针。注意: 笔者建议读者读到这里时暂时放下本书,写一下指向一个含MAX个元素的字符数组的指针变量的说明。希望你不要敷衍了事,因为只有这样你才能真正了解C语言表示复杂指针的句法的奥秘。下文将介绍如何获得指向整个数组的指针。
数组是一种类型,它有三个要素,即基本类型(数组元素的类型),大小(当数组被说明为不完整类型时除外),数组的值(整个数组的值)。你可以用一个指针指向整个数组的值:
char a[MAX]; /*array of MAX characters*/
char *p = a;
char *pa = &a;
在运行了上述这段代码后,你就会发现p和pa的打印结果是一个相同的值,即p和pa指向同一个地址。但是,p和pa指向的对象是不同的。
上述定义和以下定义是相同的,它们的含义都是“ap是一个含MAX个字符指针的数组”: char *ap[MAX].
以下这种定义并不能获得一个指向整个数组的值的指针:char *(ap[MAX]).
怎样判断一个字符是数字、字母或其它类别的符号?
在头文件ctype.h中定义了一批函数,它们可用来判断一个字符属于哪一类别。下面列出了这些函数:
---------------------------------------------------------------------------------------
函数 字符类别 返回非零值的字符
---------------------------------------------------------------------------------------
isdigit() 十进制数 0--9
isxdigit() 十六进制数 0--9,a—f,或A--F
isalnum() 字母数字符号 0--9,a--Z,或A--Z
isalpha() 字母 a—z或A--Z
islower() 小写字母 a—z
isupper() 大写字母 A--Z
isspace() 空白符 空格符,水平制表符,垂直制表符,换行符,换页符,或回车符
isgraph() 非空白字符 任何打印出来不是空白的字符(ASCII码从21到7E)
isprint() 可打印字符 所有非空白字符,加上空格符
ispunct() 标点符 除字母数字符号以外的所有非空白字符
iscntrl() 控制字符 除可打印字符外的所有字符(ASCII码从00到1F,加上7F)
----------------------------------------------------------------------------------------
与前文提到过的使用标准库函数的好处相似,调用上述这些宏而不是自己编写测试字符类别的程序也有三点好处。首先,这些宏运算速度快,因为它们的实现方式通常都是利用位屏蔽技术来检查一个表,所以即使是进行一项相当复杂的检查,也比真正去比较字符的值要快得多。其次,这些宏都是正确的。如果你自己编写一个测试程序,你很容易犯逻辑上或输入上的错误,例如引入了一个错误的字符(或漏掉了一个正确的字符)。第三,这些宏是可移植的。信不信由你,并非所有的人都使用同样的含PC扩充字符的ASCII字符集。也许今天你还不太在意,但是,当你发现你的下一台计算机使用的是Unicode字符集而不是ASCII字符集,你就会庆幸自己原来没有按照字符集中的字符值来编写程序。
其他字符转换函数:
isascii(测试字符是否为ASCII 码字符)
int isascii(int c);
检查参数c是否为ASCII码字符,也就是判断c的范围是否在0到127之间。若参数c为ASCII码字符,则返回TRUE,否则返回NULL(0)。
toascii(将整型数转换成合法的ASCII 码字符)
int toascii(int c);
toascii()会将参数c转换成7位的unsigned char值,第八位则会被清除,此字符即会被转成ASCII码字符。将转换成功的ASCII码字符值返回。
tolower(将大写字母转换成小写字母)
int tolower(int c);
若参数c为大写字母则将该对应的小写字母返回。返回转换后的小写字母,若不须转换则将参数c值返回。
toupper(将小写字母转换成大写字母)
int toupper(int c);
若参数c为小写字母则将该对映的大写字母返回。返回转换后的大写字母,若不须转换则将参数c值返回。
以isalmun为例,说明这些函数的用法[剩余的其他函数跟它类似]:
int isalnum ( int c )
检查参数c是否为英文字母或阿拉伯数字,若参数c为字母或数字,则返回TRUE,否则返回NULL。此为宏定义,非真正函数。
1 #include<stdio.h>
2 #include <ctype.h>
3
4 int main()
5 {
6 char str[]="123c@#FDsP[e?";
7 int i;
8 for( i = 0; str[i] != 0; i++ )
9 {
10
11 if( isalnum( str[i] ) )
12 printf("%c is an alphanumeric character\n", str[i] );
13 }
14
15 }