【问题标题】:Pass and modify single row of a 2D array in C [duplicate]在C中传递和修改二维数组的单行[重复]
【发布时间】:2013-06-24 12:04:15
【问题描述】:

首先我对 C 没有信心,但我有一个 int 的二维数组,我想要一个函数来写入该数组单行的所有值。

例如:

int main(int argc, char *argv[])
{ 
    int a[2][2];
    a[0][0] = 1;
    a[0][1] = 2;
    a[1][0] = 3;
    a[1][1] = 4;
    change_array(&a[0]);
}

void change_array(int* array[])
{
    (*array)[0] = -1; 
    (*array)[1] = -1;        
}

程序立即崩溃。我试图将 change_array 函数更改为 array[0] = -1 并且......它有效!值已正确更改(我不知道为什么,因为它应该是完全错误的),但是如果我在程序的其他部分使用此函数,则数组的值保持不变。 怎么可能?有什么建议可以成功更改我的数组的值吗? 非常感谢!

【问题讨论】:

  • 重复数千次。数组不是指针。
  • 你在gcc中的原始代码会说:warning: passing argument 1 of ‘change_array’ from incompatible pointer typenote: expected ‘int **’ but argument is of type ‘int (*)[2]’即使没有-Wall,这已经很清楚了。

标签: c arrays pointers multidimensional-array


【解决方案1】:

你可以尝试这样做:

#include <stdio.h>

void change_array(int array[2][2])
{
    array[0][0] = -1;
    array[0][1] = -1;
}

int main(int argc, char *argv[])
{
    int a[2][2];
    a[0][0] = 1;
    a[0][1] = 2;
    a[1][0] = 3;
    a[1][1] = 4;
    printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
    change_array(a);
    printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
}

这取决于您的需求,但在某些情况下,我发现最好使用单维数组并为 2 维构建 getter/setter。这样的解决方案可以在这个答案中找到:Correct way to allocate and free arrays of pointers to arrays

【讨论】:

  • 什么是“不标准”?
  • 另外,使用多维数组完全没问题,为什么还要麻烦使用显式的 setter/getter?
  • @H2CO3 取决于具体情况,但每当我使用标准二维数组时,我迟早必须将其修改为动态版本。因为我不想要一个双重分配的**结构,所以我使用了我提到的 getter/setter。不过,我稍微改变了答案。
  • @H2CO3 我删除了那个注释;该函数声明在我看来似乎很奇怪,但经过一番谷歌搜索后,我发现它完全没问题。谢谢你的评论。
  • 不要忘记您也可以动态分配二维数组。 int (*arr)[10] = malloc(sizeof(*arr) * 10); 创建一个 10x10 的 ints 数组。
【解决方案2】:

您的代码向change_array 传递了与为change_array 声明的参数不同的内容。

在代码change_array(&amp;a[0]) 中,a 是两个int 的两个数组的数组。所以a[0] 是两个int 的第一个数组。所以&amp;a[0]是两个int的第一个数组的地址。将此与change_array 的声明进行比较。在void change_array(int* array[]) 中,array 是指向int 的指针数组。所以这是一种不同的类型。

相反,您可以使用void change_array(int (*array)[]) 声明change_array。那么array 是一个指向int 数组的指针,你的代码就可以工作(使用(*array)[0] = -1)。

注意:您应该在启用警告的情况下进行编译,最好使用严格或迂腐的语言语义。那么编译器应该已经警告你change_arraymain 中使用而没有事先声明。您应该有一个事先声明,以便编译器在使用之前知道change_array 的完整类型。这样,编译器就会发现传递了错误的类型,并且会警告你。

尽管上述方法可以更正您的代码,但大多数人会使用不同的解决方案。他们会用void change_array(int *array) 声明change_array,然后用change_array(a[0]) 调用它。

change_array(a[0]) 中,a[0] 是两个int 的第一个数组。但是,C 语言中有一条规则,即数组表达式被转换为指向其第一个元素的指针。所以a[0] 自动变为&amp;a[0][0]。 (只要数组不是&amp;sizeof_Alignof 的操作数,并且不是用于初始化数组的字符串文字,就会发生这种转换。)所以调用传递了一个指向int 的指针,它匹配指向int 的参数。那么参数array可以作为array[i]来访问数组元素,所以可以使用更简单的语法array[0] = -1代替(*array)[0] = -1

【讨论】:

    【解决方案3】:

    C 中,当将数组变量作为参数传递给函数时,数组变量会衰减为指向包含数组第一个元素的内存地址的指针。这与它的所有元素都放置在连续内存中的事实相结合。所以C 只需要保存数组的第一个位置(不管它有多少维),然后就可以根据索引计算出它应该访问的指向内存的偏移量。

    因此,在您的特定代码段上,变量a 指向第一个元素,即a[0][0]。所以,基本上是这样做的:

    change_array(&a[0]);
    

    与做的大致(但不完全)相同:

    change_array(a);
    

    现在,知道了数组是如何在 C 中传递的,你应该能够推断出,事实上,二维数组在作为参数传递时,实际上在访问它们的第一个坐标时包含一个指向第一个元素所在位置的指针。第二个坐标是。因此,当您执行array[0] 时,您将传递一个指向第一个坐标下数组第一个元素的指针。然后当您执行*(array[0]) 时,您实际上是在访问第二个坐标的第一个元素。

    在此补充一点也很重要;由于数组变量衰减为指针,因此 C 中的所有数组都是通过引用传递的,因为您正在传递一个指针。因此,所有修改传递给它们的数组的函数调用都会进行实际修改。

    知道了这一点,那么你的功能应该是:

    void change_array(int *array)
    {
        array[0] = -1;
        array[1] = -1;
    }
    

    然后你可以执行如下调用:

    change_array(a[0]);
    

    为了修改a的第一个数组的元素。

    现在,作为C 中的良好做法,为了避免分段错误,可以将指针与表示数组大小的整数一起传递给函数:

    void change_array(int *array, int size);
    

    然后总是执行访问检查这个边界。

    【讨论】:

    • “数组变量实际上是指向包含数组第一个元素的内存地址的指针” - 不,它不是。数组就是数组,指针就是指针。在某些情况下,数组可能会衰减为指向其第一个元素的指针,但并非总是如此。
    • @H2CO3 实际上在 C 中它总是正确的。认为它不是正常的错误是正常的错误,当人们认为数组是按值传递时,通常会退化为错误,而实际上按值传递只发生在指针的值上。
    • 别教我 C。数组不是“总是”指针。事实上,他们从来没有。如果您使用sizeof() 会怎样?如果您获取数组的地址和指针并对它们执行指针运算会怎样?在这两种情况下,您都会得到不同的结果,因为 数组不是 FRIGGIN' POINTERS!
    • @H2CO3 请原谅我滥用术语。此处使用的正确命题是“因为在 C 中指针算术和数组索引是等效的”,如此处所述:c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=aryptr,问题 6.3。不用吐槽>
    • 编辑:使用正确的术语,感谢@H2CO3 丰富的对话..
    猜你喜欢
    • 2012-04-25
    • 1970-01-01
    • 2016-03-19
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 2013-03-28
    • 1970-01-01
    相关资源
    最近更新 更多