0
#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); 或者其他的东西。

4

4 回答 4

5

我真的需要分配arr吗?

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

于 2013-02-06T11:48:29.030 回答
0

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

在现代操作系统/编译器中, Achar *将占用至少 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 添加的换行符,否则有人会抱怨这一点,我敢肯定。

于 2013-02-06T12:04:41.937 回答
0
  • 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()的那个随机位置执行 and ,但是另一个程序可以覆盖该数据。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", &arr)。而 printf 的 "%s" 具体需要一个字符数组(一个指向字符串的指针),这又是什么arr,所以不需要尊重。

于 2013-02-06T12:10:50.200 回答
0

我真的需要分配arr吗?

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

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

我通常建议malloc调用写成

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

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

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

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

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

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

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


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

于 2013-02-06T18:26:27.583 回答