C++中使用什么类型来定义数组大小

What type is used in C++ to define an array size?

本文关键字:定义 数组 类型 什么 C++      更新时间:2023-10-16

avr-gcc中为8位微控制器、行编译一些测试代码

const uint32_t N = 65537;
uint8_t values[N]; 

我收到了以下编译警告(默认情况下应该是一个错误,真的)

warning: conversion from 'long unsigned int' to 'unsigned int' changes value from '65537' to '1' [-Woverflow]
uint8_t values[N];

请注意,为该目标编译时,sizeof(int)为2。

因此,在一个数组中,大小似乎不能超过unsigned int的大小。

我说得对吗?这个GCC是特定的还是某些C或C++标准的一部分?

在有人说8位微控制器通常没有足够的内存来容纳这么大的阵列之前,让我先说一下,这与重点无关。

size_t被认为是要使用的类型,尽管它没有得到C或C++标准的正式批准。

其基本原理是sizeof(values)将是该类型(是C和C++标准强制要求的),并且元素的数量不一定大于此,因为对象的sizeof至少为1。

因此,似乎在数组大小不能超过unsigned int

在您的特定C[++]实现中,情况似乎就是这样。

我说得对吗?这个gcc是特定的还是某些C或C的一部分++标准

它不是GCC的一般特征,也不是由C或C++标准指定的。这是您特定实现的一个特点:用于特定计算平台的GCC版本。

C标准要求指定数组元素数的表达式具有整数类型,但它没有指定特定的整数类型。我确实觉得奇怪的是,你的GCC似乎声称它给了你一个数组,它的元素数量与你指定的不同。我认为这不符合标准,我认为这作为一种扩展也没有多大意义。我更希望看到它拒绝代码。

我将用"不正确且不完整"的ISO CPP标准草案n4659中的规则来剖析这个问题。我强调了这一点。

11.3.4定义了数组声明。第一段包含

如果存在[方括号之间](8.20)的常量表达式,则它应是std::size_t[…]类型的转换常量表达式

std::size_t来自<cstddef>,定义为

[…]一个实现定义的无符号整数类型,其大小足以包含任何对象的字节大小。

由于它是通过C标准库头导入的,因此C标准与size_t的属性相关。ISO C草案N2176在7.20.3中规定了整数类型的"最小最大值"。对于size_t,最大值为65535。换句话说,一个16位的size_t是完全一致的。

8.20/4:中定义了"转换常数表达式">

T类型的转换常量表达式是一个隐式转换为T类型的表达式,其中转换表达式是一个常量表达式,隐式转换序列仅包含[10个不同转换中的任何一个,其中一个涉及整数(第4.7段):]

--积分转换(7.8)而非窄化转换(11.6.4)

积分转换(与将类型更改为等效或更大类型的提升相反)定义如下(7.8/3):

整数类型的prvalue可以转换为另一个整数类型的pr值。

7.8/5然后从积分转换中排除积分promotion这意味着转换通常是缩小类型更改。

缩小转换(您会记得,在用于数组大小的转换常量表达式中,这些转换被排除在允许的转换列表之外)是在列表初始化11.6.4第7段的上下文中定义的

缩小转换是一种隐式转换
[…]
7.31--从整数类型[…]到不能表示原始类型的所有值的整数类型,除非源是一个常量表达式,其值在整数提升后将适合目标类型

这实际上是说有效数组大小必须是显示时的常数值,这是避免意外的完全合理的要求。


现在让我们把它们拼凑在一起。工作假设是std::size_t是一个16位无符号整数类型,取值范围为0..65535。整数字面值65537在系统的16位unsigned int中不可表示,因此具有类型long。因此,它将进行整数转换这将是一个缩小转换,因为该值在16位size_t2中不可表示,因此11.6.4/7.3中的异常条件"值无论如何都适合"不适用。

那么这意味着什么呢?

11.6.4/3.11是针对未能从初始化器列表中的项生成初始化器值的包罗万象的规则。因为初始值设定项列表规则用于数组大小,所以我们可以假设转换失败的catch-all适用于数组大小常量:

(3.11)--否则,程序格式错误

需要一个符合要求的编译器来生成诊断,它确实做到了。案件结案。


1是的,他们细分段落

2将一个65537的整数值(在任何类型中都可以保存该数字,这里可能是"长")转换为一个16位无符号整数是一个定义的操作。7.8/2详细信息:

如果目标类型是无符号的,则得到的值是与源一致的最小无符号整数整数(模2n,其中n是用于表示无符号类型的位数)。[注:在两个补码表示,这种转换是概念性的,位模式没有变化(如果有没有截断)--结束注释]

65537的二进制表示为1_0000_0000_0000_0001,即仅设置较低的16位中的最低有效位。转换为16位无符号值(间接证据表明size_t是)计算[表达式值]模2^16,即只取较低的16位。这导致编译器诊断中提到的值为1

在您的实现中,size_t被定义为unsigned intuint32_t被定义为一个long unsigned int。创建C数组时,编译器会将数组大小的参数隐式转换为size_t

这就是你收到警告的原因。您正在使用uint32_t指定数组大小参数,该参数将转换为size_t,但这些类型不匹配。

这可能不是你想要的。请改用size_t

sizeof返回的值的类型为size_t

它通常被用作数组中元素的数量,因为它将具有足够的大小。size_t总是无符号的,但它是由实现定义的。最后,它定义了实现是否可以支持SIZE_MAX字节的对象。。。甚至接近它。

[这个答案是在问题被标记为C和C++时写的。我还没有根据OP的启示重新检查它,他们使用的是C++而不是C。]

size_t是C标准指定的用于处理对象大小的类型。然而,这并不是正确尺寸的万全之策。

size_t应该在<stddef.h>报头中定义(也可以在其他报头中定义)。

C标准不要求在声明中指定数组大小的表达式的类型为size_t,也不要求它们适合size_t。当C实现不能满足对数组大小的请求时,特别是对于可变长度数组,它没有指定应该做什么。

在您的代码中:

const uint32_t N = 65537;
uint8_t values[N];

CCD_ 37被声明为可变长度数组。(虽然我们可以看到N的值在编译时很容易知道,但它不符合C对常量表达式的定义,因此uint8_t values[N];可以作为可变长度数组的声明。)正如您所观察到的,GCC警告您,32位无符号整数N已缩窄为16位无符号整型。C标准不需要此警告;这是编译器提供的礼貌。除此之外,根本不需要转换——因为C标准没有指定数组维度的类型,所以编译器可以接受这里的任何整数表达式。因此,它插入了一个隐式转换到数组维度所需的类型,并警告您这是编译器的一个特性,而不是C标准的特性。

想想如果你写:会发生什么

size_t N = 65537;
uint8_t values[N];

现在uint8_t values[N];中没有警告,因为在需要16位整数的地方使用了16位整数(C实现中size_t的宽度)。但是,在这种情况下,编译器可能会在size_t N = 65537;中发出警告,因为65537将具有32位整数类型,并且在初始化N期间会执行缩小转换。

然而,使用可变长度数组的事实表明,您可能在运行时计算数组大小,这只是一个简化的示例。可能您的实际代码没有像这样使用常量大小;它可以在执行期间计算大小。例如,您可以使用:

size_t N = NumberOfGroups * ElementsPerGroup + Header;

在这种情况下,有可能计算出错误的结果。如果变量都具有类型size_t,则结果可能很容易换行(有效地溢出了size_t类型的限制)。在这种情况下,编译器不会向您发出任何警告,因为这些值的宽度都相同;没有缩小转换,只是溢出。

因此,使用size_t不足以防止数组维度中的错误

另一种选择是使用一种您希望足够宽的类型来进行计算,可能是uint32_t。给定NumberOfGroupsuint32_t等类型,则:

const uint32_t N = NumberOfGroups * ElementsPerGroup + Header;

将产生CCD_ 52的正确值。然后您可以在运行时对其进行测试,以防止出现错误:

if ((size_t) N != N)
Report error…
uint8_t values[(size_t) N];