什么是数组指针衰减?它和数组指针有关系吗?


当前回答

tl;dr:当您使用已定义的数组时,实际上是在使用指向其第一个元素的指针。

因此:

当你写arr[idx]时,你实际上是在说*(arr + idx)。 函数从来不会真正将数组作为参数,只接受指针——当指定数组参数时可以直接接受,如果将引用传递给数组则可以间接接受。

这条规则的例外情况:

可以将固定长度的数组传递给结构中的函数。 Sizeof()给出数组占用的大小,而不是指针的大小。

其他回答

当数组腐烂并被指向时;-)

实际上,如果你想传递一个数组到某个地方,但却传递了指针(因为谁会他妈的为你传递整个数组),人们会说这个可怜的数组衰减为指针。

据说数组会“衰减”成指针。声明为int numbers[5]的c++数组不能被重指向,即不能说numbers = 0x5a5aff23。更重要的是,衰减一词意味着类型和维度的丧失;数字通过丢失维数信息(计数5)衰减为int*,类型不再是int[5]。看看这里没有发生衰变的情况。

如果你是按值传递一个数组,你实际上是在复制一个指针——指向数组第一个元素的指针被复制到形参(形参的类型也应该是数组元素的类型的指针)。这是由于数组的衰减性质;一旦衰变,sizeof就不再给出整个数组的大小,因为它本质上变成了一个指针。这就是为什么首选通过引用或指针传递的原因(以及其他原因)。

传递数组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])

后两个将提供适当的sizeof信息,而第一个不会,因为数组参数已经衰减为赋值给形参。

常量U应该在编译时已知。

数组是由c语言中的指针自动传递的,其背后的原理只能推测。

int a[5], int *a and int (*a)[5] are all glorified addresses meaning that the compiler treats arithmetic and deference operators on them differently depending on the type, so when they refer to the same address they are not treated the same by the compiler. int a[5] is different to the other 2 in that the address is implicit and does not manifest on the stack or the executable as part of the array itself, it is only used by the compiler to resolve certain arithmetic operations, like taking its address or pointer arithmetic. int a[5] is therefore an array as well as an implicit address, but as soon as you talk about the address itself and place it on the stack, the address itself is no longer an array, and can only be a pointer to an array or a decayed array i.e. a pointer to the first member of the array.

例如,在int (*a)[5]上,对a的第一次解引用将产生int *(因此相同的地址,只是不同的类型,注意不是int a[5]),对a的指针算术,即a+1或*(a+1)将根据5个int数组的大小(这是它所指向的数据类型),第二次解引用将产生int。然而,对于int类型a[5],第一次解引用将产生int类型,指针的算术将与int类型的大小相关。

To a function, you can only pass int * and int (*)[5], and the function casts it to whatever the parameter type is, so within the function you have a choice whether to treat an address that is being passed as a decayed array or a pointer to an array (where the function has to specify the size of the array being passed). If you pass a to a function and a is defined int a[5], then as a resolves to an address, you are passing an address, and an address can only be a pointer type. In the function, the parameter it accesses is then an address on the stack or in a register, which can only be a pointer type and not an array type -- this is because it's an actual address on the stack and is therefore clearly not the array itself.

You lose the size of the array because the type of the parameter, being an address, is a pointer and not an array, which does not have an array size, as can be seen when using sizeof, which works on the type of the value being passed to it. The parameter type int a[5] instead of int *a is allowed but is treated as int * instead of disallowing it outright, though it should be disallowed, because it is misleading, because it makes you think that the size information can be used, but you can only do this by casting it to int (*a)[5], and of course, the function has to specify the size of the array because there is no way to pass the size of the array because the size of the array needs to be a compile-time constant.

tl;dr:当您使用已定义的数组时,实际上是在使用指向其第一个元素的指针。

因此:

当你写arr[idx]时,你实际上是在说*(arr + idx)。 函数从来不会真正将数组作为参数,只接受指针——当指定数组参数时可以直接接受,如果将引用传递给数组则可以间接接受。

这条规则的例外情况:

可以将固定长度的数组传递给结构中的函数。 Sizeof()给出数组占用的大小,而不是指针的大小。

我可能会大胆地认为有四(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 ) ;
}

我可能也认为这显示了c++相对于C的优势,至少在引用(双关语)通过引用传递数组。

当然,有些非常严格的项目没有堆分配,没有异常,也没有std:: lib。有人可能会说,c++原生数组处理是关键任务语言特性。