能否将char[][]传递给请求char**的函数?

3

我正在尝试调用一个以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?

1
fill_my_strings 函数需要一个指向数组的指针数组作为参数。因此,您需要提供这样的参数。您可以声明一个 std::vector<char*> v,使用循环将指针(每个指针都指向矩阵的相关部分)添加到向量中,然后传递 &v[0]。祝好运! - Cheers and hth. - Alf
1
@AlfP.Steinbach 是的,你的回答是可以接受的。我唯一关心的是,从历史上看,我不认为向量内存实际上是保证连续的。我以前读过一些有关它的条目,似乎它们基本上总是这样,但是像那样通过向量指针传递来假设这种情况似乎是很差的风格...我确实喜欢你的解决方案,并且没有显式的堆调用,这使得它非常漂亮 :) - aardvarkk
2
@RomanB:关于“为什么不把你的评论放在答案里?”的问题,SO社区通过其自我任命和选举领导人已经明确表示,我的任何答案,包括在结尾处写上“cheers & hth.,”这样的告别语都是非常不受欢迎的。在上周,由于我坚持使用“Cheers & hth.,”而被暂停了一周,尽管那个决定在一个小时左右就被撤销了。此外,我的一个(正确的)答案因为太短而被删除。总之,通过发布评论,我避免了整个麻烦事情,直到他们也开始删除评论... Cheers, - Cheers and hth. - Alf
1
@AlfP.Steinbach 感谢您和Steve澄清了那一点。在这种情况下,似乎没有比Alf的解决方案更好的选择了!如果您愿意,Alf,请随时将您的解决方案发布为实际答案。我可能会看看是否有其他天才的条目,但如果没有的话,我很可能会接受您的解决方案。谢谢! - aardvarkk
@AlfP.Steinbach 我会在你的解决方案中添加 *reserve()*。 - Roman Byshko
显示剩余8条评论
4个回答

3
你的问题的简单答案是不可以;二维数组与指向指针的类型是不同的。数组会“衰减”成为指向其第一个元素的指针,但指针实际上就是该值。
如果你将它们都转换成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的某些函数,那么他无法控制签名。否则更改签名是一种完全有效的方法。 - dmckee --- ex-moderator kitten

2
假设您有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
}

我认为这个答案是最清晰的,并且给出了最好的解释。谢谢! - aardvarkk

0

显而易见的做法是建立一个索引

在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

2
为什么不显式地使用空终止符呢?这样你就不会浪费时间去初始化所有内容,只是为了覆盖大部分内容。new操作符是否进行零填充? - Dave
@Dave:我希望当我获取数组时,它处于“安全”状态。如果您的分析器告诉您这会减慢速度,您可以用mallocindex[num_strings]=NULL替换它。 - dmckee --- ex-moderator kitten
@dmckee 对不起,我删除了我的评论,因为Dave说了同样的话。所以这是出于预防措施,我明白了。 - Roman Byshko
看起来“new”不会用零填充数组;你不能偏爱使用“new”而不是“calloc”。 - Dave
@Dave:是的,我应该明确指定new[]以确保指针的构造函数被调用 - dmckee --- ex-moderator kitten
显示剩余2条评论

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