C语言的本质——C语言的函数接口
函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能。
函数接口通过函数名,参数和返回值来描述这个协议,只要函数名和参数名命名合理,参数和返回值的类型定义的准确,调用者仅仅通过函数接口就能知道函数的用法。当函数接口不能表达函数的全部语义时,文档就起了重要的补充作用,函数文档的写法我们可以参照Linux下的ManPage或MSDN。
下面通过分析C标准库函数来说明函数接口:
在Linux终端下敲命令manstrcpy就可以看到下面的ManPage。
这个ManPage描述了两个函数,strcpy和strncpy,这两个函数的作用是把一个字符串拷贝给另一个字符串。SYNOPSIS部分给出了这两个函数的原型,以及要使用这些函数需要包含哪些头文件。参数通达信下单接口下载,dest、src和n都加了下划线,有时候并不想从头到尾阅读整个ManPage,而是想查一下某个参数的含义,通过下划线和参数名就能很快找到我们关心的部分。
通达信下单接口下载,dest表示Destination,src表示Source,看名字就能猜到是把src所指向的字符串拷贝到通达信下单接口下载,dest所指向的内存空间。这一点从两个参数的类型也能看出来,通达信下单接口下载,dest是char*型的,而src是constchar*型的,说明src所指向的内存空间在函数中只能读不能改写,而通达信下单接口下载,dest所指向的内存空间在函数中是要改写的,显然改写的目的是当函数返回后调用者可以读取改写的结果。通过ManPage我们可以推测到strcpy函数是这样用的:
char buf[10];
strcpy(buf,'hello');
printf(buf);
那么strncpy的参数n是干什么用的呢?单从函数接口无法推测,我们继续看下面的文档。
在文档中强调了strcpy在拷贝字符串时会把结尾的' '也拷到通达信下单接口下载,dest中,因此保证了通达信下单接口下载,dest中是以' '结尾的字符串。但另外一个要注意的问题是,strcpy只知道src字符串的首地址,不知道长度,它会一直拷贝到' '为止,所以通达信下单接口下载,dest所指向的内存空间要足够大,否则有可能写越界,例如:
char buf[10];
strcpy(buf,'hello world');
char buf[10] ='abcdefghij', str[4] = 'hell';
strcpy(buf,str);
此外,文档中还强调了src和通达信下单接口下载,dest所指向的内存空间不能有重叠。凡是有指针参数的C标准库函数基本上都有这条要求,每个指针参数所指向的内存空间互不重叠,例如这样调用是不允许的:
char buf[10] ='hello';
strcpy(buf,buf+1);
char buf[10];
strncpy(buf,'hello world', sizeof(buf));
那么这意味着什么呢?文档中特别用了Warning指出,这意味着通达信下单接口下载,dest有可能不是以' '结尾的。例如上面的调用,虽然把'helloworld'截断到10个字符拷贝至buf中,但buf不是以' '结尾的,如果再printf(bu就会读越界。如果你需要确保通达信下单接口下载,dest以' '结束,可以这么调用:
char buf[10];
strncpy(buf,'hello world', sizeof(buf));
buf[sizeof(buf)-1]= ' ';
strncpy还有一个特性,如果src字符串全部拷完了不足n个字节,那么还差多少个字节就补多少个' ',但是正如上面所述,这并不保证通达信下单接口下载,dest一定以' '结束,当src字符串的长度大于n时,不但不补多余的' ',连字符串的结尾' '也不拷贝。下面文档非常友好,为了帮助理解,还给出一个strncpy的简单实现。
函数的ManPage都有一部分专门讲返回值的。这两个函数的返回值都是通达信下单接口下载,dest指针。可是为什么要返回通达信下单接口下载,dest指针呢?通达信下单接口下载,dest指针本来就是调用者传过去的,再返回一遍通达信下单接口下载,dest指针并没有提供任何有用的信息。之所以这么规定是为了把函数调用当作一个指针类型的表达式使用,比如printf(strcpy(buf,'hello')),一举两得,如果strcpy的返回值是void就没有这么方便了。
CONFORMINGTO部分描述了这个函数是遵照哪些标准实现的。strcpy和strncpy是C标准库函数,当然遵照C99标准。以后我们还会看到libc中有些函数属于POSIX标准但并不属于C标准,例如write。
NOTES部分给出一些提示信息。这里指出如何确保strncpy的通达信下单接口下载,dest以' '结尾,和我们上面给出的代码类似,但由于n是个变量,在执行buf[n-1]=' ';之前先检查一下n是否大于0,如果n不大于0,buf[n-1]就访问越界了,所以要避免。
BUGS部分说明了使用这些函数可能引起的Bug,这部分一定要仔细看。用strcpy比用strncpy更加不安全,如果在调用strcpy之前不仔细检查src字符串的长度就有可能写越界,这是一个很常见的错误,例如:
void foo(char*str)
{
char buf[10];
strcpy(buf, str);
......
}
str所指向的字符串有可能超过10个字符而导致写越界,在第4节“段错误”我们看到过,这种写越界可能当时不出错,而在函数返回时出现段错误,原因是写越界覆盖了保存在栈帧上的返回地址,函数返回时跳转到非法地址,因而出错。像buf这种由调用者分配并传给函数读或写的一段内存通常称为缓冲区,缓冲区写越界的错误称为缓冲区溢出。如果只是出现段错误那还不算严重,更严重的是缓冲区溢出Bug经常被恶意用户利用,使函数返回时跳转到一个事先设好的地址,执行事先设好的指令,如果设计得巧妙甚至可以启动一个Shell,然后随心所欲执行任何命令,可想而知,如果一个用root权限执行的程序存在这样的Bug,被攻陷了,后果将会非常严重。
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点