【问题标题】:Is it possible to pass char[][] to a function requesting char**?是否可以将 char[][] 传递给请求 char** 的函数?
【发布时间】:2012-01-12 10:56:04
【问题描述】:

我正在尝试调用一个以 char** 作为参数的函数。它的工作是填充字符串数组(即 char* 数组)。我知道字符串的最大长度,我可以将要填充的最大数量作为另一个参数传递,所以我希望像这样堆栈分配它:

fill_my_strings(char** arr_str, int max_str); // function prototype

char fill_these[max_strings][max_chars_per_string]; // allocating chars
fill_my_strings(fill_these, max_strings); // please fill them!

当然,我收到“无法将 char[max_strings][max_chars_per_string] 转换为 char**”错误。

我知道这是一些微妙(或不那么微妙)的问题,因为我对数组和指针之间的区别的理解。我只是不确定为什么不能将这块内存传递给想要一个 char** 的东西并让它填充我的堆栈分配的字符。有人可以解释一下这是否可行,如果不可行,为什么不呢?

不调用malloc/new可以调用这样的函数吗?

【问题讨论】:

  • 您可以将char[][] 传递给期望char** 的函数,但如果您试图在堆栈上给它们指针,一旦函数结束,这些指针将指向无效位置。您需要在堆上分配内存。
  • ¤ fill_my_strings 需要一个指针数组,每个指针都指向一个数组。所以这就是你必须给它的。您可以只声明一个std::vector<char*> v,使用循环指向push_back 指针(其中每个指针指向矩阵的相关部分),然后传递&v[0]。干杯&hth.,
  • @AlfP.Steinbach 是的,您的回答是可以接受的。我唯一担心的是,从历史上看,我不相信向量内存实际上保证是连续的。我以前读过一些关于它的条目,似乎它们基本上总是如此,但是通过将指针传递给这样的向量来假设这样的风格似乎很糟糕......虽然我确实喜欢你的解决方案,但没有显式的堆调用,让它变得漂亮:)
  • @RomanB: Re "Why don't you put your cmets as answers?", well the SO community, via its self-appointed and its elected leaders, have made it clear that any answers from me包括最后一句“cheers & hth.”之类的告别词是非常不受欢迎的。在前一周,由于我坚持“Cheers & hth.”,我被停职一周,尽管这个决定在一小时左右就被推翻了。此外,我的一个(正确)答案因太短而被删除。无论如何,通过发表评论,我避免了整个麻烦事,直到他们也开始删除 cmets...干杯,
  • @AlfP.Steinbach 感谢史蒂夫和您自己澄清这一点。在这种情况下,似乎没有比 Alf 更好的解决方案了!如果您希望 Alf,请随时将您的解决方案作为实际答案发布。我可能会看看其他人是否有任何天才条目,但如果不是这样,我可能会接受你的解决方案。谢谢!

标签: c++ c arrays pointers char


【解决方案1】:

您的问题的简单答案是否定的;二维数组不同于指向指针的指针类型。数组衰减指向指向它们的第一个元素的指针,但实际上指针那个值。

如果将两者都转换为char*,这些类型之间的区别就很明显了

int x;
char *arr_pp[] = {"foo", "bar", "baz"};
char arr_2d[][4] = {"foo", "bar", "baz"};

char *cp = (char*)arr_pp;
for(x=0; x<3; x++)
     printf("%d ", cp[x]);
printf("\n");

cp = (char*)arr_2d;  
for(x=0; x<3; x++)
     printf("%d ", cp[x]);
printf("\n");

输出(在我的电脑上)是:

-80 -123 4 
102 111 111 

第一行是乱码,因为我正在打印一个转换为字节的地址,第二行是“foo”的 ascii 值。

在采用char ** 的函数中,编译器无法知道衰减数组类型,这些类型实际上并不包含指针。

【讨论】:

    【解决方案2】:

    我不确定为什么 fill_my_strings() 需要 char** 参数。从您的示例中,调用者已经从堆栈中分配了内存。所以使用char* 应该没问题。

    但是如果你想使用char**或者你不能修改fill_my_strings()函数,试试下面的示例代码:

    void fill_my_strings(char** arr_str, int max_chars_per_string, int max_strings)
    {
    
        for(int i = 0; i < max_strings; ++i)
        {
            //Make sure you have enough space
            memcpy(*arr_str, "ABCD", sizeof("ABCD"));
    
            *arr_str += max_chars_per_string;
        }
    }
    
    char fill_these[max_strings][max_chars_per_string];
    char* pointer = (char*)fill_these;
    fill_my_strings(&pointer, max_strings, max_chars_per_string);
    

    【讨论】:

    • 如果他正在调用一些通常处理argv 的函数,那么他无法控制签名。否则更改签名是一种完全有效的方法。
    【解决方案3】:

    假设您有 n 个指向最多 m-1 个字符的字符串(包括 NULL 的 m 个字符)的指针。 因此,在纯 C 中: sizeof(char[n][m]) 将返回 n*m。 sizeof(char**) 将返回架构中指针的大小,可能是 32(如果 x86)或 64(如果 x86_64)。

    char[n][m] 实际上是连续分配 n*m 字节。 char** 分配一个指针。此指针引用 *n 字节的内存条。这 n 个指针中的每一个都指向一个由 m 个字符组成的内存条。

    所以,考虑到 sizeof(char) == u,如果你声明 char a[n][m],当你使用 a[i][j] 时,编译器会理解 *(a + i*m*u + j*u)。 所以,考虑到sizeof(char *) == w,如果你声明char **a,当你使用a[i][j]时,编译器会理解((a + i*w) + j*w)。

    完全不同的数据管理。

    处理特殊情况的关闭方法是创建一个 char** 变量,并使用堆栈分配矩阵的地址填充它。

    char **tmp = malloc(max_strings * sizeof(char *));
    int i;
    for(i = 0; i < max_strings; i++){
        tmp[i] = &(fill_these[i][0]); //you probably can't reference a char[][] with a single index - not confirmed
    }
    

    【讨论】:

    • 我认为这个答案是最清楚的并且给出了最好的解释。谢谢!
    【解决方案4】:

    显而易见的事情就是建立一个索引

    在 c 中使用类似的东西:

    char string_list[num_strings][str_length];
    
    // ...
    
    char**index = calloc( (num_strings+1), sizeof(*index) ); // calloc insures NULL termination
    for (int i=0; i<num_strings; ++i) {
       index[i] = string_list[i]
    }
    

    在 c++ 中,new[] 优于 calloc

    【讨论】:

    • 为什么不显式地空终止;那么您就不会浪费时间初始化所有内容,而只是覆盖其中的大部分内容。新的零填充吗?
    • @Dave:我希望阵列一拿到它就处于“安全”状态。如果您的分析器告诉您这会减慢您的速度,您可以将其替换为 mallocindex[num_strings]=NULL;
    • @Roman:我会覆盖除了 last 元素之外的所有元素,因为人们经常使用 for(p=index; *p; ++p) 处理 char** 类型的结构。 NULL 终止数组使之安全。如果你能保证这不会发生,你就不需要它。
    • @dmckee 抱歉,我删除了我的评论,因为戴夫也这么说。所以原因是预防措施,我明白了。
    • 看来new 不会对数组进行零填充;你不能喜欢new而不是calloc
    猜你喜欢
    • 1970-01-01
    • 2023-03-29
    • 2019-12-11
    • 2012-07-26
    • 2023-03-06
    • 2012-05-10
    • 1970-01-01
    • 2020-01-31
    相关资源
    最近更新 更多