C中的指针:何时使用&符号和星号?

时间:2010-01-19 15:41:06

标签: c pointers

我刚刚开始使用指针,我有点困惑。我知道&表示变量的地址,*可以在指针变量前面使用,以获取指针指向的对象的值。但是当您使用数组,字符串或使用变量的指针副本调用函数时,情况会有所不同。在所有这些内部很难看到逻辑模式。

我应该何时使用&*

10 个答案:

答案 0 :(得分:552)

你有指针和值:

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 address of 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

答案 1 :(得分:24)

处理数组和函数时有一种模式;一开始有点难以看清。

当处理数组时,记住以下内容是有用的:当数组表达式出现在大多数上下文中时,表达式的类型被隐式地从“N元素数组T”转换为“指向T”,并且value设置为指向数组中的第一个元素。此规则的例外情况是,数组表达式显示为&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]) {...}

这样的代码并不常见;您必须知道函数声明中数组的大小,并且该函数仅适用于指向特定大小的数组的指针(指向T的10元素数组的指针与指向11元素数组的指针的类型不同T)。

当数组表达式作为&运算符的操作数出现时,结果表达式的类型是“指向T的N元素数组的指针”或T (*)[N],它不同于指针数组(T *[N])和指向基类型(T *)的指针。

在处理函数和指针时,要记住的规则是:如果要更改参数的值并将其反映在调用代码中,则必须将指针传递给要修改的事物。再一次,数组会投入一些动作,但我们会首先处理正常情况。

请记住,C按值传递所有函数参数;形式参数接收实际参数中值的副本,并且形式参数的任何更改都不会反映在实际参数中。常见的例子是交换功能:

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

形式参数xy是来自ab的不同对象,因此不会反映对xy的更改在ab中。由于我们要修改ab的值,我们必须将指针传递给它们到交换函数:

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

请注意,在交换函数中,我们不会更改xy的值,而是更改xy 的值指向。写入*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 = ...;
}

不起作用。

答案 2 :(得分:11)

简单地说

  • &表示地址,您会看到在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
   /* ... */
}

以上示例说明了如何使用pass-by-reference调用函数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

答案 3 :(得分:9)

是的,这可能非常复杂,因为*在C / C ++中用于许多不同的目的。

如果*出现在已声明的变量/函数前面,则表示:

  • a)*可以访问该变量的值(如果该变量的类型是指针类型,或者*运算符超载)。
  • b)*具有乘法运算符的含义,在这种情况下,*
  • 左侧必须有另一个变量

如果*出现在变量或函数声明中,则表示该变量是指针:

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 aswell 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 indentical

void func_takes_int_ptr1(int *int_ptr){} // these two are indentical
void func_takes int_ptr2(int int_ptr[]){}// and legal

如果变量或函数声明中出现&,则通常表示该变量是对该类型变量的引用。

如果&出现在已声明的变量前面,则返回该变量的地址

此外你应该知道,当一个数组传递给一个函数时,你总是必须传递该数组的数组大小,除非数组类似于0终止的cstring(char数组)。

答案 4 :(得分:4)

当您声明指针变量或函数参数时,请使用*:

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

注意:每个声明的变量都需要自己的*。

如果要获取值的地址,请使用&amp ;.如果要在指针中读取或写入值,请使用*。

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

数组通常只是像指针一样对待。当你在函数中声明一个数组参数时,你可以很容易地声明它是一个指针(它意味着同样的事情)。将数组传递给函数时,实际上是将指针传递给第一个元素。

函数指针是唯一不完全遵循规则的东西。您可以在不使用&amp;的情况下获取函数的地址,并且可以在不使用*的情况下调用函数指针。

答案 5 :(得分:3)

实际上,你已经知道了,你不需要知道: - )

我只想添加以下内容:

  • 这两个操作是光谱的两端。 &接受变量并为您提供地址,*获取地址并为您提供变量(或内容)。
  • 将文件传递给函数时,
  • 数组“降级”为指针。
  • 你实际上可以在间接上有多个级别(char **p意味着p是指向char指针的指针。

对于不同的工作方式,不是真的:

    如上所述,
  • 数组在传递给函数时会降级为指针(到数组中的第一个元素);它们不保留尺寸信息。
  • C中没有字符串,只是字符数组,按照惯例,表示以零(\0)字符结尾的字符串。
  • 当您将变量的地址传递给函数时,您可以取消引用指针以更改变量本身(通常变量按值传递(数组除外))。

答案 6 :(得分:3)

我觉得你有点困惑。你应该阅读一本关于指针的好教程/书。

This教程非常适合初学者(清楚地解释了&*是什么)。是的,不要忘记阅读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;
}

答案 7 :(得分:2)

我正在查看所有罗嗦的解释,所以转而使用新南威尔士大学的视频进行救援。这是一个简单的解释:如果我们有一个地址为x且值为{{1 }},询问价值地址7的间接方式是7,在地址&7询问价值的间接方式是x。所以{{1}查看它的另一种方法:*x位于(cell: x , value: 7) == (cell: &7 , value: *x)John将指向7th seat*7th seat将指向John / &John的位置。这个简单的解释对我有所帮助,并希望它也能帮助其他人。以下是优秀视频的链接:click here.

这是另一个例子:

address

Add-on:在使用之前始终初始化指针。如果没有,指针将指向任何内容,这可能会导致程序崩溃,因为操作系统会阻止您访问内存知道你不拥有。但是简单地放7th seat,我们正在为指针分配一个特定的位置。

答案 8 :(得分:1)

好的,看起来你的帖子已被编辑......

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]; }

会做同样的事情。

答案 9 :(得分:0)

一开始理解指针很复杂,你必须做练习和 多练习。 不要期望在第一次迭代时就抓住它或期望阅读解释 并认为你已经理解了,因为很可能你没有......

如果你想要的不仅仅是理论理解,我建议你遵循这个 斯坦福 CS107 课程和练习给出的练习, 至少遵循前三节课,其中解释了指针。

Stanford CS107 by Jerry Cain

另一个非常有价值的工具是 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'