首先,请记住 C 对数组的处理与 Java 非常不同。像这样的声明
char foo[10];
char
为 10 个值分配足够的存储空间,仅此而已(模任何额外空间以满足对齐要求);没有为指向第一个元素的指针或任何其他类型的元数据(例如数组大小或元素类类型)预留额外的存储空间。foo
除了数组元素本身1之外,没有其他对象。相反,语言中有一条规则,只要编译器看到一个数组表达式不是sizeof
或一元运算符的操作数&
(或用于在声明中初始化另一个数组的字符串文字),它就会隐式地将该表达式从类型转换为 " T
“到”指针的 N 元素数组T
",表达式的值是数组第一个元素的地址。
这有几个含义。首先是当您将数组表达式作为参数传递给函数时,函数实际接收的是指针值:
char foo[10];
do_something_with( foo );
...
void do_something_with( char *p )
{
...
}
与实参p
对应的形参foo
是指向 的指针char
,而不是 的数组char
。为了让事情变得混乱,C允许do_something_with
被声明为
void do_something_with( char p[] )
甚至
void do_something_with( char p[10] )
但是在函数参数声明的情况下,T p[]
andT p[N]
与 相同T *p
,并且所有三个都声明p
为指针,而不是数组2。请注意,这仅适用于函数参数声明。
第二个含义是下标运算符[]
可以用于指针操作数以及数组操作数,例如
char foo[10];
char *p = foo;
...
p[i] = 'A'; // equivalent to foo[i] = 'A';
最后的含义导致处理指向指针的指针的一种情况-假设您有一个指针数组,例如
const char *strs[] = { "foo", "bar", "bletch", "blurga", NULL };
strs
是一个 3 的 5 元素const char *
数组;但是,如果您将其传递给类似的函数
do_something_with( strs );
那么函数接收的实际上是指向指针的指针,而不是指针数组:
void do_something_with( const char **strs ) { ... }
指向指针的指针(以及更高级别的间接)也出现在以下情况:
- 写入指针类型的参数:记住 C 是按值传递所有参数的;函数定义中的形参与函数调用中的实参在内存中是不同的对象,所以如果想让函数更新实参的值,必须传递一个指向该参数的指针:
void foo( T *param ) // for any type T
{
*param = new_value(); // update the object param *points to*
}
void bar( void )
{
T x;
foo( &x ); // update the value in x
}
现在假设我们用T
指针类型替换类型R *
,那么我们的代码片段如下所示:
void foo( R **param ) // for any type R *
{
...
*param = new_value(); // update the object param *points to*
...
}
void bar( void )
{
R *x;
foo( &x ); // update the value in x
}
相同的语义 - 我们正在更新x
. 只是在这种情况下,x
已经有了指针类型,所以我们必须将指针传递给指针。这可以扩展到更高层次的方向:
void foo( Q ****param ) // for any type Q ***
{
...
*param = new_value(); // update the object param *points to*
...
}
void bar( void )
{
Q ***x;
foo( &x ); // update the value in x
}
- 动态分配的多维数组:在 C 中分配多维数组的一种常用技术是分配一个指针数组,并为该数组的每个元素分配一个指针指向的缓冲区:
T **arr;
arr = malloc( rows * sizeof *arr ); // arr has type T **, *arr has type T *
if ( arr )
{
for ( size_t i = 0; i < rows; i++ )
{
arr[i] = malloc( cols * sizeof *arr[i] ); // arr[i] has type T *
if ( arr[i] )
{
for ( size_t j = 0; j < cols; j++ )
{
arr[i][j] = some_initial_value();
}
}
}
}
这可以扩展到更高级别的间接性,因此您有类似T ***
andT ****
等
类型。
1. 这就是为什么数组表达式可能不是赋值目标的部分原因;没有什么可以分配给.
这是源自 C 的 B 编程语言的保留;在 B 中,指针被声明为auto p[]
.
每个字符串字面量都是 的数组char
,但是因为我们没有使用它们来初始化 的各个数组char
,所以表达式被转换为指针值。