我刚开始讲指针,有点困惑。我知道&意味着一个变量的地址,*可以用在一个指针变量前面,以获得指针所指向的对象的值。但是当你使用数组、字符串或者使用变量的指针副本调用函数时,情况就不同了。很难看出其中的逻辑模式。
什么时候应该使用&和*?
我刚开始讲指针,有点困惑。我知道&意味着一个变量的地址,*可以用在一个指针变量前面,以获得指针所指向的对象的值。但是当你使用数组、字符串或者使用变量的指针副本调用函数时,情况就不同了。很难看出其中的逻辑模式。
什么时候应该使用&和*?
当前回答
在处理数组和函数时,有一个模式;只是刚开始有点看不出来。
在处理数组时,记住以下内容是有用的:当数组表达式出现在大多数上下文中时,表达式的类型会从“T的n元素数组”隐式转换为“指向T的指针”,并且它的值被设置为指向数组中的第一个元素。该规则的例外情况是当数组表达式作为&或sizeof操作符的操作数出现时,或者当它是一个字符串字面值在声明中用作初始化式时。
因此,当你调用一个以数组表达式作为参数的函数时,函数将接收一个指针,而不是数组:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
这就是为什么在scanf()中不使用&运算符来对应“%s”的参数:
char str[STRING_LENGTH];
...
scanf("%s", str);
由于隐式转换,scanf()接收一个char *值,该值指向str数组的开头。这适用于任何以数组表达式作为参数调用的函数(包括str*函数、*scanf和*printf函数等)。
在实践中,你可能永远不会使用&操作符调用带有数组表达式的函数,例如:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
这样的代码并不常见;你必须在函数声明中知道数组的大小,并且该函数只适用于指向特定大小的数组的指针(指向10个元素的T数组的指针与指向11个元素的T数组的指针是不同类型的)。
当数组表达式作为&操作符的操作数出现时,结果表达式的类型是“指向T的N元素数组的指针”,或T (*)[N],它不同于指针数组(T *[N])和指向基类型的指针(T *)。
在处理函数和指针时,要记住的规则是:如果你想改变一个参数的值,并让它反映在调用代码中,你必须传递一个指针到你想修改的东西。同样,数组会给工作带来一些麻烦,但我们将首先处理正常情况。
记住C语言通过值传递所有函数参数;形式形参接收实际形参值的副本,对形式形参的任何更改都不会反映在实际形参中。常见的例子是swap函数:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
您将得到以下输出:
before swap: a = 1, b = 2 after swap: a = 1, b = 2
形式形参x和y与a和b是不同的对象,因此对x和y的更改不会反映在a和b中。由于我们想修改a和b的值,必须将指向它们的指针传递给swap函数:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
现在您的输出将是
before swap: a = 1, b = 2 after swap: a = 2, b = 1
注意,在swap函数中,我们不改变x和y的值,而是改变x和y指向的值。写入*x不同于写入x;我们没有更新x本身的值,我们从x获取一个位置并更新该位置的值。
如果我们想修改一个指针值,这同样成立;如果我们写
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
然后我们修改输入参数stream的值,而不是stream指向什么,所以改变stream对in的值没有影响;为了让它工作,我们必须传递一个指针给指针:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
同样,数组会给工作带来一些麻烦。将数组表达式传递给函数时,函数接收到的是一个指针。由于数组下标的定义方式,你可以在指针上使用下标操作符,就像在数组上使用它一样:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
注意,数组对象不能被赋值;也就是说,你不能做
int a[10], b[10];
...
a = b;
所以当你处理指向数组的指针时要小心;类似的
void (int (*foo)[N])
{
...
*foo = ...;
}
不能工作。
其他回答
当你声明一个指针变量或函数形参时,使用*:
int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
...
}
注意:每个声明的变量都需要自己的*。
当你想获取一个值的地址时,使用&。当您想读写指针中的值时,请使用*。
int a;
int *b;
b = f(&a);
a = *b;
a = *f(&a);
数组通常就像指针一样。当你在函数中声明一个数组形参时,你可以很容易地声明它是一个指针(它的意思是一样的)。当你将一个数组传递给一个函数时,你实际上是在传递一个指向第一个元素的指针。
函数指针是唯一不完全遵循规则的东西。你可以不使用&而获取函数的地址,也可以不使用*而调用函数指针。
我觉得你有点糊涂了。你应该读一本关于指针的教程/书。
本教程非常适合初学者(清楚地解释了&和*是什么)。别忘了读Kenneth Reek写的《C语言指针》
&和*之间的区别非常明显。
例子:
#include <stdio.h>
int main(){
int x, *p;
p = &x; /* initialise pointer(take the address of x) */
*p = 0; /* set x to zero */
printf("x is %d\n", x);
printf("*p is %d\n", *p);
*p += 1; /* increment what p points to i.e x */
printf("x is %d\n", x);
(*p)++; /* increment what p points to i.e x */
printf("x is %d\n", x);
return 0;
}
在处理数组和函数时,有一个模式;只是刚开始有点看不出来。
在处理数组时,记住以下内容是有用的:当数组表达式出现在大多数上下文中时,表达式的类型会从“T的n元素数组”隐式转换为“指向T的指针”,并且它的值被设置为指向数组中的第一个元素。该规则的例外情况是当数组表达式作为&或sizeof操作符的操作数出现时,或者当它是一个字符串字面值在声明中用作初始化式时。
因此,当你调用一个以数组表达式作为参数的函数时,函数将接收一个指针,而不是数组:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
这就是为什么在scanf()中不使用&运算符来对应“%s”的参数:
char str[STRING_LENGTH];
...
scanf("%s", str);
由于隐式转换,scanf()接收一个char *值,该值指向str数组的开头。这适用于任何以数组表达式作为参数调用的函数(包括str*函数、*scanf和*printf函数等)。
在实践中,你可能永远不会使用&操作符调用带有数组表达式的函数,例如:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
这样的代码并不常见;你必须在函数声明中知道数组的大小,并且该函数只适用于指向特定大小的数组的指针(指向10个元素的T数组的指针与指向11个元素的T数组的指针是不同类型的)。
当数组表达式作为&操作符的操作数出现时,结果表达式的类型是“指向T的N元素数组的指针”,或T (*)[N],它不同于指针数组(T *[N])和指向基类型的指针(T *)。
在处理函数和指针时,要记住的规则是:如果你想改变一个参数的值,并让它反映在调用代码中,你必须传递一个指针到你想修改的东西。同样,数组会给工作带来一些麻烦,但我们将首先处理正常情况。
记住C语言通过值传递所有函数参数;形式形参接收实际形参值的副本,对形式形参的任何更改都不会反映在实际形参中。常见的例子是swap函数:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
您将得到以下输出:
before swap: a = 1, b = 2 after swap: a = 1, b = 2
形式形参x和y与a和b是不同的对象,因此对x和y的更改不会反映在a和b中。由于我们想修改a和b的值,必须将指向它们的指针传递给swap函数:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
现在您的输出将是
before swap: a = 1, b = 2 after swap: a = 2, b = 1
注意,在swap函数中,我们不改变x和y的值,而是改变x和y指向的值。写入*x不同于写入x;我们没有更新x本身的值,我们从x获取一个位置并更新该位置的值。
如果我们想修改一个指针值,这同样成立;如果我们写
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
然后我们修改输入参数stream的值,而不是stream指向什么,所以改变stream对in的值没有影响;为了让它工作,我们必须传递一个指针给指针:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
同样,数组会给工作带来一些麻烦。将数组表达式传递给函数时,函数接收到的是一个指针。由于数组下标的定义方式,你可以在指针上使用下标操作符,就像在数组上使用它一样:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
注意,数组对象不能被赋值;也就是说,你不能做
int a[10], b[10];
...
a = b;
所以当你处理指向数组的指针时要小心;类似的
void (int (*foo)[N])
{
...
*foo = ...;
}
不能工作。
事实上,你已经掌握了,没有什么你需要知道的了:-)
我想补充以下几点:
这两种操作是截然相反的。&接受一个变量并给你地址,*接受一个地址并给你变量(或内容)。 将数组传递给函数时,数组“降级”为指针。 实际上你可以在间接上有多个层次(char **p表示p是指向一个char的指针的指针。
至于事情的运作方式不同,其实不是这样的:
如前所述,数组在传递给函数时降级为指针(指向数组中的第一个元素);它们不保存大小信息。 C语言中没有字符串,只有字符数组,按照惯例,表示以0(\0)字符结束的字符串。 当你将一个变量的地址传递给一个函数时,你可以去引用指针来改变变量本身(通常变量是按值传递的(数组除外))。
简单地说
&表示地址,你会看到在C语言中修改参数变量的函数占位符中,参数变量是按值传递的,使用&表示按引用传递。 *表示指针变量的解引用,即获取该指针变量的值。
int foo(int *x){
*x++;
}
int main(int argc, char **argv){
int y = 5;
foo(&y); // Now y is incremented and in scope here
printf("value of y = %d\n", y); // output is 6
/* ... */
}
上面的例子说明了如何使用引用传递来调用函数foo,与此比较
int foo(int x){
x++;
}
int main(int argc, char **argv){
int y = 5;
foo(y); // Now y is still 5
printf("value of y = %d\n", y); // output is 5
/* ... */
}
下面是使用解引用的示例
int main(int argc, char **argv){
int y = 5;
int *p = NULL;
p = &y;
printf("value of *p = %d\n", *p); // output is 5
}
上面说明了我们如何获得y的地址并将其赋值给指针变量p。然后我们通过将*附加到p的前面来解引用p,以获得p的值,即*p。