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

Pie*_*ter 272 c pointers

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

什么时候应该使用&*

Dan*_*son 580

你有指针和价值观:

int* p; // variable p is pointer to integer type
int i; // integer value
Run Code Online (Sandbox Code Playgroud)

您将指针转换为值*:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to
Run Code Online (Sandbox Code Playgroud)

您将值转换为指针&:

int* p2 = &i; // pointer p2 will point to the address of integer i
Run Code Online (Sandbox Code Playgroud)

编辑:在数组的情况下,它们被视为非常类似于指针.如果您将它们视为指针,您将使用它*来获取它们内部的值,如上所述,但是使用[]运算符还有另一种更常见的方法:

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
Run Code Online (Sandbox Code Playgroud)

要获得第二个元素:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element
Run Code Online (Sandbox Code Playgroud)

所以[]索引操作符是运算符的一种特殊形式*,它的工作原理如下:

a[i] == *(a + i);  // these two statements are the same thing
Run Code Online (Sandbox Code Playgroud)

  • 数组是特殊的,可以透明地转换为指针.这突出了另一种从指针到值的方法,我将它添加到上面的解释中. (4认同)
  • 你是对的,虽然有些情况你可能真的想要地址的地址.在这种情况下,您将其声明为int**bX =&aX,但这是一个更高级的主题. (4认同)
  • 如果我理解正确...示例`int*bX =&aX;`不起作用,因为`aX`已经返回`aX [0]`的地址(即`&aX [0]`),所以` &aX`将获得一个没有意义的地址.它是否正确? (3认同)
  • @Dan,给定`int aX [] = {3,4};`,`int**bX =&aX;`是一个错误.`&aX`是"指向`int`的数组[2]的类型",而不是"指向`int`指针的指针".具体来说,数组的名称不被视为指向其一元"&"的第一个元素的指针.你可以这样做:`int(*bX)[2] =&aX;` (3认同)
  • 这怎么行不通?`int aX[] = {3, 4}; int *bX = &aX;` (2认同)
  • “你把一个指针变成一个值”是一个糟糕的描述。您可以找到带有“*”的指针引用的值。指针是一个包含值的变量,它包含的值是某个内存位置的地址。没有“转换”,指针也没有“变成”它所引用的值。同样,`&` 并不“将值转换为指针”。`&v` 的计算结果为 `v` 的地址。没有转换。 (2认同)

Joh*_*ode 27

处理数组和函数时有一种模式; 起初有点难以看到.

在处理数组时,记住以下内容是有用的:当数组表达式出现在大多数上下文中时,表达式的类型被隐式地从"N元素数组T"转换为"指向T",并且其值被设置指向数组中的第一个元素.此规则的例外情况是,数组表达式显示为&sizeof运算符的操作数,或者它是在声明中用作初始值设定项的字符串文字.

因此,当您使用数组表达式作为参数调用函数时,该函数将接收指针,而不是数组:

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }
Run Code Online (Sandbox Code Playgroud)

这就是为什么你使用&运算符作为对应于"%s"的参数scanf():

char str[STRING_LENGTH];
...
scanf("%s", str);
Run Code Online (Sandbox Code Playgroud)

由于隐式转换,scanf()接收char *指向str数组开头的值.这适用于使用数组表达式作为参数调用的任何函数(几乎任何str*函数*scanf*printf函数等).

在实践中,您可能永远不会使用&运算符调用带有数组表达式的函数,如:

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}
Run Code Online (Sandbox Code Playgroud)

这样的代码并不常见; 您必须知道函数声明中数组的大小,并且该函数仅适用于指向特定大小的数组的指针(指向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);
Run Code Online (Sandbox Code Playgroud)

您将获得以下输出:

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);
Run Code Online (Sandbox Code Playgroud)

现在您的输出将是

before swap: a = 1, b = 2
after swap: a = 2, b = 1

请注意,在交换函数中,我们不会更改xand y的值,而是更改what xy 指向的值.写作*x不同于写作x; 我们没有更新价值x本身,我们从该位置获取位置x并更新该位置的值.

如果我们想要修改指针值,这同样适用; 如果我们写

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
Run Code Online (Sandbox Code Playgroud)

然后我们修改输入参数的值stream,而不是stream 指向stream的值,因此更改对值的影响没有影响in; 为了使其工作,我们必须传入一个指针指针:

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Run Code Online (Sandbox Code Playgroud)

再次,阵列投入了一些猴子扳手.将数组表达式传递给函数时,函数接收的是指针.由于如何定义数组下标,您可以在指针上使用下标运算符,就像在数组上使用它一样:

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;}
Run Code Online (Sandbox Code Playgroud)

请注意,可能未分配数组对象; 即,你不能做类似的事情

int a[10], b[10];
...
a = b;
Run Code Online (Sandbox Code Playgroud)

所以当你处理指向数组的指针时要小心; 就像是

void (int (*foo)[N])
{
  ...
  *foo = ...;
}
Run Code Online (Sandbox Code Playgroud)

不行.


t0m*_*13b 12

简单地说

  • &表示地址,您将看到在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使用pass-by-reference 来调用函数,并与之进行比较

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


sme*_*lin 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
Run Code Online (Sandbox Code Playgroud)

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

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

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


pax*_*blo 7

事实上,你已经知道了,没有什么你需要知道的了:-)

我只想添加以下内容:

  • 这两种操作是完全相反的。&接受一个变量并给你地址,*接受一个地址并给你变量(或内容)。
  • 当您将数组传递给函数时,数组“降级”为指针。
  • 实际上,您可以在间接上有多个级别(char **p意味着这p是一个指向char.

至于工作方式不同的事情,其实并非如此:

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


小智 7

我正在浏览所有冗长的解释,所以转而使用新南威尔士大学的视频进行救援。这是一个简单的解释:如果我们有一个具有地址x和值的单元格7,那么间接询问地址值的方法7&7并且在地址处请求值的间接方法x*x.So 。(cell: x , value: 7) == (cell: &7 , value: *x)另一种查看它的方法:John坐在7th seat.The *7th seatwill 指向John&John给出 . 的address/location 7th seat。这个简单的解释帮助了我,希望它也能帮助其他人。这是优秀视频的链接:单击此处。

这是另一个例子:

#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();
}
Run Code Online (Sandbox Code Playgroud)

附加: 在使用它们之前总是初始化指针。如果没有,指针将指向任何东西,这可能会导致程序崩溃,因为操作系统会阻止你访问它知道你不拥有的内存。但简单地把 p = &x;,我们正在为指针分配一个特定的位置。


Pra*_*rav 5

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

教程非常适合初学者(清楚地解释了什么&*是什么)。是的,别忘了阅读Kenneth Reek所著的Pointers in 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;
}
Run Code Online (Sandbox Code Playgroud)


Jay*_*rod 5

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

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

当您想要获取值的地址时,请使用 &。当您想读取或写入指针中的值时,请使用 *。

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

a = *f(&a);
Run Code Online (Sandbox Code Playgroud)

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

函数指针是唯一不完全遵循规则的东西。不用&就可以取函数的地址,不用*就可以调用函数指针。