动态内存分配(Malloc) VS 指定大小的字符数组

4

首先,我对C语言还很陌生,所以可能存在一些基本的误解。给定下面的代码:

int main()
{
    char ack[100];
    char *bar;
    bar = malloc(100);
    strncpy(ack, "testing", 7);    
    bar = "testing";

    return 0;
}

ack 被 gdb 检查时,看起来像这样:

(gdb) p ack
$1 = "testing\000\360WV\000\000\000\000\000\277\000\000\000\000\000\000      
'\000'\000\220\005@",<repeats 13 times>, "\003\004@", '\000' <repeats 13 times>    
"\325,\005@\000\000\000\000\000H\214\246\367\377\177\000\000\220\005@",
'\000' <repeats 13 times>,     "P\004@\000\000\000\000\000\360\342\377\377"` 

考虑到我如何初始化ack,这对我来说是有意义的。但我不太明白为什么bar看起来像这样:

(gdb) p bar
$2 = 0x40066c "testing"

我分配了与请求ack的空间相同的存储空间(据我所知),但是malloc没有附带额外的东西。据我所知,malloc不执行任何数据初始化或其他操作,因此我有点困惑。出现这种情况的原因是我在使用strstr时遇到了问题。基本上,当我从文件(fgets)中读取数据到一个具有特定大小的char数组中时,strstr()将失败(我认为这是因为有“垃圾”导致的)。使用指针和malloc'd内存正常工作。无论如何,我有几个特定的问题。
  1. 这个malloc的变量的行为是否符合预期?这里是否进行了某些优化(我使用gcc编译,但没有进行任何优化),还是gdb没有显示给我所有内容?该变量是否应该有“垃圾”?
  2. 我使用malloc正确吗?我是否应该初始化我请求的所有内存?如果是,请问如何初始化?
谢谢!
编辑 感谢所有回答我的人!我从你们那里学到了很多东西,非常感谢。我现在看到了我上面发布的代码的问题,以及我在使用fgets()和strstr()时遇到的原始问题。

当内存被分配时,它并不会被填充为null。为了解决这个问题,可以使用memset(..)函数。 - Adrian
2
@Adrian,不,不,不。这是C语言。不要将malloc()强制转换,并且不要教新手在C中这样做。 - Dan Fego
@DanFego 我也在学习哈哈。我一直使用malloc的强制类型转换。谢谢信息,我已经编辑了我的评论! - Adrian
2
@Adrian 抱歉我刚才大声喊叫了!我看到这里有很多人建议这样做,我不想让我们教初学者也这么做。 :P - Dan Fego
2
@Adrian:在 C89 之前,malloc 返回的是 char *,所以在那些年代,强制转换是必要的。当然,任何当时编写的教程或参考资料都会显示这一点。随着 C89 的到来,malloc 被改为返回 void *,可以隐式地转换为任何对象指针类型,因此强制转换现在是多余的(实际上可能会隐藏错误)。不幸的是,许多旧的参考资料没有更新以反映这一变化,并且这种做法仍然存在。 - John Bode
4个回答

10
bar = "testing";

重新赋值指针 bar 以指向保存字符串"testing"的静态缓冲区,也就是它不再指向你使用malloc分配的数组。这是一个内存泄漏问题。

如果要把字符串放入malloc分配的缓冲区,请使用像你对ack所做的那样的strcpystrncpymemcpy函数。


2
这是因为在赋值时,你(无意中)丢弃了用malloc分配的内存。
bar = "testing";

这使得bar指向字符串字面量,而不再指向(泄漏的)malloc分配的内存。


嗯,“throw away”有意图的内涵,所以这是“forget about”的一个优点。另一方面,“forget about”对于内存泄漏来说不够夸张。我做了个妥协。 - Daniel Fischer
太好了!我们还可以接受“失落”,“泄漏”,“掉落”,“忽略”和“将其作为太空垃圾丢弃”。 - cdeszaq

0

你有几个地方错了。

首先,当打印ack后面的垃圾时,原因是你没有将其NULL结尾。在C中,所有字符串都必须以\0结尾。你可以通过调用长度为8(strlen("testing") + 1)的strncpy()或者直接使用strcpy()来解决这个问题。

其次,bargdb中看起来不同,因为它是指向char的指针,而ack是一个普通的char数组。gdb会显示bar的指针值,并且如果可能的话,还会显示指针另一侧的内容。当你执行bar = "testing;"时,你正在重新分配bar指向的字符数组,该数组在内存中包含“testing”。当你使用这样的字符串字面量时,它自动在末尾添加\0字符,因此是NULL结尾的,因此不会有垃圾数据。


-1

C字符串有一个空终止符(ASCII字符值为零),因此C字符串“testing”实际上包括终止符在内的8个字节。

当您打印原始值时,它会在达到终止符时停止打印。

但是,因为您只复制了前7个字符,所以当您打印第二个值时,它会一直打印垃圾数据,直到达到之前存在的垃圾数据中的零。


再说,也许是丹尼尔的解释;-) 但从技术上讲,我认为我不应该被踩。 - Nick Lockwood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接