微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

声明数组而不指定行大小?

如何解决声明数组而不指定行大小?

a[][3] 声明如何正确,而 a[3][] 在 C 中是错误的?

解决方法

您可以为不完整类型的对象声明标识符。例如,考虑以下声明:

extern int a[][3];

这告诉编译器,当使用 a 时,它指的是一个由三个 int 组成的数组。如果使用它,则必须在其他地方定义该数组。但是,要使用这个数组,编译器不需要知道它的大小。如果 a 的起始地址为 x 并且 int 为四个字节,我们可以通过以 x 开头来计算元素 a[i][j] 的地址>,加上三个i的{​​{1}}数组的大小(每个数组3•4个字节)和int j的大小(每个4个字节)。所以int的地址是x + 3•4•a[i][j] + 4•i

在定义这个数组的地方,不能不完整;编译器需要知道完整的大小才能知道要保留多少内存。

虽然可以为不完整类型的对象声明标识符,但数组元素必须具有完整类型。此规则在 C 2018 6.7.6.2 1 中说明:“元素类型不得为不完整或函数类型。”在上面的计算中,如果我们不知道子数组的大小,我们就无法计算 j 的地址。所以声明 a[i][j] 是不允许的。

注意,在定义带有初始化器的数组时,我们可以省略第一维。但是,它仍然是通过初始化器指定的;编译器检查初始值设定项并找出第一个维度必须是什么。所以,当数组被定义时,它的类型是完整的。例如,在:

a[3][]

编译器会看到有两个子数组并且知道int a[][3] = { { 0,1,2 },{ 3,4,5 } }; 的类型是a。这对于其他维度是不允许的,因为它会造成复杂性。尽管我在上面使用了结构良好的大括号,但该语言允许省略它们,如下所示:

int [2][3]

如果省略第一个维度之外的任何维度,那么初始化器初始化哪些元素以及它们暗示维度是什么就会产生歧义。

,

重要的是要记住,在初始化二维数组时,它是 有必要提到第二个(列)维度,而第一个 维度(行)是可选的 -

因此声明,

int arr[ 2 ][ 3 ] = { 12,34,23,45,56,45 } ;
int arr[ ][ 3 ] = { 12,45 } ;

完全可以接受,

然而,

int arr[ 2 ][ ] = { 12,45 } ;
int arr[ ][ ] = { 12,45 } ;

永远行不通。

假设你是一个编译器,你被告知二维数组中有 3 行。您将如何根据此信息计算元素的位置? 在内部,所有这些元素都将以顺序格式存储,并且要计算该位置,您肯定需要 col。

arr[i][j] = *( arrr + i * col + j )

问题的详细回答为什么? Here

,

这种没有初始化器的声明

T a[][3];

(其中 T 是某种类型说明符)声明了一个不完整类型的数组(数组的大小未知)。以这种方式声明数组可能没有自动存储持续时间。但可以在文件范围内声明。在任何情况下,此类数组的元素类型(在上面的数组示例中,元素类型为 T[3])都应该是完整的。也就是说,您可能不会声明一个数组,例如

T a[][];

T a[3][];

这是一个演示程序,显示了一个允许的数组声明

#include <stdio.h>

int a[][3];

int main(void) 
{
    extern int a[][3];
        
    for ( size_t i = 0; i < 3; i++ )
    {
        a[0][i] = i;
    }

    for ( size_t i = 0; i < 3; i++ )
    {
        printf( "%d ",a[0][i] );
    }
    
    putchar( '\n' );
    
    return 0;
}

文件作用域内的声明

int a[][3];

是一个暂定的定义,等价于 tp

int a[1][3];

编译器可以发出警告,声明 int a[][3] 被假定为类似于 int a[1][3]

如果您将使用初始化器,例如

int a[][3] =
{
    { 1,2,3 },{ 4,5,6 }
}; 

然后编译器会通过初始化器的数量来确定数组中元素的数量。上述数组的类型为 int [2][3]。或者如果你会写例如

int a[][3] = { [10] = { 1,3 } };

那么数组将有 11 个元素。即数组的类型将为 int[11][3]

您也可以使用不完整类型的数组作为函数参数,例如

void f( int a[][3] );

编译器会将参数调整为指向数组元素类型的指针类型,如

void f( int ( *a )[3] );

也就是说,这两个函数声明声明了同一个函数,并且两者可以同时出现在程序中,尽管编译器会发出一条消息,表明存在冗余声明。

有趣的是,带有可变长度数组参数的函数声明可以像这样声明

void f( size_t,size_t,int[*][*] );

您也可以声明一个指向不完整数组类型的指针,前提是您不会取消引用该指针。这是一个演示程序。

#include <stdio.h>

int main(void) 
{
    int ( *p )[];
    
    printf( "sizeof( p ) = %zu\n",sizeof p );
    
    return 0;
}

它的输出可能看起来像

sizeof( p ) = 8

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。