【问题标题】:Passing an array of strings as parameter to a function in C将字符串数组作为参数传递给 C 中的函数
【发布时间】:2010-10-03 22:03:10
【问题描述】:

我想要一个简单的函数,它接收一个字符串并在经过一些解析后返回一个字符串数组。所以,这是我的函数签名:

int parse(const char *foo, char **sep_foo, int *sep_foo_qty) {
    int i;
    char *token;
    ...
    strcpy(sep_foo[i], token); /* sf here */
    ...
}

那我这样称呼它:

char sep_foo[MAX_QTY][MAX_STRING_LENGTH];
char foo[MAX_STRING_LENGTH];
int sep_foo_qty, error;

...

error = parse(foo, sep_foo, &sep_foo_qyt);

...

这样我会在编译期间收到警告:

warning: passing argument 2 of 'parse' from incompatible pointer type

然后在执行过程中出现分段错误,这里用 /* sf 标记 */

我的 C 代码有什么问题?

提前致谢

【问题讨论】:

    标签: c arrays pointers parameters segmentation-fault


    【解决方案1】:

    警告是完全正确的。您的函数需要一个指针数组。你给它一个数组数组。

    预期:

    sep_foo: +-----+ +-----+ |char**|--> 0: |char*|-->"string1" +-----+ +-----+ 1: |char*|-->"string2" +-----+ *sep_foo_qty-1: |... | +-----+

    你提供了什么:

    sep_foo: +--------------------------------+ 0:|字符[MAX_STRING_LENGTH] | +--------------------------------+ 1:|字符[MAX_STRING_LENGTH] | +--------------------------------+ MAX_QTY-1:| ... | +--------------------------------+

    具有X 类型元素的数组可以“衰减”为指向XX* 的指针。但是X 的值不允许在该转换中更改。只允许 一个 衰减操作。你需要它发生两次。在您的情况下,X 是 array-of-MAX_STRING_LENGTH-chars。该函数希望X 成为指向字符的指针。由于它们不一样,编译器会警告您。我有点惊讶这只是一个警告,因为编译器允许发生的事情不会带来任何好处。

    在您的函数中,您可以编写以下代码:

    char* y = NULL;
    *sep_foo = y;
    

    这是合法代码,因为sep_foochar**,所以*sep_foochar*y 也是;你可以分配他们。但是根据您的尝试,*sep_foo 不会真的成为char*;它将指向一个字符数组。实际上,您的代码会尝试这样做:

    char destination[MAX_STRING_LENGTH];
    char* y = NULL;
    destination = y;
    

    您不能将指针分配给数组,因此编译器会警告调用无效。

    有两种方法可以解决这个问题:

    • 更改您在调用方声明和分配sep_foo 的方式,使其与函数期望接收的内容相匹配:

      char** sep_foo = calloc(MAX_QTY, sizeof(char*));
      for (int i = 0; i < MAX_QTY; ++i)
        sep_foo[i] = malloc(MAX_STRING_LENGTH);
      

      或者,等价

      char* sep_foo[MAX_QTY];
      for (int i = 0; i < MAX_QTY; ++i)
        sep_foo[i] = malloc(MAX_STRING_LENGTH);
      
    • 更改函数的原型以接受你真正给它的东西:

      int parse(const char *foo, char sep_foo[MAX_QTY][MAX_STRING_LENGTH], int *sep_foo_qty);
      

    【讨论】:

    • @che,数组可以通过获取其第一个元素的指针 (ptr = &array[0]) 转换为指针。这可以隐式完成(ptr = array)。反之则不然。
    • 另外,当你取一个数组的指针时(ptr = &ar),ptr 包含了整个数组的地址。一般情况下,ptr 将等于 &ar[0],但类型不同。如果你取一个指针的指针,你会得到一个不等于那个指针的数字(内存是 ref'd)。
    • 在您的编辑中...您在图表上的工作比我做得好得多。我是艺术的反面。 =]
    • Rob,我非常喜欢你的回答。我也喜欢这个答案如何很好地补充了我最近的烦恼:*.com/questions/423823/…。我会在你的答案上放一个链接。玩得开心:)
    • Rob,我想你打错了变量名:sep_foo_qty 是 int*,sep_foo 是 char**。请编辑您的答案,因为它真的很好但有点令人困惑。
    【解决方案2】:

    参数2应该是

    char sep_foo[][MAX_STRING_LENGTH]
    

    为了澄清,您将指针传递给 parse() 并将其视为指向指针的指针。 C 中的多维数组不是指针数组。它是由数组变量指向的单个内存块。你不能取消引用它两次。

    【讨论】:

    • 我一直在尝试理解 strager 的记忆模型,但您已经用三个清晰简单的句子确定了差异。感谢您的澄清!
    【解决方案3】:

    sep_foo 被定义为一个数组数组。换句话说,当你使用sep_foo 时,它指向的是顺序内存的开始。这是一个模型:

    (assume MAX_STRING_LENGTH = 16, MAX_QTY = 2)
    sep_foo       = &&0000
    sep_foo[0]    =  &0000
    sep_foo[0][0] = *&0000 = 12
    sep_foo[0][8] = *&0008 = 74
    sep_foo[1]    =  &0010
    sep_foo[1][0] = *&0010 = 12
    
    
    0000  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
    0010  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
    

    但是,您的函数需要一个指针数组(实际上是指向指针的指针)。这是这样建模的:

    sep_foo_arg       =   &&0000
    sep_foo_arg[0]    =  *&&0000 = &0010
    sep_foo_arg[0][0] =  *&*&0000 = 12
    sep_foo_arg[0][8] = *(&*&0000 + 8) = 74
    sep_foo_arg[1]    =  *&&0002 = &0020
    sep_foo_arg[1][0] = *&*&0000 = 12
    
    0000  0010 0020  xxxx xxxx  xxxx xxxx  xxxx xxxx
    
    0010  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
    0020  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
    

    是的......我的解释可能有点令人困惑......

    无论如何,你可以通过告诉你的函数如何处理指向的指针来解决这个问题。特别是,您可能希望将其视为一个数组(内存序列):

    int parse(const char *foo, char (*sep_foo)[MAX_STRING_LENGTH], int *sep_foo_qty);
    

    【讨论】:

    • strager,你不是说 char (sep_foo)[MAX_STRING_LENGTH] 吗?因为现在它仍然是“char*”。对我来说似乎是一个错字。
    • @litb,啊,对。通常我会查这个,但我写的时候很着急(你不能告诉我吗?=])。谢谢。
    • @litb,等等——你打错字了吗?我不知道哪一个是对的了。 @_@ 稍后会进行一些测试。
    • 很抱歉,我看到我听起来很混乱。好吧,我的意思是你打错字了 :) 看:T f[N] 是一个 T 数组。 T (*f)[N] 是指向 T 数组的指针(你想要的)。 * 与 T 的绑定比与参数名称的绑定更多,因此您必须将其与 f 绑定,以便 f 成为指针。
    • 因为如果你说“T *f[N]”,它与参数列表中的“T **f”是一样的。我不相信你是那个意思。
    【解决方案4】:

    如果那是您的确切代码,那么我猜测段错误是因为您没有在 parse 函数中为 char* token 分配内存,然后在 strcpy 中使用它。

    【讨论】:

    • 我不需要为 char *token 分配内存,因为我正在使用 strtok_r 函数:token = strtok_r(copied_foo, "/", &save_ptr); ... token = strtok_r(NULL, "/", &save_ptr);谢谢
    最近更新 更多