请阅读下面程序中的注释:
#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
答案 0 :(得分:0)
您应该记住,数组的名称“衰减”到指向第一个元素的指针。这意味着test(ch);
等效于test(&ch[0]);
。
此外,void test(char c[])
只是void test(char* c)
(指向字符的指针)。指针可以递增或递减,这就是c = c + 2
和c--
编译得很好的原因。
答案 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
是一个指针。
当main
用test
调用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); }
这表明数组声明没有完全调整为指针声明,因为指定数组大小的表达式已保留,但不会出现在指针声明中。