指针介绍

指针的声明与其他任何变量一样,除了在变量的类型和名称之间放置星号(*)以表示它是一个指针。

int *pointer; /* inside a function, pointer is uninitialized and doesn't point to any valid object yet */

要声明两个相同类型的指针变量,请在同一声明中使用每个标识符前面的星号。例如,

int *iptr1, *iptr2;
int *iptr3,  iptr4;  /* iptr3 is a pointer variable, whereas iptr4 is misnamed and is an int */

由&符号(&)表示的地址或引用运算符给出了给定变量的地址,该变量可以放在适当类型的指针中。

int value = 1;
pointer = &value;

由星号(*)表示的间接或解除引用运算符获取指针指向的对象的内容。

printf("Value of pointed to integer: %d\n", *pointer);
/* Value of pointed to integer: 1 */

如果指针指向结构或联合类型,那么你可以取消引用它并使用 -> 运算符直接访问其成员:

SomeStruct *s = &someObject;
s->someMember = 5; /* Equivalent to (*s).someMember = 5 */

在 C 中,指针是一种不同的值类型,可以重新分配,否则将被视为一个变量。例如,以下示例打印指针(变量)本身的值。

printf("Value of the pointer itself: %p\n", (void *)pointer);
/* Value of the pointer itself: 0x7ffcd41b06e4 */
/* This address will be different each time the program is executed */

因为指针是一个可变变量,所以它可能无法通过设置为 null 来指向有效对象

pointer = 0;     /* or alternatively */
pointer = NULL;

或者只是包含一个不是有效地址的任意位模式。后者是一个非常糟糕的情况,因为在指针被取消引用之前无法测试,只有对指针为空的情况进行测试:

if (!pointer) exit(EXIT_FAILURE);

如果指针指向有效对象,则只能取消引用指针,否则行为未定义。许多现代实现可能会通过引发某种错误(如分段错误和终止执行) 来帮助你,但其他实现可能只会使你的程序处于无效状态。

解引用运算符返回的值是原始变量的可变别名,因此可以更改它,修改原始变量。

*pointer += 1;
printf("Value of pointed to variable after change: %d\n", *pointer);
/* Value of pointed to variable after change: 2 */

指针也可以重新分配。这意味着指向对象的指针稍后可用于指向同一类型的另一个对象。

int value2 = 10;
pointer = &value2;
printf("Value from pointer: %d\n", *pointer);
/* Value from pointer: 10 */

与任何其他变量一样,指针具有特定类型。例如,你不能将 short int 的地址分配给指向 long int 的指针。这种行为被称为类型惩罚,并且在 C 中是被禁止的,尽管有一些例外。

尽管指针必须是特定类型,但为每种类型的指针分配的内存等于环境用于存储地址的内存,而不是指向的类型的大小。

#include <stdio.h>

int main(void) {
    printf("Size of int pointer: %zu\n", sizeof (int*));      /* size 4 bytes */
    printf("Size of int variable: %zu\n", sizeof (int));      /* size 4 bytes */
    printf("Size of char pointer: %zu\n", sizeof (char*));    /* size 4 bytes */
    printf("Size of char variable: %zu\n", sizeof (char));    /* size 1 bytes */
    printf("Size of short pointer: %zu\n", sizeof (short*));  /* size 4 bytes */
    printf("Size of short variable: %zu\n", sizeof (short));  /* size 2 bytes */
    return 0;
}

(注意:如果你使用的是不支持 C99 或 C11 标准的 Microsoft Visual Studio,则必须在上面的示例中使用 %Iu 1 而不是%zu。)

请注意,上面的结果可能因环境而异,但所有环境对于不同类型的指针都会显示相同的大小。

根据卡迪夫大学 C 指针介绍的信息提取

指针和数组

指针和数组在 C 中紧密相连 .C 中的数组总是保存在内存中的连续位置。指针算术始终按指向的项目大小进行缩放。因此,如果我们有一个三个双打的数组,以及指向基数的指针,则*ptr 指的是第一个双重,*(ptr + 1) 指向第二个,*(ptr + 2) 指向第三个。更方便的表示法是使用数组符号 []

double point[3] = {0.0, 1.0, 2.0};
double *ptr = point;

/* prints x 0.0, y 1.0 z 2.0 */ 
printf("x %f y %f z %f\n", ptr[0], ptr[1], ptr[2]);

所以基本上 ptr 和数组名称是可以互换的。此规则还意味着数组在传递给子例程时会衰减到指针。

double point[3] = {0.0, 1.0, 2.0};

printf("length of point is %s\n", length(point));

/* get the distance of a 3D point from the origin */ 
double length(double *pt)
{
   return sqrt(pt[0] * pt[0] + pt[1] * pt[1] + pt[2] * pt[2])
}

指针可以指向数组中的任何元素,也可以指向最后一个元素之外的元素。但是,将指针设置为任何其他值(包括数组之前的元素)是错误的。 (原因是在分段体系结构上,第一个元素之前的地址可能跨越段边界,编译器确保最后一个元素加上一个元素不会发生)。

脚注 1:可以通过 printf()格式规范语法找到 Microsoft 格式信息。