【问题标题】:Why do I need to allocate memory?为什么我需要分配内存?
【发布时间】:2013-02-06 11:47:08
【问题描述】:
#include<stdio.h>
#include<stdlib.h>

void main()
{
char *arr;
arr=(char *)malloc(sizeof (char)*4);
scanf("%s",arr);
printf("%s",arr);
}

在上面的程序中,我真的需要分配 arr 吗? 即使不使用 malloc,它也会给我结果。 我的第二个疑问是'我期待第 9 行出现错误,因为我认为它一定是 printf("%s",*arr); 什么的。

【问题讨论】:

    标签: c arrays pointers malloc printf


    【解决方案1】:

    我真的需要分配 arr 吗?

    是的,否则您将取消引用未初始化的指针(即写入随机的内存块),这是未定义的行为

    【讨论】:

      【解决方案2】:

      我真的需要分配 arr 吗?

      您需要将arr 设置为指向您拥有的内存块,方法是调用malloc 或将其设置为指向另一个数组。否则,它指向一个随机内存地址,您可能访问也可能无法访问。

      在 C 中,不鼓励转换 malloc 的结果1;这是不必要的,在某些情况下,如果您忘记包含 stdlib.h 或在范围内没有 malloc 的原型,则可能会掩盖错误。

      我通常建议 malloc 调用写成

      T *ptr = malloc(N * sizeof *ptr);
      

      其中T 是您使用的任何类型,N 是您要分配的该类型元素的数量。 sizeof *ptr 等价于 sizeof (T),因此如果您更改了 T,则无需在 malloc 调用本身中复制该更改。只是少了一个维护头痛。

      即使不使用 malloc,它也会给我结果

      因为你没有在声明中显式初始化,所以arr的初始值为indeterminate2;它包含一个随机位串,该位串可能对应也可能不对应于有效的可写地址。尝试通过无效指针读取或写入的行为是undefined,这意味着编译器没有义务警告您您正在做一些危险的事情。未定义行为的一个可能结果是您的代码似乎按预期工作。在这种情况下,您似乎正在访问一个随机的内存段,该段恰好是可写的并且不包含任何重要的内容。

      我的第二个疑问是'我期待第 9 行出现错误,因为我认为它必须是 printf("%s",*arr);什么的。

      %s 转换说明符告诉printf 对应的参数是char * 类型,所以printf("%s", arr); 是正确的。如果您使用了%c 转换说明符,那么是的,您需要使用* 运算符或下标取消引用arr,例如printf("%c", *arr);printf("%c", arr[i]);

      此外,除非您的编译器文档明确将其列为有效签名,否则您应该main 定义为void main();请改用int main(void)int main(int argc, char **argv)


      1。 C++ 中需要强制转换,因为 C++ 不允许您在没有显式强制转换的情况下将 void * 值分配给其他指针类型
      2。这适用于在块范围内声明的指针。在文件范围(在任何函数之外)或使用 static 关键字声明的指针被隐式初始化为 NULL。

      【讨论】:

        【解决方案3】:

        就个人而言,我认为这是分配内存的一个非常糟糕的例子。

        char * 在现代操作系统/编译器中将占用至少 4 个字节,在 64 位机器上占用 8 个字节。因此,您使用四个字节来存储三个字符串的四个字节的位置。不仅如此,malloc 还会产生开销,这可能会给实际分配的内存增加 16 到 32 个字节。因此,我们使用 20 到 40 个字节来存储 4 个字节。这比实际需要的多5-10倍。

        代码还强制转换 malloc,这在 C 语言中是错误的。

        缓冲区中只有四个字节,scanf 溢出的可能性很大。

        最后,没有调用free 将内存返回给系统。

        使用起来会更好:

        int len;
        char arr[5];
        fgets(arr, sizeof(arr), stdin);
        len = strlen(arr);
        if (arr[len] == '\n') arr[len] = '\0';
        

        这不会使字符串溢出,并且只使用 9 字节的堆栈空间(不包括任何填充...),而不是 4-8 字节的堆栈空间和更多的堆空间。我在数组中添加了一个额外的字符,以便它允许换行。还添加了代码以删除 fgets 添加的换行符,否则有人会抱怨这一点,我敢肯定。

        【讨论】:

        • 在 C 中正确地转换 malloc... 你可以在你想要的时候投射你想要的东西。在逻辑上错误可能没有用处,但它不可能是错误的perse
        • 没有必要(malloc返回void*,在C语言中可以转换成任何其他指针),并且可以隐藏诸如“不声明malloc就意味着返回int,从而切断指针”等问题在 640 位机器上减半”。
        【解决方案4】:
        • In the above program, do I really need to allocate the arr?

        你敢打赌。


        • It is giving me the result even without using the malloc.

        当然,这完全有可能...arr 是一个指针。它指向一个内存位置。在你对它做任何事情之前,它是未初始化的......所以它指向一些随机内存位置。这里的关键是它指向的地方是你的程序不能保证拥有的地方。这意味着您可以只执行scanf(),然后在arr 指向的那个随机位置上的值会消失,但是另一个程序可以覆盖该数据。

        当您说malloc(X) 时,您是在告诉计算机您需要 X 字节的内存供您自己使用,而其他人无法触及。然后,当arr 捕获数据时,它将安全地在那里供您使用,直到您调用free()顺便说一句,您忘记在程序中执行此操作

        这是一个很好的例子,说明为什么在创建指针时应该始终将指针初始化为NULL...它提醒您,您不拥有它们所指向的东西,您最好将它们指向有效的东西在使用它们之前。


        • I am expecting an error in 9th line because I think it must be printf("%s",*arr)

        不正确。 scanf() 想要一个地址,这就是 arr 所指向的,这就是为什么你不需要这样做:scanf("%s", &amp;arr)。而 printf 的 "%s" 具体需要一个字符数组(指向字符串的指针),这又是 arr 是什么,所以不需要尊重。

        【讨论】:

        • " 这里的关键是它指向的地方是你的程序不拥有的地方。"并不真地。由于它是一个随机位置,因此您很可能拥有该位置。当它碰巧指向与您的指令指针相同的位置时,太糟糕了。
        • @Cubic - 语义,但你是对的。 “不保证拥有”是一种更好的表达方式。
        猜你喜欢
        • 2019-04-20
        • 1970-01-01
        • 2013-10-21
        • 2019-04-09
        • 2016-12-02
        • 2014-12-07
        • 1970-01-01
        • 2020-05-20
        • 2013-08-15
        相关资源
        最近更新 更多