有没有什么办法来以编程方式确定一个C ++数组的大小? 如果没有,为什么?

这个问题受到类似问题的启发: 如何删除[]“知道”操作数数组的大小?

我的问题是有点不同: 有没有什么办法来以编程方式确定一个C ++数组的大小? 如果没有,为什么? 我见过的每个函数都需要一个整数参数来给它一个数组。 但是正如链接问题所指出的那样, delete[]必须知道要释放的内存的大小。

考虑一下这个C ++代码:

 int* arr = new int[256]; printf("Size of arr: %d\n", sizeof(arr)); 

这将打印“ Size of arr: 4 ”,这只是指针的大小。 有一些打印256的函数会很好,但是我不认为它存在于C ++中。 (同样,问题的一部分是为什么它不存在。)

澄清 :我知道,如果我在栈上而不是堆声明数组(即“ int arr[256]; ”), sizeof运算符将返回1024(数组长度* sizeof(int))。

delete []确实知道分配的大小。 但是,这些知识存在于运行时或操作系统的内存pipe理器中,这意味着编译期间编译器无法使用这些知识。 sizeof()不是一个真正的函数,它实际上是由编译器计算出来的一个常量,这对于dynamic分配的数组来说是不能做的,在编译的时候大小是不知道的。

另外,考虑这个例子:

 int *arr = new int[256]; int *p = &arr[100]; printf("Size: %d\n", sizeof(p)); 

编译器如何知道p的大小是多less? 问题的根源在于C和C ++中的数组不是一stream的对象。 它们衰变为指针,编译器或程序本身无法知道指针是指向由new或单个对象分配的一块内存的开始,还是指向一个中间的某个地方由new分配的内存块。

原因之一是C和C ++将内存pipe理留给程序员和操作系统,这也是为什么他们没有垃圾收集的原因。 newdelete实现不是C ++标准的一部分,因为C ++的目的是在各种平台上使用,这些平台可能以不同的方式pipe理它们的内存。 如果您正在为最新的英特尔CPU上运行的Windows系统编写一个文字处理器,那么可以让C ++跟踪所有已分配的数组及其大小,但是在编写embedded式系统时,可能完全不可行一个DSP。

不,在Standard C ++中没有办法做到这一点。

没有很好的理由为什么我不知道。 大小可能被认为是实现细节,最好不要暴露。 请注意,当您说malloc(1000)时,不能保证返回的块是1000字节 – 只有至less 1000字节。 最有可能的是大约1020(1K减4字节的开销)。 在这种情况下,“1020”大小对于运行时间库是非常重要的。 当然,这会在实现之间改变。

这就是为什么标准委员会增加了std:vector <>,它确实跟踪了它的大小。

那么实际上有一种方法来确定大小,但它不是“安全的”,将从编译器到编译器不同, 所以它不应该被使用

当你这样做:int * arr = new int [256];

256是不相关的,你会得到256 * sizeof(int)假设这种情况下1024,这个值将被存储在(arr – 4)

所以给你一些“物品”

int * p_iToSize = arr – 4;

printf(“项目数%d”,* p_iToSize / sizeof(int));

对于每个malloc,new,无论在接收到的连续内存块之前,还会为保留的内存块分配一个空间。

处理这个问题的常用方法是使用vector

 int main() { std::vector<int> v(256); printf("size of v is %i capacity is %i\n", sizeof(int) * v.size(), sizeof(int) * v.capacity()); } 

或预定义大小

 const int arrSize = 256; int main() { int array[arrSize]; printf("Size of array is %i", sizeof(int) * arrSize); } 

C ++决定添加一个新的types安全malloc,比新的必须知道调用ctors的大小e个元素的数量,所以删除调用dtors。 在早期你必须真正通过删除你传递给新对象的数字。

 string* p = new string[5]; delete[5] p; 

但是他们认为如果使用新的<type> [],数字的开销很小。 所以他们决定new [n]必须记住n并通过删除。 有三种主要的实现方法。

  1. 保持指针大小的哈希表
  2. 直接写在vector附近
  3. 做一些完全不同的事情

也许有可能获得这样的大小:

 size_t* p = new size_t[10]; cout << p[-1] << endl; // Or cout << p[11] << endl; 

或者这些都不是。

根据您的应用程序,您可以在arrays末尾创build一个“标记值”。

哨兵值必须有一些独特的财产。

然后,您可以处理数组(或做一个线性search)的哨兵价值,随着你走。 一旦你达到哨兵价值,你有你的arrays数。

对于简单的Cstring,终止\ 0是一个sentinel值的例子。

有些魔力:

 template <typename T, size_t S> inline size_t array_size(const T (&v)[S]) { return S; } 

这就是我们在C ++ 11中的做法:

 template<typename T, size_t S> constexpr auto array_size(const T (&)[S]) -> size_t { return S; } 

那是因为你的variablesarr只是一个指针。 它保存着内存中某个特定位置的地址,而不知道它的任何内容。 你把它声明为int *,这会给编译器一些指示,当你增加指针时该怎么做。 除此之外,您可能会指向数组的开始或结尾,或指向堆栈或无效的内存。 但我同意你,不能够调用sizeof是非常讨厌的:)

QuantumPete

在给定C ++dynamic分配数组的指针的情况下,没有可移植的方法来确定它的大小。 C ++被做成非常灵活,给用户提供权力。 例如,标准没有定义内存分配器如何工作,例如通过添加所需的大小头。 不需要头部允许更多的灵活性。

作为一个例子,考虑一个实现为char *数组的string。 使用指向数组中间的指针来挑出子串是很常见的。 作为一个例子,看标准C库中的strtok函数。 如果需要在每个数组之前embedded一些标头,则需要在子串之前清除数组的部分。

处理标题的另一种方法是将数组标题放在一块内存中,并让它们指向其他地方的原始数组内存。 在许多情况下,这将需要两个指针查找每个参考,这将是一个巨大的性能拖累。 有办法克服这些缺陷,但它们增加了复杂性并降低了实施的灵活性。

std :: vector模板是我最喜欢的方法来保持数组的大小绑定到数组本身。

C是一种语法更好的可移植汇编语言。

不幸的是,这是不可能的。 在C和C ++中,程序员有责任记住数组的长度,因为数组长度不存储在任何地方。 删除[]和free()确实记得分配块的大小,但它们可能分配的内存比请求的多,所以它们的内部数据结构存储分配的内存块的大小可能不会给你数组的确切大小。

请注意,C ++ STL向量,基本上是用一些辅助函数包装在一个类中的数组,所以存储数组的长度,所以如果你确实需要这个function,你可以使用向量。

一般来说,没有。 C和C ++中的数组只是记忆块,没有logging信息。 如果不在内存中存储数组的长度,并增加开销,那么在一般情况下是不可能的。

静态分配的数组有个例外。 例如,如果声明: int a[50]那么sizeof(a)将起作用。 这是可能的,因为[50]是数组静态types的一部分:它是编译器已知的。 sizeof在编译时被解释。

但是,如果你创build一个指针: int *p = a ,那么sizeof(p)将返回指针的大小,而不是数组的大小,因为编译器不知道指向哪个指针。

从根本上说你不能:

 void foo(int* arr); int arr[100] = {0}; foo(arr+1); // Calls foo with a pointer to 100-1 elements. 

C ++数组只不过是存储在连续内存区域中的对象的集合。 由于它们之间没有任何孔(填充对象内部 ),所以只需简单地指示一下数组就可以find数组的下一个元素。 在CPU级别,这是一个简单的调整。 C ++只插入一个sizeof(元素)乘数。

请注意,实现可能select实现包含数组边界的“胖指针”。 他们需要两倍的大,因为你需要链接到某种“数组绑定描述符”。 作为一个副作用,在这样的实现中你可以调用delete [] (1+new int[5]);

不,没有办法做到这一点,你必须跟踪外部有多大。 像std::vector类为你做这个。

现在有一个std :: array ,一个有效的编译时包装一个常量大小的数组:

 #include <array> int main (int argc, char** argv) { std::array<int, 256> arr; printf("Size of arr: %ld\n", arr.size()); } 

参数是<type, #elements>

你还会得到一些其他的细节,比如迭代器,empty()和max_size()。

编译器不知道

 char *ar = new char[100] 

是一个由100个字符组成的数组,因为它不会在内存中创build一个实际的数组,它只会创build一个指向内存中未初始化的100个字节的指针。

如果你想知道给定数组的大小,只需使用std :: vector。 std :: vector只是一个更好的数组。

有没有什么办法来以编程方式确定一个C ++数组的大小? 如果没有,为什么?

  1. 不,除非你自己跟踪。
  2. 因为如果编译器不必告诉除了它自己以外的任何人这个信息,它就会减less编译器。 这是否可取决于辩论。

@Dima,

编译器如何知道p的大小是多less?

编译器必须知道p的大小; 否则,不能实现delete[] 。 编译器不需要告诉其他人它是如何计算出来的。

为了validation这个有趣的方法,将operator new[]返回的指针与new[]返回的指针进行比较。

当你创build数组指针(创build包含模板指针的包装器)时,你不得不创build数组的对象,你可以像这样获得数组的大小:

 char* chars=new char[100]; printf("%d",*((int*)chars-1)); 

delete[]函数需要解构其中的所有对象。 要做到这一点, new[]关键字将元素的数量放在所有数组的后面。

arrays的主体是这样的:

 int count; ObjectType* data; //This value is returned when using new[] 

我这样做的方式是将数组的大小除以第一个元素的大小

 int intarray[100]; printf ("Size of the array %d\n", (sizeof(intarray) / sizeof(intarray[0])); 

它打印100

你可以创build一个额外的数组元素,然后应用最不可能的数字将被存储在数组中。 然后你可以通过传递这个数字来确定一些函数的元素数量。

在创build时声明和初始化数组的情况下,可以对其进行扫描,然后生成一个与数组中任何元素都不匹配的数字。 但是如果你修改了其中一个元素,你就不会知道这个元素是否和最后一个元素存储了相同的值,所以你将不得不生成一个新的数字来存储在最后一个元素中。不妨将创build时的元素总数存储在一个variables中。 如果你只在函数中使用数组,那可能就是这种情况。