C语言内部静态成员陷阱

在C语言中,我们知道当函数返回时,其栈上的内存会随着函数出栈而释放,但是我们有时需要返回一块函数内部可以处理,而函数外面仍然有效的内存。大体来说有如下几种方法:

1)在函数内部通过malloc在堆上分配内存,然后把这块内存返回。但是这将带来潜在的安全隐患,如内存泄露或多次释放导致程序崩溃。

2)由函数外部传入一块内存,函数内部的数据处理可以在该内存块上完成。让内存由外部程序维护,比较简显直观,且相对安全,但稍显麻烦。

3)函数内部定义static变量,即便函数返回仍然有效。既不用使用堆上的内存,也不需用户传入buffer和其长度,故简洁易用。

这里,我想对第三种方法进行一些讨论。使用static内存这个方法看似不错,但是它有让你想象不到的陷阱。见如下代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static char *st(int n)
{
    static char buf[20];
    memset(buf, 0, sizeof buf);
    sprintf(buf, "n = %d\n", n);
    return buf;
}

int main(void)
{
    printf("%s%s", st(10), st(20));
    return 0;
}
运行结果有点出乎意料,显示两个 n = 10。接下来,分析一下为何会如此。
想象一下C的运行机理,对于main中的printf调用,一共有三个参数,通常 从右往左入栈 ,即先计算
st(20)的值入栈,再计算st(10)的值入栈,最后将字符串"%s%s"地址入栈,然后call printf完成调用,如果是这样那就明白了,因为两次st调用都返回的是st内部static变量buf的地址,因此在printf的调用中入栈的前两个参数都是相同的地址,而且 第二次调用st即st(10)会修改缓冲区覆盖前一次的修改结果st(20 ),这样最后就会显示出两个 n = 10。如果还觉得不踏实,我们可以从汇编层面来分析其内部机理。

同样的问题可能在很多地方都会遇到,如ctime函数利用内部静态存储的方式保存时间字符串并返回其地址,还有inet_ntoa函数同样如此,我们在调用类似的函数时需要注意一下,如果有必要,可以将其拷贝到另外一块缓冲区中再使用

你可能感兴趣的:(C语言内部静态成员陷阱)