c 数组初始化问题

话说上次修改 icecast server 的代码时,看到下面一段代码

1
2
3
4
5
6
if (plugin->contenttype == NULL) {
/* We default to MP3 audio for old clients without content types */
plugin->contenttype = "audio/mpeg";
/* plugin->contenttype = "audio/mpeg"; */ // 是否可以不注释?
}
plugin->contenttype = "audio/aac";

如果是写 java 的话, 很明显,上面地方可以不注释, plugin->contenttype 相当于被赋值了2次,
但是由于是 c 代码, 不注释看着感觉有种会造成内存泄漏的感觉。

于是, 下面证明一下。

首先, 上一段测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char *returnStr1(){
char *p="hello world!";

printf("str1: %p\n", p);
return p;
}
char *returnStr2(){
char p[5]={'h','e','l','l', '\0'};

printf("str2: %p\n", p);
return p;
}

int main(int argc, char *argv[]) {
printf("1:%s\n", returnStr1());
printf("2:%s\n", returnStr2());
}

首先, 对于 c 来说, 字符串有上面 2 种创建方式,(应该有能多钟, 不过我看的c代码大都只有以上2种)

由于程序的内存区域分 栈,堆, 代码区 。。。 所以最简单的方式就是直接打印她们的内存地址。

结果如下

1
2
3
4
str1: 0x100000f71
1:hello world!
str2: 0x104801f6b
2:

很明显, 她们不在一个区, 网上查了下,栈区地址高,堆区地址低,所以

str1 的写法是创建在 的。

str2 的写法是创建在 的。

而向 申请的空间在离开函数后就释放了。

很明显, 下面没打印出来。

ps, 其实, 用 clang 编译警告也能看出。

1
2
3
memory_leak.c:28:12: warning: address of stack memory associated with local variable 'p' returned [-Wreturn-stack-address]
return p;
^

但是

网上也查了下, 结果发现并不是。。。

可以试试看 这么写:

1
2
3
4
5
6
7
char *returnStr1(){
char *p="hello world!";
*p="hello world!";

printf("str1: %p\n", p);
return p;
}

编译会警告

1
2
3
memory_leak.c:20:7: warning: incompatible pointer to integer conversion assigning to 'char' from 'char [13]' [-Wint-conversion]
*p="hello world!";
^~~~~~~~~~~~~~~

运行的时候会报错。

1
2
name: 0x7fc99b4033d0
[1] 25697 bus error ./a.out

原来, 其实 char *p="hello world!"; 这种写法相当于 const char *p="hello world!";

他其实是 创建了一个常量,然后将指针指向了他。 而这个常量是存储在 initialized data 区。 不可变的。

这里盗下图

结论

所以, 其实最上面的代码并不会造成内存泄漏, 而因为对常量重赋值直接报错。 (ps 在gcc 中会报 sengment error)

reference

http://stackoverflow.com/questions/19656025/why-is-char-on-the-stack-but-char-on-the-heap

https://en.wikipedia.org/wiki/Data_segment

avatar

lelouchcr's blog