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函数同样如此,我们在调用类似的函数时需要注意一下,如果有必要,可以将其拷贝到另外一块缓冲区中再使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C 语言中,静态成员(即静态变量和静态函数)可以通过类名和对象名来访问。但是在 Java 中,静态成员只能通过类名来访问,不能通过对象名来访问。 在 C 语言中,静态成员在内存中只有一份副本,无论创建多少个对象,都共享这份副本。因此,可以通过类名和对象名来访问静态成员变量和静态成员函数。例如: ```c #include <stdio.h> struct Student { int id; char name[20]; static int count; // 静态成员变量 static void printCount() { // 静态成员函数 printf("count = %d\n", count); } }; int Student::count = 0; // 静态成员变量初始化 int main() { Student s1, s2; s1.id = 1001; s2.id = 1002; Student::count = 2; // 通过类名访问静态成员变量 s1.count = 3; // 通过对象名访问静态成员变量 s1.printCount(); // 通过对象名访问静态成员函数 Student::printCount(); // 通过类名访问静态成员函数 return 0; } ``` 在 Java 中,静态成员变量和静态成员方法属于类本身,而不是对象实例。因此,只能通过类名来访问静态成员,不能通过对象名来访问。例如: ```java class Student { int id; String name; static int count; // 静态成员变量 static void printCount() { // 静态成员方法 System.out.println("count = " + count); } } int main() { Student s1 = new Student(); Student s2 = new Student(); s1.id = 1001; s2.id = 1002; Student.count = 2; // 通过类名访问静态成员变量 // s1.count = 3; // 错误:不能通过对象名访问静态成员变量 s1.printCount(); // 错误:不能通过对象名访问静态成员方法 Student.printCount(); // 通过类名访问静态成员方法 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值