C 指向固定大小数组的指针数组
我试图将两个固定大小的数组分配给指向它们的指针数组,但编译器警告我,我不明白为什么C 指向固定大小数组的指针数组,c,arrays,C,Arrays,我试图将两个固定大小的数组分配给指向它们的指针数组,但编译器警告我,我不明白为什么 int A[5][5]; int B[5][5]; int*** C = {&A, &B}; 此代码编译时带有以下警告: 警告:从不兼容的指针类型初始化[默认启用] 如果我运行代码,它将引发错误。但是,如果我动态地分配A和B,它就可以正常工作这是为什么?数组与C中的多维指针不同。在大多数情况下,数组的名称会被解释为包含它的缓冲区的地址,而不管您如何索引它。如果A被声明为inta[5][5],那么
int A[5][5];
int B[5][5];
int*** C = {&A, &B};
此代码编译时带有以下警告:
警告:从不兼容的指针类型初始化[默认启用]
如果我运行代码,它将引发错误。但是,如果我动态地分配
A
和B
,它就可以正常工作这是为什么?数组与C中的多维指针不同。在大多数情况下,数组的名称会被解释为包含它的缓冲区的地址,而不管您如何索引它。如果A
被声明为inta[5][5]
,那么A
通常意味着第一个元素的地址,即它被有效地解释为int*
(实际上是int*[5]
),而不是int**
。地址的计算恰好需要两个元素:A[x][y]=A+x+5*y
。这是执行a[x+5*y]
的一种便利,它不会将a
提升到多维缓冲区
如果你想要C语言中的多维指针,你也可以这样做。语法将非常相似,但它需要更多的设置。有几种常见的方法
使用单个缓冲区:
int **A = malloc(5 * sizeof(int *));
A[0] = malloc(5 * 5 * sizeof(int));
int i;
for(i = 1; i < 5; i++) {
A[i] = A[0] + 5 * i;
}
int**A=malloc(5*sizeof(int*);
A[0]=malloc(5*5*sizeof(int));
int i;
对于(i=1;i<5;i++){
A[i]=A[0]+5*i;
}
每行有一个单独的缓冲区:
int **A = malloc(5 * sizeof(int *));
int i;
for(i = 0; i < 5; i++) {
A[i] = malloc(5 * sizeof(int));
}
int**A=malloc(5*sizeof(int*);
int i;
对于(i=0;i<5;i++){
A[i]=malloc(5*sizeof(int));
}
数组和指针的等价性让您感到困惑
当您声明像A[5][5]
这样的数组时,因为您已经声明了两个维度,所以C将连续为25个对象分配内存。也就是说,内存将按如下方式分配:
A00, A01, ... A04, A10, A11, ..., A14, A20, ..., A24, ...
int A[5][5];
int B[5][5];
int (*C)[5][5] = &A;
int A[5][5];
int B[5][5];
int (*C[])[5][5] = {&A, &B};
结果对象A
,是指向该内存块开始的指针。它的类型是int*
,而不是int**
如果需要指向数组的指针向量,则需要将变量声明为:
int *A[5], *B[5];
这将给你:
A0, A1, A2, A3, A4
所有类型都是int*
,您必须使用malloc()
或其他方法填充
或者,您可以将
C
声明为int**C
或者您应该声明第三个数组,如
int A[5][5];
int B[5][5];
int ( *C[] )[N][N] = { &A, &B };
这是一个指向二维数组的指针数组
比如说
#include <stdio.h>
#define N 5
void output( int ( *a )[N][N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", ( *a )[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N][N];
T C[] = { &A, &B };
*/
int ( *C[] )[N][N] = { &A, &B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
#include <stdio.h>
#define N 5
void output( int ( *a )[N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", a[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N];
T C[] = { A, B };
*/
int ( *C[] )[N] = { A, B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
或者
int A[5][5];
int B[5][5];
int ( *C[] )[N] = { A, B };
这是指向二维数组的第一个元素的指针数组
比如说
#include <stdio.h>
#define N 5
void output( int ( *a )[N][N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", ( *a )[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N][N];
T C[] = { &A, &B };
*/
int ( *C[] )[N][N] = { &A, &B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
#include <stdio.h>
#define N 5
void output( int ( *a )[N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", a[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N];
T C[] = { A, B };
*/
int ( *C[] )[N] = { A, B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
这取决于您将如何使用第三个阵列
使用typedefs(在演示程序中如注释所示)简化数组的定义
至于这个声明,
int*** C = {&A, &B};
int A[5][5];
然后在左侧声明了一个类型为int***
的指针,它是一个标量对象,而在右侧声明了一个具有不同类型int(*)[N][N]
的初始化器列表
因此编译器会发出一条消息。尽管数组和指针密切相关,但它们根本不是一回事。人们有时会对此感到困惑,因为在大多数上下文中,数组值会衰减为指针,而且数组表示法可以在函数原型中用于声明实际上是指针的参数。此外,许多人认为的数组索引表示法实际上执行指针算术和解引用的组合,因此它对指针值和数组值同样有效(因为数组值会衰减为指针) 根据声明
int*** C = {&A, &B};
int A[5][5];
变量A
指定一个由五个int
数组组成的数组。它在衰减的地方衰减为int(*)[5]
类型的指针,即指向5int
数组的指针。另一方面,指向整个多维数组的指针具有类型int(*)[5][5]
(指向5个数组的数组的指针int
),这与int***
(指向int
)的指针完全不同。如果要声明指向如下多维数组的指针,可以这样做:
A00, A01, ... A04, A10, A11, ..., A14, A20, ..., A24, ...
int A[5][5];
int B[5][5];
int (*C)[5][5] = &A;
int A[5][5];
int B[5][5];
int (*C[])[5][5] = {&A, &B};
如果要声明此类指针的数组,则可以执行以下操作:
int (*D[2])[5][5] = { &A, &B };
增加: 这些区别以各种方式发挥作用,其中一些更重要的是数组值不会衰减为指针的上下文,以及与这些值相关的上下文。其中最重要的一个是当值是
sizeof
运算符的操作数时。鉴于上述声明,以下所有关系表达式的计算结果均为1(true):
此外,这些关系表达式的计算结果可能为1,但不能保证:
sizeof(C) == sizeof(void *)
sizeof(D) == 2 * sizeof(void *)
这是数组索引工作原理的基础,也是理解何时分配内存的关键。如果您想要符合
a
和B
现有声明的C
,您需要这样做:
A00, A01, ... A04, A10, A11, ..., A14, A20, ..., A24, ...
int A[5][5];
int B[5][5];
int (*C)[5][5] = &A;
int A[5][5];
int B[5][5];
int (*C[])[5][5] = {&A, &B};
C
的类型被解读为“C
是指向int[5][5]
数组的指针数组”。由于无法分配整个数组,因此需要分配指向该数组的指针
通过此声明,(*C[0])[1][2]
正在访问与A[1][2]
相同的内存位置
如果您想要更简洁的语法,如C[0][1][2]
,则需要执行其他人所述的操作,并动态分配内存:
int **A;
int **B;
// allocate memory for A and each A[i]
// allocate memory for B and each B[i]
int **C[] = {A, B};
您也可以使用莫斯科Vlad建议的语法:
int A[5][5];
int B[5][5];
int (*C[])[5] = {A, B};
C
的声明被理解为“C
是指向int[5]
数组的指针数组”。在这种情况下,C
的每个数组元素的类型为int(*)[5]
,数组的类型为int[5][
int x = (*C[0])[i][j]; // x = A[i][j]
int y = (*C[1])[i][j]; // y = B[i][j]
int (*C[2])[5] = { A, B };
*a == *(a + 0) == a[0]
*C[i] == *(C[i] + 0) == C[i][0]
C[0] == A // int [5][5], decays to int (*)[5]
C[1] == B // int [5][5], decays to int (*)[5]
*C[0] == C[0][0] == A[0] // int [5], decays to int *
*C[1] == C[1][0] == B[0] // int [5], decays to int *
C[0][i] == A[i] // int [5], decays to int *
C[1][i] == B[i] // int [5], decays to int *
C[0][i][j] == A[i][j] // int
C[1][i][j] == B[i][j] // int
int a1[] = {1,2,3,4,5};
int *p1 = a1; // Beginners intuition: If 'p1' is a pointer and 'a1' can be assigned
// to it then arrays are pointers and pointers are arrays.
p1[1] = 0; // Oh! I was right
a1[3] = 0; // Bruce Wayne is the Batman! Yeah.
int a2[][5] = {{0}};
int **p2 = a2;
int a3[][5][10] = {{{0}}};
int ***p3 = a3; // "?"
int a1[] = {1,2,3,4,5};
int *p1 = a1;
int a2[][5] = {{0}};
int **p2 = a2;
int (*p2)[5] = a2;
int a3[][5][10] = {{{0}}};
int ***p3 = a3;
int (*p3)[5][10] = a3;
int A[5][5];
int B[5][5];
int*** C = {&A, &B};
int (*C[2])[5][5] = {&A, &B};
int*** C = &A;
#define SIZE 5
typedef int OneD[SIZE]; // OneD is a one-dimensional array of ints
typedef OneD TwoD[SIZE]; // TwoD is a one-dimensional array of OneD's
// So it's a two-dimensional array of ints!
TwoD a;
TwoD b;
TwoD *c[] = { &a, &b, 0 }; // c is a one-dimensional array of pointers to TwoD's
// That does NOT make it a three-dimensional array!
int main() {
for (int i = 0; c[i] != 0; ++i) { // Test contents of c to not go too far!
for (int j = 0; j < SIZE; ++j) {
for (int k = 0; k < SIZE; ++k) {
// c[i][j][k] = 0; // Error! This proves it's not a 3D array!
(*c[i])[j][k] = 0; // You need to dereference the entry in c first
} // for
} // for
} // for
return 0;
} // main()