在C语言中指针:何时使用“&”和“*”?

12 浏览
0 Comments

在C语言中指针:何时使用“&”和“*”?

我刚开始学习指针,有些困惑。我知道&表示变量的地址,并且可以在指针变量前使用*来获得指针指向的对象的值。但是当你处理数组、字符串或者使用变量的指针副本调用函数时,情况又有所不同。在所有这些中,很难看到逻辑的模式。

什么时候应该使用&*

admin 更改状态以发布 2023年5月24日
0
0 Comments

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

在处理数组时,记住以下有用的信息:当大多数情况下,数组表达式出现时,表达式的类型会从 "N元素的T数组" 隐式转换为 "指向T的指针",其值设置为指向数组中第一个元素。这个规则的例外是当数组表达式作为 &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]) {...}

这样的代码并不常见;你必须知道函数的声明中数组的大小,而且函数只能与特定大小的数组指针一起使用(指向10元素的T数组的指针与指向11元素的T数组的指针是不同的类型)。

当一个数组表达式作为 & 运算符的操作数时,结果表达式的类型是 "指向N元素的T数组",或者是 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 = 2, b = 1

形式参数 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);

现在你的输出将会是:

交换前:a = 1,b = 2
交换后: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 = ...;
}

0
0 Comments

你有指针和值:

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

0