从现在开始我们要用到很多库函数,在学习每个库函数时一定要看Man Page。Man Page随时都在我们手边,想查什么只要敲一个命令就行,然而很多初学者就是不喜欢看Man Page,宁可满世界去查书、查资料,也不愿意看Man Page。据我分析原因有三:
-
英文不好。那还是先学好了英文再学编程吧,否则即使你把这本书都学透了也一样无法胜任开发工作,因为你没有进一步学习的能力。
-
Man Page的语言不够友好。Man Page不像本书这样由浅入深地讲解,而是平铺直叙,不过看习惯了就好了,每个Man Page都不长,多看几遍自然可以抓住重点,理清头绪。本节分析一个例子,帮助读者把握Man Page的语言特点。
-
Man Page通常没有例子。描述一个函数怎么用,一靠接口,二靠文档,而不是靠例子。函数的用法无非是本章所总结的几种模式,只要把本章学透了,你就不需要每个函数都得有个例子教你怎么用了。
总之,Man Page是一定要看的,一开始看不懂硬着头皮也要看,为了鼓励读者看Man Page,本书不会像[K&R]那样把库函数总结成一个附录附在书后面。现在我们来分析strcpy(3)
。
图 24.1. strcpy(3)
![strcpy(3)](https://i-blog.csdnimg.cn/blog_migrate/ba3f5494cb418c33385a325dff7b6d13.jpeg)
这个Man Page描述了两个函数,strcpy
和strncpy
,敲命令man strcpy
或者man strncpy
都可以看到这个Man Page。这两个函数的作用是把一个字符串拷贝给另一个字符串。SYNOPSIS部分给出了这两个函数的原型,以及要用这些函数需要包含哪些头文件。参数dest
、src
和n
都加了下划线,有时候并不想从头到尾阅读整个Man Page,而是想查一下某个参数的含义,通过下划线和参数名就能很快找到你关心的部分。
dest
表示Destination,src
表示Source,看名字就能猜到是把src
所指向的字符串拷贝到dest
所指向的内存空间。这一点从两个参数的类型也能看出来,dest
是char *
型的,而src
是const char *
型的,说明src
所指向的内存空间在函数中只能读不能改写,而dest
所指向的内存空间在函数中是要改写的,显然改写的目的是当函数返回后调用者可以读取改写的结果。因此可以猜到strcpy
函数是这样用的:
至于strncpy
的参数n
是干什么用的,单从函数接口猜不出来,就需要看下面的文档。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
在文档中强调了strcpy
在拷贝字符串时会把结尾的'\0'
也拷到dest
中,因此保证了dest
中是以'\0'
结尾的字符串。但另外一个要注意的问题是,strcpy
只知道src
字符串的首地址,不知道长度,它会一直拷贝到'\0'
为止,所以dest
所指向的内存空间要足够大,否则有可能写越界,例如:
如果没有保证src
所指向的内存空间以'\0'
结尾,也有可能读越界,例如:
因为strcpy
函数的实现者通过函数接口无法得知src
字符串的长度和dest
内存空间的大小,所以“确保不会写越界”应该是调用者的责任,调用者提供的dest
参数应该指向足够大的内存空间,“确保不会读越界”也是调用者的责任,调用者提供的src
参数指向的内存应该确保以'\0'
结尾。
此外,文档中还强调了src
和dest
所指向的内存空间不能有重叠。凡是有指针参数的C标准库函数基本上都有这条要求,每个指针参数所指向的内存空间互不重叠,例如这样调用是不允许的:
strncpy
的参数n
指定最多从src
中拷贝n
个字节到dest
中,换句话说,如果拷贝到'\0'
就结束,如果拷贝到n
个字节还没有碰到'\0'
,那么也结束,调用者负责提供适当的n
值,以确保读写不会越界,比如让n
的值等于dest
所指向的内存空间的大小:
然而这意味着什么呢?文档中特别用了Warning指出,这意味着dest
有可能不是以'\0'
结尾的。例如上面的调用,虽然把"hello world"
截断到10个字符拷贝至buf
中,但buf
不是以'\0'
结尾的,如果再printf(buf)
就会读越界。如果你需要确保dest
以'\0'
结束,可以这么调用:
strncpy
还有一个特性,如果src
字符串全部拷完了不足n
个字节,那么还差多少个字节就补多少个'\0'
,但是正如上面所述,这并不保证dest
一定以'\0'
结束,当src
字符串的长度大于n
时,不但不补多余的'\0'
,连字符串的结尾'\0'
也不拷贝。strcpy(3)
的文档已经相当友好了,为了帮助理解,还给出一个strncpy
的简单实现。