C字符指针

4

假设我们有一个指针数组:

char *ptr[30];

现在它能够正常工作,并且没有出现任何意外情况!我可以很容易地输入名称。

scanf("%s", ptr[1]);
scanf("%s", ptr[2]);
scanf("%s", ptr[3]);

printf("%s\n", ptr[1]);
printf("%s\n", ptr[2]);
printf("%s\n", ptr[3]);

我的问题是,如果指针可以用这种方式来存储无数个名称,那么为什么要使用malloc?在这种情况下,ptr [1] 不指向输入中的字符,而是指向一个新的输入。 例如,如果ptr有mukul,则ptr [1] 应该指向'u',如果在声明指针时没有分配空间,限制是什么?

7
另外,需要注意的是数组中的第一个元素是ptr[0]。 - SirDarius
尝试在 scanfprintf 之间做一些事情。你应该会看到一些未定义的行为。 - stanwise
是的,ptr[0]肯定在那里!感谢您的回复! - Mukul Shukla
4个回答

13

这种方式无法使用指针。有时候由于纯粹的偶然,它可能会“工作”,但是在没有为指针分配内存的情况下使用指针将导致未定义行为,这意味着您的程序可能表现不稳定并给出意想不到的结果。

请记住,仅因为您的程序编译并运行并不意味着它是正确的。在C语言中,存在一整类称为“未定义行为”的错误,其中许多错误编译器都会默默地放过而不发出警告。这些错误包括覆盖缓冲区、使用未初始化的变量或对未合法分配内存块的指针进行引用。表现出未定义行为的程序有时甚至可能正常工作,但通常非常不稳定且容易崩溃。


2
声明 char *ptr[30] 表示有 30 个字符指针,即它可以指向 30 个不同的字符,因为它是一个指针数组,或者它可以指向 30 个不同的字符数组(在这种情况下是名称)。 - Mukul Shukla
这里应该指向30个不同的字符,但是它被用来指向30个不同的名称,这正确吗? - Mukul Shukla
1
@Mukul,在C语言中,char*通常指向一个以空字符结尾的字符数组或字符序列(也称为“字符串”)。因此,char *ptr[30]是一个包含30个字符指针的数组,或者你可以称之为一个包含30个“字符串”的数组。请注意,char*不一定要指向一个以空字符结尾的字符数组 - 它也可以指向单个字符。但在常见用法中,char*指向一个以空字符结尾的字符串。 - Charles Salvia

4
如果您使用示例中的内容,您只会覆盖在ptr数组之后的其他位置。大多数编译器实际上应该至少给出一个警告。在大多数系统上,您的程序将崩溃,您只是非常幸运。

3
当您定义指针时,例如:
char *ptr = 0; // NULL pointer: dereferencing it will crash
puts(ptr);    // crash

你只需要创建一个指向内存中某个位置的链接:

ptr = "string"; // dereferencing it will show the string
puts(ptr);     // displaying "string"

因此,拥有一个指针数组只是创建了对其他变量的引用列表。

要引用内存中的位置,您需要为每个指针分配变量或分配内存。


那么一个指针,比如 char *c;,它可以指向一个字符,也可以指向一个字符数组。哪个更正确呢?如果它只指向一个字符,那么执行 c++ 并再次存储一些内容,它应该会被覆盖! - Mukul Shukla
指针可以指向任何大小的数据,不一定是指针类型相同的数据。目标是否为数组由您决定。变量是内存位置。指针是对内存的引用。 - Gil
这是正确的,但是执行pointer++应该总是指向下一个内存位置。如果数组已经存储在前一个位置,那么这个位置应该被占用吗? - Mukul Shukla
当指针被递增时,它会按其类型的字节数递增(对于长结构体来说可能是很多字节),当然,你应该只遍历先前分配的内存,以防止崩溃发生。 - Gil

3
您已经为30个指针分配了空间,但您没有将它们初始化为指向任何有意义的地方。除非您在函数外声明了该数组,否则数组中的每个元素都将包含一些随机的位字符串,这些字符串可能与可写内存位置相对应,也可能不相对应。如果我们画出一张图片,它会像这样(所有地址都是凭空想象的;不要假设这与任何真实的架构相对应):
Item       Address       0x00  0x01  0x02  0x03
----       -------       ----  ----  ----  ----
ptr        0xbbc81230    0x??  0x??  0x??  0x??
           0xbbc81234    0x??  0x??  0x??  0x??
           0xbbc81238    0x??  0x??  0x??  0x??
           ...
           0xbbc812a8    0x??  0x??  0x??  0x??           
其中0x??表示随机字节值。对于您描述的行为,每个随机值“恰好”指向可写内存,并且覆盖存储在那里的任何内容“恰好”不会产生任何立即的不良影响。
坏结果:当您的代码表现良好时,实际上它的行为非常糟糕,并且可能会导致程序中其他地方的一些令人讨厌的运行时问题,这很难调试。
在尝试通过它写入之前,您需要显式设置ptr数组的每个元素以指向有效的内存位置。
假设我们添加以下代码:
ptr[0] = malloc(strlen("foo") + 1);
strcpy(ptr[0], "foo");
ptr[1] = malloc(strlen("bar") + 1);
strcpy(ptr[1], "bar");

我们动态分配了一些额外的内存来容纳一些字符串,并将这些新缓冲区的指针存储到 ptr[0]ptr[1] 中。
我们的图片现在会看起来像这样:
项目       地址          0x00  0x01  0x02  0x03
----       -------       ----  ----  ----  ----
           0x80ff0000     'f'   'o'   'o'  0x00
           ...
           0x80ffcdc0     'b'   'a'   'r'  0x00
           ...
ptr        0xbbc81230    0x80  0xff  0x00  0x00
           0xbbc81234    0x80  0xff  0xcd  0xc0
           0xbbc81238    0x??  0x??  0x??  0x??
           ...
           0xbbc812a8    0x??  0x??  0x??  0x??
ptr[0] 现在包含一个大小为4个 char 值的缓冲区的地址,我们将字符串 "foo" 复制到该缓冲区。同样,ptr[1] 包含另一个4字节缓冲区的地址,该缓冲区现在包含字符串 "bar"。

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