char [1024] vs char *

6

我正在学习C语言,但是在使用char *和char数组时遇到了麻烦。我正在使用一个来自库的通用哈希集容器(我不想详细描述此库)。这个库包括以下函数:

void *HashSetLookup(hashset *h, const void *elemAddr);

我需要使用哈希集合中的搜索功能来查看元素是否已经存在于其中(哈希和比较函数是哈希集合结构体的一部分)。在这种情况下,我使用哈希集合来存储指向C字符串的指针,更具体地说是(char **)。我的问题是以下代码导致了分段错误:

    char word[1024];
    /* Some code that writes to the word buffer */
    HashSetLookup(stopList, &word);

虽然这段代码运行良好(并且符合预期):

    char word[1024];
    /* The same code as before that writes to the word buffer */
    char* tmp = strdup(word);
    HashSetLookup(stopList, &tmp);
    free(tmp);

我以为 char word[] 和 char* 基本上是一样的东西。唯一的区别是,char word[1024] 存储在堆栈中,长度固定为 1024,但 tmp 存储在堆中,只占用必要的空间(strlen(word)+1)。
因此,我不理解为什么我必须复制字符串到堆中才能调用这个函数。为什么会发生这种情况呢?char* tmp = strdup("something") 和 char word[1024] = "something" 之间是否存在更基本的区别?

1
展示第一个例子中写入Word的代码。 - Jamison Dance
我在第一个示例中没有看到变量tmp被声明或使用。 - MAK
MAK,抱歉那是一个错误。我现在已经修复了它。 - Siggi
Jergason,我曾经考虑过这样做,但那段代码实际上只是一个包含五个相当复杂的函数调用列表。我不认为那会有所帮助。然而,我已经确保它实际上写入了Word,所以这不是问题。事实上,Word将包含单词“BBC”(当然是由\0关闭的)。 - Siggi
5个回答

6
你提到你需要一个char **,这里存在一个问题:对于一个数组,word&word意思相同,都指向数组内容的实际位置。当你使用指针时,它能正常工作是因为“指针”被存储在不同的位置,同时指向同一个数组。你不需要使用strdup,你只需要创建一个指针即可:
char* tmp = word;
HashSetLookup(stopList, &tmp);

很高兴它能够工作。这是数组和指针之间微妙差别之一。 - casablanca
-1 是错误的答案。如果你传递 tmp 或 &tmp,这确实会产生根本性的区别。自己尝试一下,你会发现会出现不同的结果。不过你可以使用 &tmp[0]。 - Eiko
1
我认为你误解了我的回答。tmp&tmp显然是不同的,因为tmp是一个指针。但是word&word是一样的,因为word是一个数组。我在发布之前已经试过几次了。 - casablanca

1
很难在没有HashSetLookup文档的情况下确定。
但是它期望第二个参数为const void *,因此您应该传递tmp,而不是&tmp,因为tmp已经是指针。
我完全看不出这里需要char **。
另外,您可能会对HashSetLookup()返回的内容感兴趣。

谢谢你的回答。实际上,我需要存储char,因为HashSet会将void指针指向的任何内容复制到固定大小的内存块中。这对于字符串来说并不适用,因为它们的大小各不相同。因此,所有HashSet函数都需要接受char,因为我只想存储存储在堆中的字符串指针。而且你是对的,我对HashSetLookup()返回的内容感兴趣。但由于它在这一点上崩溃了,所以我没有在代码中包含它。 - Siggi
你的签名和传递的值并不代表有效性。如果你认为现在它正在运行,那很可能只是另一个 bug 的原因。 - Eiko

0

既然你提到了char**,我认为问题出在函数试图向第二个参数指向的位置写入时,也就是当你写下:

HashSetLookup( stopList, &word );

它会尝试给word分配一个地址(这就是为什么它需要它的地址),这将用指针覆盖缓冲区。

以下愚蠢的片段演示了这一点(请记住,数组的地址仍然是其第一个元素的地址):

#include <stdio.h>
#include <stdlib.h>

void func( void* boo )
{
        char** ptr = ( char** )boo;
        printf( "func: got %p\n", boo );
        *ptr = "bad func";
}

int main( int argc, char* argv[] )
{
        char buf[128], *p;
        func( &buf ); /* buf got overwritten */
        printf( "array: %s\n", buf );
        p = malloc( 128 );
        func( &p ); /* p got new value */
        printf( "malloc: %s\n", p );
        return 0;
}

我一开始也有类似的想法。但是如果你看一下HashSetLookup的原型,你会发现第二个参数是一个指向常量的指针。其次,这只是一个查找函数,根本不应该写入任何东西。 - Siggi

0

在第一个例子中,您可能错过了某些内容,因为 "word" 根本没有被使用。

无论如何,在大多数环境中,当您编写 'char *s = "Hello World"' 时,它会在代码段上创建,并且不能被修改。代码段意味着它是可执行代码的一部分,必须不可修改,只能读取。当您尝试编写它时,会出现段错误。

但是 'char []' 在数据段上创建,因此可以轻松修改。


他在第一个示例的注释中说,有一些遗漏的代码可以写入单词。 - Jamison Dance
你是对的。在第一段代码中,应该是HashSetLookup(stopList, &word),而不是HashSetLookup(stopList, &tmp)。我已经修复了。谢谢。 - Siggi


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