C 指向数组和二维数组表示形式的指针

C 指向数组和二维数组表示形式的指针,c,arrays,pointers,C,Arrays,Pointers,我对数组指针和2D数组的语法有疑问 #include<stdio.h> #include<stdlib.h> void show ( int q[][4], int row ) { int i, j ; for ( i = 0 ; i < row ; i++ ) { for ( j = 0 ; j < 4 ; j++ ) printf ( "%d ", q[i][j] ) ; printf ( "\n" ) ;

我对数组指针和2D数组的语法有疑问

#include<stdio.h>
#include<stdlib.h>

void show ( int q[][4], int row )
{
  int i, j ;
  for ( i = 0 ; i < row ; i++ )
  {
     for ( j = 0 ; j < 4 ; j++ )
      printf ( "%d ", q[i][j] ) ;
    printf ( "\n" ) ;
  }
  printf ( "\n" ) ;
}

int main( )
{
 int a[][4] = {
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 0, 1, 6}
              } ;
 show ( a, 3, 4 ) ;
 return 0;
}
我读过这个

intq[][4]
这与
int(*q)[4]
相同,其中
q
是指向4的数组的指针 整数。唯一的优点是我们现在可以使用更多 用于访问数组元素的熟悉表达式
q[i][j]

问题:
show()
函数与这两个函数一起使用,
int q[][4]
int q[3][4]
来接收2D数组的地址

但这些是2D数组的表示,对吗

我们不能像下面这样分配2D

  int (*a)[4] = {                 
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 0, 1, 6}
              } ; 
但2D可以通过此语句进行分配

     int a[][4]={                 
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 0, 1, 6}
              } ; 

int(*a)[4]
int a[][4]
不同,那么为什么“指向数组的指针”和“二维数组表示法”都可以在show函数中使用呢?

数组不是指针,但在许多情况下,数组会衰减为指针,例如当作为函数参数传递时。因此,当您将
int q[][4]
传递给函数时,函数将收到一个
int(*q)[4]
,一个指向四个
int
数组的指针。这就是为什么这两种形式都用作
show()
的参数。但是对于声明和初始化,实际类型很重要,因此不能使用数组初始值设定项初始化指针。

函数有关于数组参数的特殊规则,基本规则是,如果您说出函数的原型,并且您的第一个单词是数组,则将其更改为“指针指向”,以便

在一个函数中,这种转换不会发生,数组类型和指针类型之间有很大的区别

int a[] = {5, 3, 2};
这是合法的,但是

int *a = {5, 3, 2};
事实并非如此。通常,不能从数组初始值设定项中指定指针。相反,c99提供复合文字:

int *a = (int[]){5, 3, 2};

int (*a)[4] = (int [][4]){
     {2, 2, 3, 1},
     {2, 3, 5, 3},
};

首先,C标准中的一些语言:

6.3.2.1左值、数组和函数指示符

3除非它是
sizeof
运算符或一元
&
运算符的操作数,或者是用于初始化数组的字符串文字,类型为“类型的数组”的表达式转换为类型为“指向类型的指针”的表达式,该表达式指向数组对象的初始元素,并且不是左值。如果数组对象具有寄存器存储类,则行为未定义。 在调用
show
函数时,表达式
a
的类型为“int
的4元素数组的3元素数组”。根据上述规则,在进行调用之前,此表达式将替换为“指向
int
的4元素数组的指针”类型的表达式;因此,
show
接收类型为
int(*)[4]
的指针值,而不是数组

更标准的语言:

6.7.5.3功能声明器(包括原型)

7作为“类型数组”的参数声明应调整为“类型限定指针”,其中类型限定符(如有)是数组类型派生的
[
]
中指定的类型限定符。如果关键字
static
也出现在数组类型派生的
[
]
中,则对于函数的每次调用,对应的实际参数的值应提供对数组第一个元素的访问,该元素的数量至少与大小表达式指定的元素数量相同。 因此,在函数参数声明的上下文中,
ta[n]
ta[]
ta*a
都是等效的。如果
T
是数组类型,例如“int的4元素数组”,那么
int a[3][4]
int a[][4]
int(*a)[4]
都是等价的

请注意,这仅适用于函数参数声明。声明数组对象时,将使用以下语言:

6.7.8初始化

22如果初始化了未知大小的数组,则其大小由具有显式初始值设定项的最大索引元素确定。在初始值设定项列表的末尾,数组不再具有不完整的类型。 所以当你写作的时候

int a[][4] = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 0, 1, 6}
          } ;
您正在声明一个
int
的Nx4数组,而不是指向
int
数组的指针——内部维度3是从初始值设定项中的4元素数组数推断出来的

有了
int(*q)[4]
和int
q[][4]
q[i][j]
就可以了。
int *a = (int[]){5, 3, 2};

int (*a)[4] = (int [][4]){
     {2, 2, 3, 1},
     {2, 3, 5, 3},
};
int a[][4] = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 0, 1, 6}
          } ;