C++ 什么是指针衰减数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1461432/
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
What is array to pointer decay?
提问by Vamsi
What is array to pointer decay? Is there any relation to array pointers?
什么是指针衰减数组?与数组指针有任何关系吗?
采纳答案by phoebus
It's said that arrays "decay" into pointers. A C++ array declared as int numbers [5]
cannot be re-pointed, i.e. you can't say numbers = 0x5a5aff23
. More importantly the term decay signifies loss of type and dimension; numbers
decay into int*
by losing the dimension information (count 5) and the type is not int [5]
any more. Look here for cases where the decay doesn't happen.
据说数组“衰减”为指针。声明为的 C++ 数组int numbers [5]
不能重新指向,即您不能说numbers = 0x5a5aff23
. 更重要的是,衰减一词表示类型和维度的损失;通过丢失维度信息(计数 5)而numbers
衰减为int*
类型,不再是类型int [5]
。在此处查看未发生衰减的情况。
If you're passing an array by value, what you're really doing is copying a pointer - a pointer to the array's first element is copied to the parameter (whose type should also be a pointer the array element's type). This works due to array's decaying nature; once decayed, sizeof
no longer gives the complete array's size, because it essentially becomes a pointer. This is why it's preferred (among other reasons) to pass by reference or pointer.
如果您按值传递数组,那么您真正要做的是复制一个指针——指向数组第一个元素的指针被复制到参数(其类型也应该是数组元素类型的指针)。由于数组的衰减性质,这是有效的;一旦衰减,sizeof
就不再给出完整数组的大小,因为它本质上变成了一个指针。这就是为什么首选(除其他原因外)通过引用或指针传递。
Three ways to pass in an array1:
传入数组1 的三种方法:
void by_value(const T* array) // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])
The last two will give proper sizeof
info, while the first one won't since the array argument has decayed to be assigned to the parameter.
最后两个将提供正确的sizeof
信息,而第一个不会,因为数组参数已经衰减以分配给参数。
1 The constant U should be known at compile-time.
1 常数 U 在编译时应该是已知的。
回答by system PAUSE
Arrays are basically the same as pointers in C/C++, but not quite. Once you convert an array:
数组与 C/C++ 中的指针基本相同,但不完全相同。转换数组后:
const int a[] = { 2, 3, 5, 7, 11 };
into a pointer (which works without casting, and therefore can happen unexpectedly in some cases):
进入一个指针(无需强制转换即可工作,因此在某些情况下可能会意外发生):
const int* p = a;
you lose the ability of the sizeof
operator to count elements in the array:
您将失去sizeof
运算符计算数组中元素的能力:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
This lost ability is referred to as "decay".
这种失去的能力被称为“衰变”。
For more details, check out this article about array decay.
有关更多详细信息,请查看这篇关于数组衰减的文章。
回答by Michael Burr
Here's what the standard says (C99 6.3.2.1/3 - Other operands - Lvalues, arrays, and function designators):
这是标准所说的(C99 6.3.2.1/3 - 其他操作数 - 左值、数组和函数指示符):
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type'' is converted to an expression with type ‘‘pointer to type'' that points to the initial element of the array object and is not an lvalue.
除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为“array of type”的表达式将转换为类型为“pointer to”的表达式type'' 指向数组对象的初始元素并且不是左值。
This means that pretty much anytime the array name is used in an expression, it is automatically converted to a pointer to the 1st item in the array.
这意味着几乎任何时候在表达式中使用数组名称时,它都会自动转换为指向数组中第一项的指针。
Note that function names act in a similar way, but function pointers are used far less and in a much more specialized way that it doesn't cause nearly as much confusion as the automatic conversion of array names to pointers.
请注意,函数名称的行为方式类似,但函数指针的使用要少得多,而且以更专业的方式使用,它不会像将数组名称自动转换为指针那样引起混淆。
The C++ standard (4.2 Array-to-pointer conversion) loosens the conversion requirement to (emphasis mine):
C++ 标准(4.2 数组到指针的转换)将转换要求放宽到(强调我的):
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” canbe converted to an rvalue of type “pointer to T.”
左值或类型“数组NT的”或“结合的T未知的数组”的右值可以被转换成类型的右值“指针T.”
So the conversion doesn't haveto happen like it pretty much always does in C (this lets functions overload or templates match on the array type).
所以转换不会有发生像它几乎总是用C做(这让函数重载或模板的阵列类型相匹配)。
This is also why in C you should avoid using array parameters in function prototypes/definitions (in my opinion - I'm not sure if there's any general agreement). They cause confusion and are a fiction anyway - use pointer parameters and the confusion might not go away entirely, but at least the parameter declaration isn't lying.
这也是为什么在 C 中应该避免在函数原型/定义中使用数组参数的原因(在我看来 - 我不确定是否有任何普遍共识)。它们会引起混乱并且无论如何都是虚构的 - 使用指针参数并且混乱可能不会完全消失,但至少参数声明没有说谎。
回答by John Bode
"Decay" refers to the implicit conversion of an expression from an array type to a pointer type. In most contexts, when the compiler sees an array expression it converts the type of the expression from "N-element array of T" to "pointer to T" and sets the value of the expression to the address of the first element of the array. The exceptions to this rule are when an array is an operand of either the sizeof
or &
operators, or the array is a string literal being used as an initializer in a declaration.
“衰减”是指表达式从数组类型到指针类型的隐式转换。在大多数情况下,当编译器看到数组表达式时,它会将表达式的类型从“T 的 N 元素数组”转换为“指向 T 的指针”,并将表达式的值设置为数组第一个元素的地址. 此规则的例外是当数组是sizeof
or&
运算符的操作数,或者数组是在声明中用作初始化器的字符串文字时。
Assume the following code:
假设以下代码:
char a[80];
strcpy(a, "This is a test");
The expression a
is of type "80-element array of char" and the expression "This is a test" is of type "16-element array of char" (in C; in C++ string literals are arrays of const char). However, in the call to strcpy()
, neither expression is an operand of sizeof
or &
, so their types are implicitly converted to "pointer to char", and their values are set to the address of the first element in each. What strcpy()
receives are not arrays, but pointers, as seen in its prototype:
表达式a
的类型为“80 个字符的字符数组”,表达式“这是一个测试”的类型为“字符的 16 个元素数组”(在 C 中;在 C++ 中,字符串文字是 const char 的数组)。但是,在对 的调用中strcpy()
,两个表达式都不是sizeof
or的操作数&
,因此它们的类型被隐式转换为“指向 char 的指针”,并且它们的值被设置为每个表达式中第一个元素的地址。什么strcpy()
接收不是数组,但是指针,作为其原型看出:
char *strcpy(char *dest, const char *src);
This is not the same thing as an array pointer. For example:
这与数组指针不同。例如:
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Both ptr_to_first_element
and ptr_to_array
have the same value; the base address of a. However, they are different types and are treated differently, as shown below:
两者ptr_to_first_element
和ptr_to_array
具有相同的值;a. 的基地址。但是,它们是不同的类型,并且被区别对待,如下所示:
a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
Remember that the expression a[i]
is interpreted as *(a+i)
(which only works if the array type is converted to a pointer type), so both a[i]
and ptr_to_first_element[i]
work the same. The expression (*ptr_to_array)[i]
is interpreted as *(*a+i)
. The expressions *ptr_to_array[i]
and ptr_to_array[i]
may lead to compiler warnings or errors depending on the context; they'll definitely do the wrong thing if you're expecting them to evaluate to a[i]
.
请记住,表达a[i]
被解释为*(a+i)
(如果该数组类型转换为指针类型仅作品),所以无论a[i]
和ptr_to_first_element[i]
工作是相同的。该表达式(*ptr_to_array)[i]
被解释为*(*a+i)
。表达式*ptr_to_array[i]
andptr_to_array[i]
可能会导致编译器警告或错误,具体取决于上下文;如果您期望他们评估为 ,他们肯定会做错事a[i]
。
sizeof a == sizeof *ptr_to_array == 80
Again, when an array is an operand of sizeof
, it's not converted to a pointer type.
同样,当数组是 的操作数时sizeof
,它不会转换为指针类型。
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform
ptr_to_first_element
is a simple pointer to char.
ptr_to_first_element
是一个简单的指向 char 的指针。
回答by pmg
Arrays, in C, have no value.
在 C 中,数组没有价值。
Wherever the value of an object is expected but the object is an array, the address of its first element is used instead, with type pointer to (type of array elements)
.
只要对象的值是预期的,但对象是数组,则使用其第一个元素的地址,类型为pointer to (type of array elements)
。
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer" (sic); when you compare an array to something else, again it "decays into a pointer" (sic); ...
在函数中,所有参数都是按值传递的(数组也不例外)。当您在函数中传递数组时,它“衰减为指针”(原文如此);当您将数组与其他内容进行比较时,它再次“衰减为指针”(原文如此);...
void foo(int arr[]);
Function foo expects the value of an array. But, in C, arrays have no value! So foo
gets instead the address of the first element of the array.
函数 foo 需要一个数组的值。但是,在 C 中,数组没有价值!所以foo
获取数组的第一个元素的地址。
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
In the comparison above, arr
has no value, so it becomes a pointer. It becomes a pointer to int. That pointer can be compared with the variable ip
.
在上面的比较中,arr
没有值,所以它变成了一个指针。它变成了一个指向 int 的指针。该指针可以与变量进行比较ip
。
In the array indexing syntax you are used to seeing, again, the arr is 'decayed to a pointer'
在您习惯于看到的数组索引语法中,arr 再次“退化为指针”
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
The only times an array doesn't decay into a pointer are when it is the operand of the sizeof operator, or the & operator (the 'address of' operator), or as a string literal used to initialize a character array.
数组不会衰减为指针的唯一时间是当它是 sizeof 运算符或 & 运算符('address of' 运算符)的操作数,或作为用于初始化字符数组的字符串文字时。
回答by Michael Krelin - hacker
It's when array rots and is being pointed at ;-)
这是数组腐烂并被指向的时候;-)
Actually, it's just that if you want to pass an array somewhere, but the pointer is passed instead (because who the hell would pass the whole array for you), people say that poor array decayed to pointer.
实际上,只是如果你想在某处传递一个数组,而是传递了指针(因为谁会为你传递整个数组),人们说糟糕的数组衰减为指针。
回答by Josh Kelley
Array decaying means that, when an array is passed as a parameter to a function, it's treated identically to ("decays to") a pointer.
数组衰减意味着,当数组作为参数传递给函数时,它被视为(“衰减到”)指针。
void do_something(int *array) {
// We don't know how big array is here, because it's decayed to a pointer.
printf("%i\n", sizeof(array)); // always prints 4 on a 32-bit machine
}
int main (int argc, char **argv) {
int a[10];
int b[20];
int *c;
printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
do_something(a);
do_something(b);
do_something(c);
}
There are two complications or exceptions to the above.
上述情况有两种复杂情况或例外情况。
First, when dealing with multidimensional arrays in C and C++, only the first dimension is lost. This is because arrays are layed out contiguously in memory, so the compiler must know all but the first dimension to be able to calculate offsets into that block of memory.
首先,在C和C++中处理多维数组时,只丢失了第一维。这是因为数组在内存中是连续排列的,因此编译器必须知道除第一个维度之外的所有维度,才能计算该内存块的偏移量。
void do_something(int array[][10])
{
// We don't know how big the first dimension is.
}
int main(int argc, char *argv[]) {
int a[5][10];
int b[20][10];
do_something(a);
do_something(b);
return 0;
}
Second, in C++, you can use templates to deduce the size of arrays. Microsoft uses this for the C++ versions of Secure CRT functions like strcpy_s, and you can use a similar trick to reliably get the number of elements in an array.
其次,在 C++ 中,您可以使用模板来推断数组的大小。Microsoft 将此用于 C++ 版本的 Secure CRT 函数(如strcpy_s ),您可以使用类似的技巧来可靠地获取数组中的元素数。
回答by einpoklum
tl;dr: When you use an array you've define, you'll actually be using a pointer to its first element.
tl;dr:当您使用已定义的数组时,您实际上将使用指向其第一个元素的指针。
Thus:
因此:
- When you write
arr[idx]
you're really just saying*(arr + idx)
. - functions never really take arrays as parameters, only pointers, even when you specify an array parameter.
- 当你写作时,
arr[idx]
你真的只是在说*(arr + idx)
。 - 函数从不真正将数组作为参数,只有指针,即使您指定了数组参数。
Sort-of exceptions to this rule:
此规则的例外情况:
- You can pass fixed-length arrays to functions within a
struct
. sizeof()
gives the size taken up by the array, not the size of a pointer.
- 您可以将固定长度的数组传递给
struct
. sizeof()
给出数组占用的大小,而不是指针的大小。
回答by Chef Gladiator
I might be so bold to think there are four (4) ways to pass an array as the function argument. Also here is the short but working code for your perusal.
我可能会大胆地认为有四 (4) 种方法可以将数组作为函数参数传递。这里还有简短但有效的代码供您阅读。
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
using namespace std;
// test data
// notice native array init with no copy aka "="
// not possible in C
const char* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };
// ONE
// simple, dangerous and useless
template<typename T>
void as_pointer(const T* array) {
// a pointer
assert(array != nullptr);
} ;
// TWO
// for above const T array[] means the same
// but and also , minimum array size indication might be given too
// this also does not stop the array decay into T *
// thus size information is lost
template<typename T>
void by_value_no_size(const T array[0xFF]) {
// decayed to a pointer
assert( array != nullptr );
}
// THREE
// size information is preserved
// but pointer is asked for
template<typename T, size_t N>
void pointer_to_array(const T (*array)[N])
{
// dealing with native pointer
assert( array != nullptr );
}
// FOUR
// no C equivalent
// array by reference
// size is preserved
template<typename T, size_t N>
void reference_to_array(const T (&array)[N])
{
// array is not a pointer here
// it is (almost) a container
// most of the std:: lib algorithms
// do work on array reference, for example
// range for requires std::begin() and std::end()
// on the type passed as range to iterate over
for (auto && elem : array )
{
cout << endl << elem ;
}
}
int main()
{
// ONE
as_pointer(specimen);
// TWO
by_value_no_size(specimen);
// THREE
pointer_to_array(&specimen);
// FOUR
reference_to_array( specimen ) ;
}
I might also think this shows the superiority of C++ vs C. At least in reference (pun intended) of passing an array by reference.
我也可能认为这显示了 C++ 与 C 的优越性。至少在通过引用传递数组的引用(双关语)中。
Of course there are extremely strict projects with no heap allocation, no exceptions and no std:: lib. C++ native array handling is mission critical language feature, one might say.
当然,有非常严格的项目,没有堆分配,没有例外,也没有 std::lib。有人可能会说,C++ 原生数组处理是关键任务语言功能。