这个问题困扰了我一段时间。
例如,如果我做int* a = new int[n]
,我只有一个指向数组a开头的指针,但C / C ++如何知道n
?我知道如果我想将这个数组传递给另一个函数,那么我必须用它传递数组的长度,所以我猜C / C ++并不知道这个数组有多长。
我知道我们可以通过查找NUL终结符来推断字符数组char*
的结尾。但其他数组是否有类似的机制,比如int?同时,char可以不仅仅是一个字符 - 您也可以将其视为整数类型。那么C ++如何知道这个数组的结束呢?
当我开发嵌入式Python时,这个问题开始让我更加困扰(如果你不熟悉嵌入式python,你可以忽略这一段并回答上面的问题。我仍然会欣赏它)。在Python中有一个“ByteArray”,将“ByteArray”转换为C / C ++的唯一方法是使用PyString_AsString()将其转换为char *。但是如果这个ByteArray中有0,那么C / C ++会认为char *数组会提前停止。这不是最糟糕的部分。最糟糕的是,比方说我做了
char* arr = PyString_AsString(something)
void* pt = calloc(1, 1000);
如果st恰好从0开始,那么C / C ++几乎可以保证消灭arr中的所有内容,因为它认为arr在出现NULL之后就会结束。然后它可能只是通过为pt分配一个内存树干来消灭arr中的所有内容。
非常感谢你的时间!对此,我真的非常感激。
答案 0 :(得分:7)
C / C ++没有;它是 allocator (实现malloc()
,free()
等的一小段代码),它知道它有多长。欢迎使用C / C ++,不受担心长度的限制。
答案 1 :(得分:4)
让我们击中反汇编程序!对于C和C ++,这将是不同的。 free
如何在C中工作的问题在另一个问题中有所涉及,以及它在C ++中的工作原理:
struct T {
~T();
int data;
};
void test(T* p)
{
delete[] p;
}
让我们运行编译器来生成程序集。这是为i386编译的相关位:
movl -4(%edi), %eax
leal (%edi,%eax,4), %esi
cmpl %esi, %edi
je L4
.align 4,0x90
L8:
subl $4, %esi
movl %esi, (%esp)
call L__ZN1TD1Ev$stub
cmpl %esi, %edi
jne L8
你可以看到重要的部分:在p
开始之前存在一个整数,其中包含p
的长度,然后代码在p
数组上循环,调用数组中每个项的析构函数。然后调用delete
,这通常很无聊,因为它只调用free
(C函数)。因此,您可以看到C ++ delete
如何用free
表示。
析构函数和异常:基于上面的程序集,您可以注意到如果T
的析构函数抛出异常,那么部分p
数组将获得析构函数被调用,而数组的其余部分则不会。析构函数永远不应该抛出异常。
警告:这只是编译器和运行时可以解决此问题的一种可能方式。 (这里,析构函数由编译器生成的代码调用,而delete
是运行时的一部分。)这些实现方式有很大的余地,而你的可能会有所不同。这也说明了为什么你应该总是调用正确的运算符delete[]
或delete
- 调用错误的运算符会导致各种麻烦,例如踩踏内存并释放无效指针。
关于NUL终结符: NUL终止符出问题的唯一原因是PyString_AsString
和其他类似函数调用strlen
来计算字符串的长度。但是,free
并不关心NUL终结符,相反,它会分别跟踪原始malloc
调用的长度。对于PyString_AsString
(以及strdup
等),这不是一个选项,因为没有可移植的方式来获取内存区域的大小 - malloc
和free
不要公开此功能。此外,您可以将指针传递给位于PyString_AsString
块中间或完全位于其他位置的malloc
。
答案 2 :(得分:0)
c / c ++不知道任何数组的长度,因此您可以轻松跨界访问数组。 c / c ++也不知道char数组的长度。
Char *可以指向字符串,但它不等于字符串。以NULL结尾的字符串是c / c ++的约定。