在阅读C ++ Primer书籍时,我遇到了这样的说法:“数组中元素的数量是数组类型的一部分。”所以我想用下面的代码找出答案:
#include<iostream>
int main()
{
char Array1[]{'H', 'e', 'l', 'p'};
char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};
std::cout<<typeid(Array1).name()<<std::endl; //prints A4_c
std::cout<<typeid(Array2).name()<<std::endl; //prints A6_c
return 0;
}
有趣的是,两个数组上的typeid结果表明它们有所不同。
只想能够深刻理解这个概念。
答案 0 :(得分:20)
幕后发生了什么事?
根据定义,非动态分配的是同类元素的固定大小容器。 N
类型的T
个元素的数组作为N
类型的T
个对象的连续序列布置在内存中。
为什么数组必须具有包含其大小的类型?
我认为数组的类型不必包含它的大小-实际上,您可以使用指针来引用T
对象的连续序列。这样的指针会丢失有关数组的大小信息。
但是,这是一件有用的事情。它提高了类型安全性,并在编译时对有用的信息进行了编码,可以以多种方式使用。例如,您可以使用引用数组在不同大小的数组上重载
void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }
或者将数组的大小作为常量表达式计算出来
template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }
这将如何影响比较数组?
实际上不是。
您无法以比较两个数字(例如int
对象)的相同方式比较C样式的数组。您将必须编写某种词典比较,并确定这对于不同大小的集合意味着什么。 std::vector<T>
provides that,并且相同的逻辑可以应用于数组。
奖金::C ++ 11及更高版本提供std::array
,它是带有容器式接口的C样式数组的包装。应该首选C样式的数组,因为它与其他容器(例如std::vector<T>
)更加一致,并且还支持开箱即用的lexicographical comparisons。
答案 1 :(得分:8)
创建对象时分配给该对象的空间量完全取决于其类型。我所说的分配不是来自new
或malloc
的分配,而是分配的空间,以便您可以运行构造函数并初始化对象。
如果您将结构定义为(例如)
struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space
然后在构造对象时:
A a{'a', 'b'};
您可以将构造对象的过程视为一个过程:
'a'
和'b'
复制到对象)重要的是要注意,所需的2个字节的空间完全由对象的类型决定,函数的参数无关紧要。因此,对于数组,处理过程是相同的,除了现在所需的空间量取决于数组中元素的数量。
char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements
因此a
和b
的类型必须反映以下事实:a
需要足够的空间来容纳1个字符,而b
需要足够的空间来容纳5个字符。这确实意味着这些数组的大小不能突然改变,一旦创建了5个元素的数组,它始终是5个元素的数组。为了拥有大小可能会变化的类似“数组”的对象,您需要动态分配内存,这应该在您的书中有所涉及。
答案 2 :(得分:0)
这是运行库的内部原因。例如,如果考虑以下语句:
unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;
然后清楚地知道问题出在哪里:例如,unsigned int *
的地址必须与sizeof operator
或unsigned int
的地址有关。
对于您在此处看到的其余内容有更详细的解释,但这主要是对Kernighan和Ritchie的 C编程语言,第二版所涵盖内容的概括。打印声明的类型字符串的纯语言文本。