函数参数中的数组名称与本地声明的数组是否有不同的对待(自动)

时间:2019-05-25 14:06:38

标签: c arrays pointers parameter-passing function-call

请阅读下面程序中的注释:

#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

4 个答案:

答案 0 :(得分:0)

您应该记住,数组的名称“衰减”到指向第一个元素的指针。这意味着test(ch);等效于test(&ch[0]);

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

答案 1 :(得分:0)

(在其他情况下)将数组作为函数参数传递时,它会衰减为指向第一个元素的指针,并且接收该数组的函数参数在函数作用域内是本地的。

引用C11,第6.3.2.1章

  

除非它是sizeof运算符,_Alignof运算符或   一元&运算符,或者是用于初始化数组的字符串文字,该表达式具有   类型“类型数组” 转换为类型为“类型指针” 的表达式   数组对象的初始元素,不是左值。 [...]

因此,在您的情况下,在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;
}

答案 2 :(得分: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)。在主sizeof( ch )中是数组的大小。

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

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

等效。

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

答案 3 :(得分:-1)

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

  

将参数声明为“ 类型的数组”声明调整为“指向 type 的合格指针”,…

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

main中,ch是一个数组,因为它是用char ch[5]…声明的,它是未经调整的常规声明。在test中,c是一个指针。

maintest调用test(ch)时,参数ch是一个表达式。在表达式中,在大多数情况下,数组会自动转换为指针,因为C 2018 6.3.2 3说:

  

除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则表达式的类型为“ 的数组”类型”转换为类型为“指向 type 的指针”的表达式,该表达式指向数组对象的初始元素……

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

请注意,仅调整了外部数组。如果函数参数声明为int x[3][4],则将其调整为int (*x)[4],即指向4 int数组的指针。仅调整作为参数的数组(上面4个int的3个数组的数组);其组成内的其他类型均未调整。

在旁边

对于调整效果,C标准并不十分清楚。结合使用Apple LLVM 10.0.1和clang-1001.0.46.4,以下程序将显示“ Hello,world”。

#include <stdio.h>

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

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

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

相关问题