我将在您的代码上下文中讨论一些事情,但我想先了解一些基础知识。
在声明中,一元运算*
符表示被声明的事物具有指针类型:
T *p; // for any type T, p has type "pointer to T"
T *p[N]; // for any type T, p has type "N-element array of pointer to T"
T (*p)[N]; // for any type T, p has type "pointer to N-element array of T"
T *f(); // for any type T, f has type "function returning pointer to T"
T (*f)(); // for any type T, f has type "pointer to function returning T"
一元运算*
符的优先级低于后缀[]
下标和()
函数运算符,因此如果您想要指向数组或函数的指针,则*
必须与标识符显式分组。
在表达式中,一元运算*
符取消引用指针,允许我们访问指向的对象或函数:
int x;
int *p;
p = &x; // assign the address of x to p
*p = 10; // assigns 10 to x via p - int = int
上述代码执行后,以下情况属实:
p == &x // int * == int *
*p == x == 10 // int == int == int
表达式 p
和&x
具有类型int *
(指向 的指针)int
,它们的值是 的(虚拟)地址x
。表达式 *p
和x
具有类型,它们的int
值为10
。
一个有效的1对象指针值是通过以下三种方式之一获得的(函数指针也是一个东西,但我们不会在这里深入探讨):
&
在左值2 ( ) 上使用一元运算符p = &x;
;
malloc()
通过、calloc()
或分配动态内存realloc()
;
- 并且,与您的代码相关的内容是使用不带或运算符的数组表达式。
&
sizeof
除非它是sizeof
or 一元运算符的操作数&
,或者是用于在声明中初始化字符数组的字符串文字,否则“N 元素数组”类型的表达式T
将被转换(“衰减”)为类型的表达式“指针T
”,表达式的值是数组3的第一个元素的地址。所以,如果你创建一个像
int a[10];
并将该数组表达式作为参数传递给类似的函数
foo( a );
那么在函数被调用之前,表达式a
从“10-element array of int
”类型转换为“pointer to int
”,其值为a
的地址a[0]
。所以函数实际接收的是一个指针值,而不是一个数组:
void foo( int *a ) { ... }
字符串字面量如"add"
和"five to two"
是数组表达式 -"add"
类型为“4 元素数组char
”,"five to two"
类型为“12 元素数组char
”(由于字符串终止符,N 个字符的字符串需要至少 N+1 个元素来存储)。
在声明中
mnemonic = "add";
operands = "five to two";
字符串文字都不是sizeof
or 一元运算符的操作数&
,并且它们不用于在声明中初始化字符数组,因此两个表达式都转换为类型char *
,它们的值是每个数组的第一个元素的地址。两者mnemonic
和operands
都被声明为char *
,所以这很好。
由于 和 的类型mnemonic
都是operands
,所以char *
当您调用
analyse_inst( mnemonic, operands );
函数形式参数的类型也必须是char *
:
void analyse_inst( char *mnemonic, char *operands )
{
...
}
至于“通过引用传递”位......
C按值传递所有函数参数。这意味着函数定义中的形式参数与函数调用中的实际参数在内存中是不同的对象,并且对形式参数所做的任何更改都不会反映在实际参数中。假设我们写一个swap
函数为:
int swap( int a, int b )
{
int tmp = a;
a = b;
b = tmp;
}
int main( void )
{
int x = 2;
int y = 3;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( x, y );
printf( "after swap: x = %d, y = %d\n", x, y );
...
}
如果您编译并运行此代码,您将看到 and 的值x
在y
调用之后不会改变swap
- 对 and 的更改对a
andb
没有影响x
,y
因为它们是内存中的不同对象。
为了使swap
函数工作,我们必须传递指向x
and的指针y
:
void swap( int *a, int *b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main( void )
{
...
swap( &x, &y );
...
}
在这种情况下,表达式 and in与表达式*a
and *b
inswap
引用相同的对象,因此对x
and的更改反映在and中:y
main
*a
*b
x
y
a == &x, b == &y
*a == x, *b == y
所以,一般来说:
void foo( T *ptr ) // for any non-array type T
{
*ptr = new_value(); // write a new value to the object `ptr` points to
}
void bar( void )
{
T var;
foo( &var ); // write a new value to var
}
对于指针类型也是如此——用指针类型替换T
,P *
我们得到以下结果:
void foo( P **ptr ) // for any non-array type T
{
*ptr = new_value(); // write a new value to the object `ptr` points to
}
void bar( void )
{
P *var;
foo( &var ); // write a new value to var
}
在这种情况下,var
存储一个指针值。如果我们想向var
through写入一个新的指针值foo
,那么我们仍然必须传递一个指针var
作为参数。既然var
有类型P *
,那么表达式 &var
就有类型P **
。
- 如果指针值在该对象的生命周期内指向该对象,则它是有效的。
- 左值是引用对象的表达式,以便可以读取或修改对象的值。
- 信不信由你,这条规则是有充分理由的,但这意味着在大多数情况下,数组表达式失去了它们的“数组性”,导致第一次学习该语言的人很困惑。