C++ 使用恒定长度时 char[] 和 new char[] 之间的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11226680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between char[] and new char[] when using constant lengths
提问by Qix - MONICA WAS MISTREATED
So this may seem like a widely-answered question, but I'm interested more in the internals of what exactly happens differently between the two.
所以这似乎是一个广泛回答的问题,但我更感兴趣的是两者之间究竟发生了什么不同的内部结构。
Other than the fact that the second example creates not only the memory, but a pointer to the memory, what happens in memorywhen the following happens:
除了第二个示例不仅创建内存,而且创建指向 memory 的指针这一事实之外,当发生以下情况时,内存中会发生什么:
char a[5];
char b* = new char[5];
And more directly related to whyI asked this question, how come I can do
更直接地与我问这个问题的原因有关,我怎么能这样做
const int len = 5;
char* c = new char[len];
but not
但不是
const int len = 5;
char d[len]; // Compiler error
EDITShould have mentioned I'm getting this compiler error on VC++ (go figure...)
编辑应该提到我在 VC++ 上遇到这个编译器错误(去图...)
1>.\input.cpp(138) : error C2057: expected constant expression
1>.\input.cpp(138) : error C2466: cannot allocate an array of constant size 0
1>.\input.cpp(138) : error C2133: 'd' : unknown size
EDIT 2: Should have posted the exact code I was working with. This error is produced when the constant length for the dynamically allocated array is calculated with run-time values.
编辑 2:应该发布我正在使用的确切代码。当使用运行时值计算动态分配数组的常量长度时,会产生此错误。
Assuming random(a,b)
returns an int
between a
and b
,
假设random(a,b)
返回int
介于a
和之间b
,
const int len1 = random(1,5);
char a[len1]; // Errors, since the value
// is not known at compile time (thanks to answers)
whereas
然而
const int len2 = 5;
char b[len2]; // Compiles just fine
回答by James Kanze
The difference is the lifetime of the array. If you write:
不同之处在于数组的生命周期。如果你写:
char a[5];
then the array has a lifetime of the block it's defined in (if it's defined in block scope), of the class object which contains it (if it's defined in class scope) or static lifetime (if it's defined at namespace scope). If you write:
然后数组具有它定义的块的生命周期(如果它在块范围内定义),包含它的类对象(如果它在类范围内定义)或静态生命周期(如果它在命名空间范围内定义)。如果你写:
char* b = new char[5];
, then the array has any lifetime you care to give it—you must explicitly terminate its lifetime with:
,那么该数组有任何你想给它的生命周期——你必须显式地终止它的生命周期:
delete [] b;
And with regards to your last question:
关于你的最后一个问题:
int const len = 5;
char d[len];
is perfectly legal, and should compile. Where there is a difference:
是完全合法的,应该编译。有区别的地方:
int len = 5; // _not_ const
char d[len]; // illegal
char* e = new char[len]; // legal
The reason for the difference is mostly one of compiler technology and history: in the very early days, the compiler had to know the length in order to create the array as a local variable.
差异的原因主要是编译器技术和历史之一:在早期,编译器必须知道长度才能将数组创建为局部变量。
回答by Steve Jessop
what happens in memory when the following happens:
发生以下情况时内存中会发生什么:
char a[5];
char *b = new char[5];
Assuming a typical but somewhat simplified C++ implementation, and that the above code appears in a function:
假设一个典型但有些简化的 C++ 实现,并且上面的代码出现在一个函数中:
char a[5];
The stack pointer is moved by 5 bytes, to make a 5-byte space. The name a
now refers to that block of 5 bytes of memory.
堆栈指针移动 5 个字节,以形成 5 个字节的空间。该名称a
现在指的是那块 5 字节的内存。
char *b = new char[5];
The stack pointer is moved by sizeof(char*)
, to make space for b
. A function is called, that goes away and allocates 5 bytes from a thing called the "free store", basically it carves 5 or more bytes off a big block of memory obtained from the OS, and does some book-keeping to ensure that when you free those bytes with delete[]
, they will be made available for future allocations to re-use. It returns the address of that allocated block of 5 bytes, which is stored into the the space on the stack for b
.
堆栈指针移动sizeof(char*)
, 为 腾出空间b
。一个函数被调用,它消失并从一个叫做“自由存储”的东西中分配 5 个字节,基本上它从从操作系统获得的一大块内存中分割出 5 个或更多字节,并做一些簿记以确保当您使用 释放这些字节delete[]
,它们将可用于将来的分配以重新使用。它返回分配的 5 字节块的地址,该地址存储在堆栈上的空间中b
。
The reason that the second is more work than the first is that objects allocated with new
can be deleted in any order. Local variables (aka "objects on the stack") are always destroyed in reverse order of being created, so less book-keeping is needed. In the case of trivially-destructible types, the implementation can just move the stack pointer by the same distance in the opposite direction.
第二个比第一个工作多的原因是分配的对象new
可以按任何顺序删除。局部变量(又名“堆栈上的对象”)总是以创建的相反顺序销毁,因此需要较少的簿记。在平凡可破坏类型的情况下,实现可以只将堆栈指针向相反方向移动相同的距离。
To remove some of the simplifications I made: the stack pointer isn't really moved once for each variable, possibly it's only moved once on function entry for all variables in the function, in this case the space required is at least sizeof(char*) + 5
. There may be alignment requirements on the stack pointer or the individual variables which mean it's not moved by the size required, but rather by some rounded-up amount. The implementation (usually the optimizer) can eliminate unused variables, or use registers for them instead of stack space. Probably some other things I haven't thought of.
为了消除我所做的一些简化:堆栈指针实际上并没有为每个变量移动一次,可能它只在函数中的所有变量的函数入口处移动一次,在这种情况下所需的空间至少为sizeof(char*) + 5
. 堆栈指针或单个变量可能有对齐要求,这意味着它不会按所需大小移动,而是按一些四舍五入的量移动。实现(通常是优化器)可以消除未使用的变量,或者为它们使用寄存器而不是堆栈空间。可能还有一些我没有想到的事情。
const int len1 = random(1,5);
const int len1 = random(1,5);
The language rule is reasonably simple: the size of an array must be a constant expression. If a const int
variable has an initializer in the same TU, and the initializer is a constant expression, then the variable name can be used in constant expressions. random(1,5)
is not a constant expression, hence len1
cannot be used in constant expressions. 5
is a constant expression, so len2
is fine.
语言规则相当简单:数组的大小必须是常量表达式。如果一个const int
变量在同一个TU中有一个初始化器,并且初始化器是一个常量表达式,那么变量名就可以用在常量表达式中。random(1,5)
不是常量表达式,因此len1
不能用于常量表达式。5
是一个常量表达式,所以len2
很好。
What the language rule is there for, is to ensure that array sizes are known at compile time. So to move the stack, the compiler can emit an instruction equivalent to stack_pointer -= 5
(where stack_pointer
will be esp
, or r13
, or whatever). After doing that, it still "knows" exactly what offsets every variable has from the new value of the stack pointer -- 5 different from the old stack pointer. Variable stack allocations create a greater burden on the implementation.
语言规则的目的是确保在编译时知道数组大小。因此,要移动堆栈,编译器可以发出一条等价于stack_pointer -= 5
(where stack_pointer
will be esp
, or r13
, or what will)的指令。这样做之后,它仍然“知道”每个变量与堆栈指针的新值的确切偏移量 - 与旧堆栈指针不同 5。可变堆栈分配给实现带来了更大的负担。
回答by Eitan T
what happens in memory when the following happens:
char a[5]; char b* = new char[5];
发生以下情况时内存中会发生什么:
char a[5]; char b* = new char[5];
char a[5]
allocates 5 chars on the stack memory.new char[5]
allocates 5 chars on the heap memory.
char a[5]
在堆栈内存上分配 5 个字符。new char[5]
在堆内存上分配 5 个字符。
And more directly related to why I asked this question, how come I can do:
const int len = 5; char* c = new char[len];
but not
const int len = 5; char d[len]; // Compiler error
更直接地与我问这个问题的原因有关,我怎么能这样做:
const int len = 5; char* c = new char[len];
但不是
const int len = 5; char d[len]; // Compiler error
Both are compiled successfully for me.
两者都为我成功编译。
回答by j4x
In C++ you can't have dynamic arrays in stack. C99 has this feature, but not C++.
在 C++ 中,堆栈中不能有动态数组。C99 有这个特性,但 C++ 没有。
When you declare char d[ len ]
you are allocating space on stack.
When you do char *c = new char[ len ]
you allocate space on heap.
当您声明时,char d[ len ]
您正在stack上分配空间。当你这样做时,你会char *c = new char[ len ]
在heap上分配空间。
The heap has its manager and can allocate variable amounts of memory. In C++, the stack must be allocated by constant expressionvalues, so the compiler has room for lots of optimizations. The compiler is aware of how much space will be spent on a given context this way and is able to predict stack frames. With dynamic arrays, it wouldn't be possible, so the language staff decided to forbid it (at least until C++11).
堆有它的管理器,可以分配可变数量的内存。在 C++ 中,堆栈必须由常量表达式值分配,因此编译器有很多优化空间。编译器知道以这种方式将在给定上下文上花费多少空间,并且能够预测堆栈帧。对于动态数组,这是不可能的,所以语言工作人员决定禁止它(至少在 C++11 之前)。
回答by weggo
char a[5]
allocates 5 sizeof(char)
bytes to stack memory, when new char[5]
allocates those bytes to heap memory. Bytes allocated to stack memory is also guaranteed to be freed when scope ends, unlike heap memory where you should free the memory explicitly.
char a[5]
将 5sizeof(char)
个字节分配给堆栈内存,当new char[5]
将这些字节分配给堆内存时。分配给堆栈内存的字节也保证在作用域结束时被释放,这与堆内存不同,您应该显式释放内存。
char d[len]
should be allowed since the variable is declared const and thus the compiler can easily make the code to allocate those bytes to stack memory.
char d[len]
应该允许,因为变量被声明为 const,因此编译器可以轻松地编写代码以将这些字节分配到堆栈内存。
回答by Daniel
The third pair of lines should work, that should not be a compiler error. There must be something else going on there.
第三对行应该可以工作,这不应该是编译器错误。那里一定有其他事情发生。
The difference between the first two examples is that the memory for char a[5];
will automatically be freed, while char* b = new char[5];
allocates memory on that will not be freed until you expressly free it. An array that you allocate the first way can not be used once that particular variable goes out of scope because its destructor is automatically called and the memory is free to be overwritten. For an array created using new
, you may pass the pointer around and use it freely outside the scope of the original variable, and even outside of the function in which it was created until you delete
it.
前两个示例之间的区别在于,for 的内存char a[5];
将被自动释放,而在其上char* b = new char[5];
分配的内存在您明确释放它之前不会被释放。一旦该特定变量超出范围,就不能使用以第一种方式分配的数组,因为它的析构函数会被自动调用并且内存可以被自由覆盖。对于使用创建的数组new
,您可以传递指针并在原始变量的范围之外自由使用它,甚至在创建它的函数之外,直到您使用delete
它。
Something that you cannot do is:
你不能做的是:
int a = 5;
int *b = new int[a];
For dynamic memory allocation, the size must be known at compile time.
对于动态内存分配,必须在编译时知道大小。
回答by Fabien
Your a array is allocated on the stack ; that means, once the program is compiled, it knows it will have to reserve 5 bytes to stores the chars of a. On the opposite, b is just declared as a pointer, and its content will be allocated at runtime on the heap, and that can fail if memory is too scarce. Finally, as be has been newed, it must be deleted at some point, or you will be leaking memory.
您的数组分配在堆栈上;这意味着,一旦程序被编译,它就知道它必须保留 5 个字节来存储 a 的字符。相反,b 只是被声明为一个指针,它的内容将在运行时在堆上分配,如果内存太少,这可能会失败。最后,由于是新的,必须在某个时候删除它,否则你会泄漏内存。
回答by ltjax
When you're using new, you are allocating memory from the free-store/heap and you need to take care of releasing it yourself. Also, locating the free memory might actually take some time, as will freeing it.
当您使用 new 时,您正在从空闲存储/堆分配内存,您需要自己释放它。此外,定位空闲内存实际上可能需要一些时间,释放它也是如此。
When you are not using new, your memory gets reserved on the stack and is implicitly allocated and freed. I.e. when you enter a function, the call stack will just expand by the size of all your local variables (at least conceptually - for example, some variables can exist entirely in registers) and it will just be decremented when you leave the function.
当您不使用 new 时,您的内存将保留在堆栈上并隐式分配和释放。即,当您进入一个函数时,调用堆栈将只扩展所有局部变量的大小(至少在概念上 - 例如,某些变量可以完全存在于寄存器中),并且在您离开该函数时它只会递减。
When you allocate a variable with dynamic size on the stack as in your last example, it means that you need some additional information when entering the function scope. Specifically, the amount of space that needs to be reserved varies depending on the function inputs. Now if the context can be determined at the beginning of the function, everything is well - which is presumably why this is allowed in C99 - but if you have a variable for the size who's value you only know mid-function, you end up adding "fake" function calls. Together with C++'s scoping rules, this can get quite hairy, so it's conceptually a lot easier to just let C++ scoping take care of this via std::vector.
当您在堆栈上分配动态大小的变量时,如上一个示例所示,这意味着您在进入函数作用域时需要一些附加信息。具体而言,需要保留的空间量因函数输入而异。现在,如果上下文可以在函数的开头确定,那么一切都很好——这大概就是为什么在 C99 中允许这样做的原因——但是如果你有一个变量来表示你只知道函数中间的值的大小,你最终会添加“假”函数调用。与 C++ 的作用域规则一起,这可能会变得非常麻烦,所以从概念上讲,让 C++ 作用域通过 std::vector 来处理这个问题要容易得多。