&
表示一个变量的地址,而在指针变量前加上*
可以得到指针所指对象的值。但是当你处理数组、字符串或者用指针拷贝来调用函数时,情况会有所不同。这其中的逻辑模式不易看出。何时应该使用
&
和*
?&
表示一个变量的地址,而在指针变量前加上*
可以得到指针所指对象的值。但是当你处理数组、字符串或者用指针拷贝来调用函数时,情况会有所不同。这其中的逻辑模式不易看出。&
和*
?int* p; // variable p is pointer to integer type
int i; // integer value
*
将指针转换为值。int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to
&
将一个值转换为指针。int* p2 = &i; // pointer p2 will point to the integer i
*
来访问其中的值,就像上面解释的那样,但还有另一种更常见的方法,即使用 []
操作符:int a[2]; // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element
int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element
[]
索引运算符是*
运算符的一种特殊形式,它的工作方式如下:a[i] == *(a + i); // these two statements are the same thing
int *bX = &aX;
不起作用是因为 aX
已经返回了 aX[0]
的地址(即 &aX[0]
),所以 &aX
将得到一个地址的地址,这没有意义。这样理解正确吗? - Pieterint aX[] = {3,4};
,int **bX = &aX;
是错误的。 &aX
的类型是“指向数组[2]的int的指针”,而不是“指向int的指针的指针”。具体来说,对于一元运算符&
,数组的名称不被视为指向其第一个元素的指针。您可以这样做:int (*bX)[2] = &aX;
- Alok Singhal*
找到指针引用的值。指针是一个包含值的变量,它所包含的值是某个内存位置的地址。没有“转换”,指针也不会“转换为”它所引用的值。同样地,&
也不会“将值转换为指针”。&v
求出 v
的地址。没有转换。 - William Pursell&
或 sizeof
运算符的操作数时,或者它是在声明中用作初始化器的字符串字面量时。int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
scanf()
中,不要使用&
运算符来对应"%s"参数的原因:
char str[STRING_LENGTH];
...
scanf("%s", str);
scanf()
接收一个指向 str
数组开头的 char *
值。对于任何使用数组表达式作为参数调用的函数(几乎所有的 str*
函数、*scanf
和 *printf
函数等),都是如此。&
运算符调用带有数组表达式的函数,例如:int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
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);
交换前:a = 1,b = 2 交换后:a = 2,b = 1形式参数
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);
交换之前: a = 1, b = 2 交换之后: a = 2, b = 1请注意,在交换函数中,我们没有改变
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 = ...;
}
无法工作。
简单来说
&
表示取地址符,在函数占位符中可以看到这一点,用于修改参数变量。在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
。在C/C++中,*
被用于多种不同的目的,因此这可能会很复杂。
如果*
出现在已经声明的变量/函数前面,则意味着:
*
运算符,则*
可以访问该变量的值。*
表示乘法运算符,则必须在*
左侧有另一个变量。如果*
出现在变量或函数声明中,则表示该变量是指针:
int int_value = 1;
int * int_ptr; //can point to another int variable
int int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer as well which points to the first int of the array
//int int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5}; // these two
int int_array4[5] = {1,2,3,4,5}; // are identical
void func_takes_int_ptr1(int *int_ptr){} // these two are identical
void func_takes_int_ptr2(int int_ptr[]){}// and legal
如果在变量或函数声明中出现 &
,通常表示该变量是对该类型变量的引用。
如果在已经声明的变量前面出现 &
,它会返回该变量的地址。
此外,当将数组传递给函数时,除非数组类似于以0结尾的c字符串(char数组),否则您总是需要传递该数组的大小。
x
,值为7
的单元格,间接询问值为7
的地址的方法是&7
,而间接询问地址为x
的值的方法是*x
。因此,(cell: x, value: 7) == (cell: &7, value: *x)
。另一种理解方式是:John
坐在第七个座位
上。*第七个座位
将指向John
,而&John
将给出第七个座位
的地址
/位置。这个简单的解释对我很有帮助,希望也能对其他人有所帮助。这是优秀视频的链接:点击这里。
这里是另一个例子:#include <stdio.h>
int main()
{
int x; /* A normal integer*/
int *p; /* A pointer to an integer ("*p" is an integer, so p
must be a pointer to an integer) */
p = &x; /* Read it, "assign the address of x to p" */
scanf( "%d", &x ); /* Put a value in x, we could also use p here */
printf( "%d\n", *p ); /* Note the use of the * to get the value */
getchar();
}
附加说明: 在使用指针之前,始终要对其进行初始化。如果不这样做,指针将指向任何内容,可能会导致程序崩溃,因为操作系统会阻止您访问它知道您不拥有的内存。但是,通过简单地将 p = &x;
,我们可以为指针分配一个特定的位置。
实际上,你已经掌握了它,没有更多需要知道的了 :-)
我只想补充以下几点:
&
取一个变量并给出地址,*
取一个地址并给出变量(或内容)。char **p
表示p
是指向指向char
的指针。至于事情是否有所不同,实际上并不是:
\0
)字符结尾的字符序列。&
和*
是什么)。还有,别忘了读肯尼思·里克(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;
}
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);
数组通常被视为指针。当您在函数中声明数组参数时,您可以轻松地将其声明为指针(这意味着相同的事情)。当您将数组传递给函数时,实际上是传递了指向第一个元素的指针。
函数指针是唯一不完全遵循规则的东西。您可以获取函数的地址而不使用&,并且可以调用函数指针而不使用*。
好的,看起来你的帖子被编辑了...
double foo[4];
double *bar_1 = &foo[0];
看看如何使用&
来获取数组结构的开头地址?以下是
Foo_1(double *bar, int size){ return bar[size-1]; }
Foo_2(double bar[], int size){ return bar[size-1]; }
将会做同样的事情。
理解指针起初是很复杂的,你需要做练习并且多加实践。 不要期望在第一次迭代中就能掌握它,也不要期望通过阅读解释就能理解, 因为很可能你并没有真正理解...
如果你想获得更多不仅仅是理论上的理解,我建议你参加斯坦福大学CS107课程并完成给定的练习, 至少要学习前三节课程,其中讲解了指针。
另一个非常有价值的工具是gdb
,它提供了一个交互式的shell,就像你在Python中使用的那样。
通过gdb
,你可以进行游戏和实验:
(gdb) x pp.name
0x555555555060 <_start>: 0x8949ed31
(gdb) x &pp.name
0x7fffffffdc38: 0x55555060
(gdb) p &pp.name
$4 = (char **) 0x7fffffffdc38
(gdb) p *pp.name
$5 = 49 '1'