我将在您的代码上下文中讨论一些事情,但我想先了解一些基础知识。
在声明中,一元* 运算符表示被声明的事物具有指针类型:
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 或一元 & 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则 表达式 类型为 "N- T"的元素数组被转换("decays")为类型为"pointer to T"的表达式,表达式的值是数组第一个元素的地址3。所以,如果你创建一个像
这样的数组
int a[10];
并将该数组表达式作为参数传递给类似的函数
foo( a );
那么在函数调用之前,表达式a从“int的10元素数组”类型转换为“指向int的指针”,a的值就是@的地址987654356@。所以函数实际接收的是指针值,而不是数组:
void foo( int *a ) { ... }
"add" 和 "five to two" 等字符串字面量是数组表达式 - "add" 的类型为“char 的 4 元素数组”,"five to two" 的类型为“char 的 12 元素数组”(一个由于字符串终止符,N 个字符的字符串需要至少 N+1 个元素来存储)。
在声明中
mnemonic = "add";
operands = "five to two";
字符串文字都不是 sizeof 或一元 & 运算符的操作数,并且它们不用于在声明中初始化字符数组,因此两个表达式都转换为类型 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 );
...
}
如果您编译并运行此代码,您将看到 x 和 y 的值在调用 swap 后没有改变 - 对 a 和 b 的更改没有对x 和y 产生影响,因为它们是内存中的不同对象。
为了使swap 函数工作,我们必须将指针 传递给x 和y:
void swap( int *a, int *b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main( void )
{
...
swap( &x, &y );
...
}
在这种情况下,swap 中的 表达式 *a 和 *b 与 main 中的表达式 x 和 y 引用相同的对象,因此更改到*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 到foo,那么我们仍然必须传递一个指向var 的指针作为参数。由于var 的类型为P *,那么表达式 &var 的类型为P **。
- 如果指针值在该对象的生命周期内指向该对象,则它是有效的。
- 左值是引用对象的表达式,以便可以读取或修改对象的值。
- 信不信由你,这条规则是有充分理由的,但这意味着在大多数情况下,数组表达式失去了它们的“数组性”,导致第一次学习该语言的人很困惑。