函数参数中的数组名称的处理方式是否与本地声明的数组不同(自动)

Are array names in function arguments treated differently than arrays declared locally (auto)

请阅读下面程序中的评论:

#include<stdio.h>
void test(char c[])
{
    c=c+2; //why does this work ?
    c--;
    printf("%c",*c);
}
int main()
{
    char ch[5]={'p','o','u','r'};
    //ch = ch+2;  //this is definitely not allowed on array names as they are not pointers
    test(ch);

    return 0;
}

OUTPUT
o

您应该记住,数组的名称 "decays" 指向指向其第一个元素的指针。这意味着 test(ch); 等同于 test(&ch[0]);

此外,void test(char c[]) 只不过是 void test(char* c),一个指向字符的指针。指针 可以 递增或递减,这就是为什么 c = c + 2c-- 编译得很好。

当数组作为函数参数传递时(在其他情况下),它会衰减为指向第一个元素的指针,并且接收它的函数参数在函数范围内是局部的。

引用 C11,章节 §6.3.2.1

Except when it is the operand of the sizeof operator, the _Alignof 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. [...]

所以,在你的例子中,在 void test(char c[]) 函数调用中,c 只是另一个指针,它指向数组的第一个元素。可以对该指针执行普通指针运算。

换句话说,

 void test(char c[]) { //....

相同
 void test(char *c) { //....

所以,你的情况类似于

int main(void)  //correcting the definition
{
    char ch[5]={'p','o','u','r'};
    //ch = ch+2;  //this is definitely not allowed on array names as they are not pointers

    char *c = &ch[0];   // this is what happens when you pass the array as function argument.
    c = c + 2;   // see, this is possible.

    test(ch);

    return 0;
}

数组指示符是不可变的左值。也就是说,您不能以 ir 定义另一个数组的方式更改数组指示符。

将数组指示符视为命名内存范围。

至于你的例子那么这个函数声明

void test(char c[]);

编译器调整如下

void test(char *c);

这是一个数组类型的参数被编译器调整为指针。因此,例如这些函数声明

void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);

是等价的并且声明了这个函数

void test(char *c);

您可以在程序中包含所有这些声明,尽管它们是多余的。

例如

#include <stdio.h>

void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);
void test( char *c)
{
    c=c+2; 
    c--;
    printf("%c",*c);
}

int main( void )
{
    char ch[5]={'p','o','u','r'};
    test(ch);
}

为了使其明显考虑以下程序

#include <stdio.h>

void test( char c[] )
{
    printf( "sizeof( c ) = %zu\n", sizeof( c ) );
}

int main( void )
{
    char ch[5]={'p','o','u','r'};
    test( ch );
    printf( "sizeof( ch ) = %zu\n", sizeof( ch ) );
}

它的输出是

sizeof( c ) = 8
sizeof( ch ) = 5

在函数内sizeof( c )等于一个指针的大小(在使用的系统中它等于8)。而在 main 中 sizeof( ch ) 是数组的大小。

当您将数组传递给此类函数时,数组指示符将隐式转换为指向其第一个元素的指针。所以这些电话

test( ch );
test( &ch[0] );

是等价的。

这意味着您在函数中处理指针,并且可以使用指针算法更改指针的值。

在函数参数声明中,数组声明自动调整为指针声明,根据 C 2018 6.7.6.3 7:

A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”,…

因此 void test(char c[]) 实际上是 void test(char *c).

main中,ch是一个数组,因为它是用char ch[5]…声明的,这是一个没有调整的普通声明。在test中,c是一个指针。

maintest(ch)调用test时,参数ch是一个表达式。在表达式中,大多数情况下数组会自动转换为指针,因为 C 2018 6.3.2 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…

因此,当将数组传递给参数声明为数组的函数时,数组将转换为指针并传递给调整为指针的参数。

注意只调整了一个外层数组。如果函数参数声明为int x[3][4],则调整为int (*x)[4],指向4int数组的指针。只调整作为参数的数组(上面的4int的3个数组的数组);其组合中的其他类型未调整。

放在一边

C标准对于调整的效果并不完全清楚。使用带有 clang-1001.0.46.4 的 Apple LLVM 10.0.1,以下程序打印“Hello, world.”:

#include <stdio.h>

static void foo(int a[printf("Hello, world.\n")]) {}

int main(void) { foo(0); }

这表明数组声明没有完全调整为指针声明,因为指定数组大小的表达式被保留但不会出现在指针声明中。