什么是阵列衰减?

Vam*_*msi 358 c c++ arrays pointers

什么是阵列的衰变?与数组指针有关系吗?

pho*_*bus 259

据说数组"衰变"成指针.声明为int numbers [5]无法重新指向的C++数组,即你不能说numbers = 0x5a5aff23.更重要的是,衰变一词意味着类型和维度的损失; 通过丢失维度信息(计数5)而numbers衰减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])
Run Code Online (Sandbox Code Playgroud)

最后两个将提供正确的sizeof信息,而第一个将不会,因为数组参数已经衰减分配给参数.

1在编译时应该知道常量U.

  • by_value传递一个指向数组第一个元素的指针; 在函数参数的上下文中,`T a []`与`T*a`相同.by_pointer传递相同的东西,除了指针值现在限定为`const`.如果要将指针*传递给数组*(而不是指向数组第一个元素的指针),则语法为`T(*array)[U]`. (9认同)
  • 第一次通过价值怎么样? (8认同)
  • @FredOverflow:所以如果`U`改变你就不必记得在两个地方改变它,或者冒着安静的错误......自治! (5认同)
  • "使用指向该数组的显式指针" - 这是不正确的.如果`a`是`char`的数组,则`a`的类型为`char [N]`,并将衰减为`char*`; 但是`&a`的类型是`char(*)[N]`,并且_not_会衰减. (4认同)
  • "如果你按值传递一个数组,你真正要做的就是复制指针"这没有任何意义,因为数组不能通过值传递. (4认同)

sys*_*USE 96

数组与C/C++中的指针基本相同,但并不完全相同.转换数组后:

const int a[] = { 2, 3, 5, 7, 11 };
Run Code Online (Sandbox Code Playgroud)

到一个指针(没有强制转换,因此在某些情况下会意外发生):

const int* p = a;
Run Code Online (Sandbox Code Playgroud)

你失去了sizeof操作员计算数组中元素的能力:

assert( sizeof(p) != sizeof(a) );  // sizes are not equal
Run Code Online (Sandbox Code Playgroud)

这种丧失的能力被称为"衰变".

有关更多详细信息,请查看有关数组衰减的文章.

  • 数组*不是*与指针基本相同; 他们是完全不同的动物.在大多数情况下,可以对数组进行处理*就好像它是一个指针一样,并且可以对指针进行处理*就像它是一个数组一样,但它就像它们得到的那样接近. (47认同)
  • @John,请原谅我不精确的语言.我试图在没有陷入冗长的背景故事的情况下得到答案,而"基本上......但不完全"就像我在大学时所做的一样好.我相信任何有兴趣的人都可以从你的评论中获得更准确的图片. (17认同)
  • 在谈论类型转换时,“无需强制转换即可工作”与“隐式发生”的含义相同 (2认同)
  • 数组变量几乎像指针一样工作这一事实并不一定意味着它们是相同的东西。他们有不同的类型。这就是为什么运算符“sizeof”适用于数组而不是指向数组的指针,尽管它们都有相同的地址。 (2认同)

Mic*_*urr 43

这是标准所说的(C99 6.3.2.1/3 - 其他操作数 - 左值,数组和函数指示符):

除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为''array of type''的表达式转换为类型为''指针的表达式type''指向数组对象的初始元素,而不是左值.

这意味着几乎在表达式中使用数组名称的任何时候,它都会自动转换为指向数组中第1项的指针.

请注意,函数名称以类似的方式起作用,但函数指针的使用要少得多,而且使用方式更加专业,因为它不会引起与将数组名称自动转换为指针一样多的混淆.

C++标准(4.2数组到指针转换)将转换要求放宽到(强调我的):

左值或类型"数组NT的"或"结合的T未知的数组"的右值可以被转换成类型的右值"指针T."

所以转换不会发生像它几乎总是用C做(这让对数组类型函数重载或模板匹配).

这也是为什么在C中你应该避免在函数原型/定义中使用数组参数(在我看来 - 我不确定是否有任何普遍的协议).它们引起混淆并且无论如何都是虚构的 - 使用指针参数并且混淆可能不会完全消失,但至少参数声明不是说谎.

  • @Garrett`char x [] ="你好";`.6个元素"Hello"的数组不会衰减; 相反,`x`得到大小`6`并且它的元素是从"Hello"的元素初始化的. (3认同)
  • 什么是“类型为‘类型数组’的表达式”是“用于初始化数组的字符串文字”的示例代码行? (2认同)

Joh*_*ode 26

"Decay"是指表达式从数组类型到指针类型的隐式转换.在大多数情况下,当编译器看到数组表达式时,它将表达式的类型从"N元素数组T"转换为"指向T",并将表达式的值设置为数组第一个元素的地址.此规则的例外情况是,数组是sizeof&运算符的操作数,或者数组是在声明中用作初始值设定项的字符串文字.

假设以下代码:

char a[80];
strcpy(a, "This is a test");
Run Code Online (Sandbox Code Playgroud)

表达式a的类型为"80元素的char数组",表达式"This is a test"的类型为"16元素的char数组"(在C中;在C++中,字符串文字是const char的数组).但是,在调用中strcpy(),表达式都不是sizeof或的操作数&,因此它们的类型被隐式转换为"指向char的指针",并且它们的值被设置为每个中的第一个元素的地址.什么strcpy()接收不是数组,但是指针,作为其原型看出:

char *strcpy(char *dest, const char *src);
Run Code Online (Sandbox Code Playgroud)

这与数组指针不同.例如:

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Run Code Online (Sandbox Code Playgroud)

双方ptr_to_first_elementptr_to_array具有相同的价值 ; a的基地址.但是,它们是不同的类型,并且处理方式不同,如下所示:

a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
Run Code Online (Sandbox Code Playgroud)

请记住,表达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
Run Code Online (Sandbox Code Playgroud)

同样,当数组是操作数时sizeof,它不会转换为指针类型.

sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
                                                  is on your platform
Run Code Online (Sandbox Code Playgroud)

ptr_to_first_element 是一个指向char的简单指针.

  • “这是一个测试”是不是“16 个字符的字符数组”类型是“字符的 15 个元素数组”?(长度 14 + 1 表示 \0) (2认同)

pmg*_*pmg 13

C中的数组没有任何价值.

在期望对象的值但对象是数组的任何地方,使用其第一个元素的地址来代替类型pointer to (type of array elements).

在函数中,所有参数都按值传递(数组也不例外).当你在一个函数中传递一个数组时,它"衰变成一个指针"(原文如此); 当你将一个数组与其他数据进行比较时,它再次"衰变成一个指针"(原文如此); ...

void foo(int arr[]);
Run Code Online (Sandbox Code Playgroud)

函数foo期望数组的值.但是,在C中,数组没有价值!因此,foo获取数组的第一个元素的地址.

int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
Run Code Online (Sandbox Code Playgroud)

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

在数组索引语法中,您习惯于再次看到arr''衰变为指针'

arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
Run Code Online (Sandbox Code Playgroud)

数组不会衰减为指针的唯一情况是它是sizeof运算符的操作数,或者是&运算符('运算符的地址),或者是用于初始化字符数组的字符串文字.

  • "数组没有价值" - 这是什么意思?当然数组有价值......它们是对象,你可以有指针,在C++中,它们是对它们的引用,等等. (5认同)
  • 我认为,严格地说,"值"在C中定义为根据类型对对象位的解释.我很难用数组类型找出它的有用含义.相反,你可以说你转换为指针,但这不是解释数组的内容,它只是获取它的位置.你得到的是指针的值(它是一个地址),而不是数组的值(这将是"包含项的值的序列",如"字符串"的定义中所使用的).也就是说,我认为说"数组的值"是公平的,当一个意味着指针得到. (2认同)

Mic*_*ker 8

当阵列腐烂并被指向时;-)

实际上,只是如果你想在某个地方传递一个数组,而是传递指针(因为谁将为你传递整个数组),人们会说可怜的数组衰减到指针.


blu*_*ote 6

试试这个代码


void f(double a[10]) {
    printf("in function: %d", sizeof(a));
    printf("pointer size: %d\n", sizeof(double *));
}

int main() {
    double a[10];
    printf("in main: %d", sizeof(a));
    f(a);
}
Run Code Online (Sandbox Code Playgroud)

你会看到函数内部数组的大小不等于main中数组的大小,但它等于指针的大小。

您可能听说过“数组是指针”,但是,这并不完全正确(sizeof内部main打印正确的大小)。然而,当传递时,数组衰减为指针。也就是说,无论语法显示什么,您实际上传递了一个指针,并且该函数实际上接收了一个指针。

在这种情况下,void f(double a[10]编译器将定义隐式转换为void f(double *a). 您可以等效地将函数参数直接声明为*a. 您甚至可以编写a[100]or a[1],而不是a[10],因为它实际上从未以这种方式编译(但是,您不应该这样做,显然,它会让读者感到困惑)。


Jos*_*ley 5

数组衰减意味着,当数组作为参数传递给函数时,它的处理方式与指针相同(“衰减到”)。

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);
}
Run Code Online (Sandbox Code Playgroud)

上述情况有两个复杂或例外情况。

首先,在 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;
}
Run Code Online (Sandbox Code Playgroud)

其次,在 C++ 中,您可以使用模板来推断数组的大小。Microsoft 将其用于 C++ 版本的 Secure CRT 函数(例如strcpy_s),并且您可以使用类似的技巧来可靠地获取数组中的元素数量