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


当前回答

我可能会大胆地认为有四(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++原生数组处理是关键任务语言特性。

其他回答

"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.

假设有以下代码:

char a[80];
strcpy(a, "This is a test");

表达式a的类型是“80-element array of char”,表达式“This is a test”的类型是“15-element array of char”(在C语言中;在c++中,字符串字面值是const char数组)。然而,在对strcpy()的调用中,两个表达式都不是sizeof或&的操作数,因此它们的类型被隐式转换为“指向char的指针”,并且它们的值被设置为每个表达式中第一个元素的地址。strcpy()接收的不是数组,而是指针,正如它的原型所示:

char *strcpy(char *dest, const char *src);

这和数组指针不是一回事。例如:

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;

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]

请记住,表达式a[i]被解释为*(a+i)(只有在数组类型转换为指针类型时才有效),因此a[i]和ptr_to_first_element[i]的工作原理相同。表达式(*ptr_to_array)[i]被解释为*(*a+i)。表达式*ptr_to_array[i]和ptr_to_array[i]可能会根据上下文导致编译器警告或错误;如果你期望它们的值是a[i],它们肯定会出错。

sizeof a == sizeof *ptr_to_array == 80

同样,当数组是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是一个简单的char指针。

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++原生数组处理是关键任务语言特性。

在C语言中,数组没有值。

如果需要对象的值,但对象是数组,则使用其第一个元素的地址,type pointer to(数组元素的类型)。

在函数中,所有参数都是按值传递的(数组也不例外)。当你在函数中传递一个数组时,它“衰减为指针”(原文如此);当你将一个数组与其他东西进行比较时,它再次“衰减为指针”(原文如此);...

void foo(int arr[]);

函数foo期望数组的值。但是,在C语言中,数组没有值!foo得到的是数组第一个元素的地址。

int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }

在上面的比较中,arr没有值,所以它成为一个指针。它变成了一个指向int的指针。该指针可以与变量ip进行比较。

在数组索引语法中你经常看到,arr被衰减为指针

arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */

只有当数组是sizeof操作符的操作数,或&操作符('address of'操作符)的操作数,或用作初始化字符数组的字符串字面值时,数组才不会衰减为指针。