我正在尝试运行此代码,但是出现编译器警告:
调用打印功能的语句
print(arr,3,4);
从不兼容的指针类型传递'print'的参数1 [-Wincompatible-pointer-types] main.c / arrays第23行C / C ++问题
对于printf语句:
格式'%d'需要类型为'int'的参数,但是参数2具有类型 'int *'[-Wformat =] main.c / arrays第6行C / C ++问题
代码将2D数组传递给函数,该数组将其作为双指针接收并在函数内部打印出来。
void print (int **A, int m, int n){
for(m = 0; m < 3; m++){
for(n = 0; n < 4; n++){
printf("%d ", *((A+(m * 4) + n)));
}
printf("\n");
}
}
int main()
{
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
return 0;
}
1)在C中将2D数组作为双指针传递是不正确的? here 上面的链接仅指向C ++?还是在C语言中也有可能?
2)如果我使用单个指针,那么以下哪些分配正确/不正确?
清单1:
int *ptr;
ptr = &arr[0][0];
for(int i = 0; i < 3; i++){
for(int j = 0; j < 4; j++){
printf("%d ", *(ptr + (i * 4) +j));
}
printf("\n");
}
清单2:
int * ptr; ptr = arr;
答案 0 :(得分:2)
您期望2D数组会衰减到指向指针的指针是没有根据的。
要使用arr
作为print
的参数,您可以使用以下选项。
将print
更改为
void print (int (*A)[4], int m){ // Not need for n. It is 4
更改print
以使用VLA。为了使此工作有效,m
和n
必须先于A
。
void print(int m, int n, int A[m][n] {
这两项更改都将要求您也更改通话。
答案 1 :(得分:1)
让我们开始:
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
在print(arr,3,4);
中,arr
是一个数组。具体来说,它是3个元素的数组,每个元素是4个元素的数组,每个元素都是int
。因此,arr
是3个4 int
的数组。您可能已经听说过或读到了数组“衰减”到指针的情况。这是一个俗语。您可以在C 2011标准第6.3.2.1节第3段中找到的实际规则是:
除非它是
sizeof
运算符,_Alignof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字,将类型为“ type 的数组”的类型转换为类型为“ pointer to type ”的表达式,该表达式指向数组对象的初始元素,而不是左值。 / p>
以下是此规则如何应用于arr
中的print(arr,3,4);
:
arr
是一个标识符,表示它是某个对象的名称。这样,它指定其对象。该对象是3个4 int
数组。sizeof
或Alignof
或&
的操作数,也不是字符串文字。因此,遵循规则,它从3个4 int
数组的数组转换为指向第4个int
第一个数组的指针。接下来会发生什么?没有。我们拥有的表达式是一个指向4 int
数组的指针。没有规则说将指向数组的指针转换为指向指针的指针。我们有一个指向数组的指针,但是该数组尚未在表达式中使用,尚未在简单表达式arr
中使用。因此它不会转换。
这意味着您要传递给print
的内容是指向4 int
数组的指针。但是您对print
的声明说,它需要一个指向int
的指针。这些是不同的东西,它们是不兼容的,因此编译器会警告您。
(要查看它们是否不兼容,请考虑到指向4 int
的数组的指针与指向int
的指针的指针的区别。指向4 {的数组的指针的内存{1}}包含4个int
值。指向int
的指针的指针处的内存包含一个指针。这是完全不同的东西。)
接下来,考虑:
int
从上面我们知道,您应该将void print (int **A, int m, int n)
…
printf("%d ", *((A+(m * 4) + n)));
更改为int **A
,这是一个指向4 int (*A)[4]
数组的指针。您也可以将其更改为int
,因为为了方便起见,C语言中有一条规则,就是将这样的参数声明自动调整为int A[][4]
。但是,假设您将其保留为int (*A)[4]
。那么int **A
是什么意思?
由于*((A+(m * 4) + n))
是指向A
的指针的指针,因此int
意味着将A+(m * 4)
添加到指针。 (顺便说一下,这是奇怪的间距。m * 4
和m
的优先级乘以比4
和A
的乘积更紧密,所以为什么(m * 4)
会更好地刻画含义。)然后A + m*4
意味着要在其中添加A+(m * 4) + n
。总共,我们将n
个元素移到了m*4+n
指向的位置之外。由于A
指向一个指针,因此我们将指针提高了A
指针。然后m*4+n
取消引用。当取消引用指向*((A+(m * 4) + n)))
的指针的指针时,将获得指向int
的指针。因此,该表达式的结果是一个指针。但是您想要一个int
。
您引用的链接涉及“ 2D数组”。它所涉及的数组类型是使用指向指针的指针实现的。若要创建这样的数组,请创建一个指针数组,然后将这些指针中的每一个设置为指向行的元素。然后,指向该指针数组的指针的作用类似于2D数组,因为int
引用了行A[i][j]
的元素j
。如果具有这样的数组,则可以使用i
来引用行n
的元素m
。同样,您可以使用A[m][n]
来引用它。此表达式的意思是:
*(*(A+m)+n)
并向其添加A
。由于m
指向指向A
的指针,因此添加int
会使指针的值向前指向m
指针。那是我们应该找到指向行m
的元素的指针的地方。m
获取*(A+m)
指向的指针的值。此值应该是指向行A+m
的元素的指针,特别是指向第一个元素(索引为0)的指针。m
将指针的值向前移至指向点*(A+m)+n
n
。在这里我们应该找到行int
的元素n
。m
获取*(*(A+m)+n)
指向的int
的值。现在,假设您将*(A+m)+n
更改为print
。然后,您的print(int A[][4], int m, int n)
语句应该像以前一样使用printf
。或者也可以像以前一样使用A[m][n]
。但是,在这种情况下,表达式将被求值:
*(*(A+m)+n)
是一个指向4 A
数组的指针。向其添加int
可使指针的值进一步指向m
数组。m
获取*(A+m)
指向的对象。该对象是整个数组。因此,这是一个指定数组的表达式。遵循有关表达式中数组的C规则,此数组将转换为指向其第一个元素的指针。因此,A+m
成为指向数组*(A+m)
的第一个元素的指针。m
将指针的值向前移至指向点*(A+m)+n
n
。在这里我们应该找到行int
的元素n
。m
获取*(*(A+m)+n)
指向的int
的值。因此,*(A+m)+n
对于指针到指针,指针到数组和数组数组具有相同的最终结果,但是每个步骤所经过的步骤是不同的。 C知道每个子表达式的类型,并对它们进行不同的处理,以达到相同的结果。
最后,假设您将A[m][n]
传递给&A[0][0]
并将其参数更改为print
。现在,表达式int *A
是什么?在这种情况下,您会将3个4 *((A+(m * 4) + n)))
的数组视为一个12 int
的大数组。然后,计算行int
的元素n
的位置。在这种情况下,m
是指向A
的指针(不是指向int
的指针)。因此,int
是第A+(m * 4) + n
行的元素n
应该位于的位置的计算,而m
得到该元素的值。
这是您应尽可能避免的方法。通常,您应该使用C的内置方法来寻址数组元素,并避免进行自己的计算。是否严格符合C代码可能取决于您对C标准中某些段落的理解程度。
答案 2 :(得分:0)
如果您的意图是一个指针数组,则无法一次将其全部初始化。您的数组编译为与{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
以下内容将编译为指向数组的指针的数组:
int arr1[] = {1, 2, 3, 4};
int arr2[] = {5, 6, 7, 8};
int arr3[] = {9, 10, 11, 12};
int* my2DArr[3] = {arr1, arr2, arr3};
对于这样的嵌套循环有效:
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
printf("%d\n", my2DArr[i][j]);
}
}
对于代码中的数组,您可以对其进行迭代,就好像它只是一个数组一样。