Logo
Section 1 / 7

指针

了解C语言中指针的作用,如何声明和解引用指针,以及指针大小带来的影响。

导入

指针

了解C语言中指针的作用,如何声明和解引用指针,以及指针大小带来的影响。

指针是C语言中最为强大但也最令人畏惧的特性之一。这种恐惧和不安部分源于指针在C语言中可能被以极其晦涩的方式使用。许多C语言教程或课程在讲解指针时,会让学生去解析一些看起来怪异的指针语法组合——而这些用法在实际编程中极少出现。你可能已经见过类似的语法(也可能会再次见到),但通常情况下,指针并不需要被用得异常复杂。事实上,它们的使用相当直接。

基本概念是:指针是C语言中一种特殊的数据类型,它存储的是内存中某个位置的地址。可以把指针想象成一根指向内存块的箭头,而该内存块中存放着我们关心的实际数据。这与电话号码或家庭住址的概念非常相似。

下面有一个代码示例,能更清晰地说明这一点。

指针的作用

指针使我们能够操作堆上动态分配的内存块。我们也可以用指针处理栈变量,但在大多数情况下这并非必要。

由于指针可以手动且直接访问内存块,因此它们常用于字符串和结构体。试想一下,将一个大型内存块(比如包含大量数据的结构体)传递给函数时,如果按值传递,就需要将所有数据复制到栈上。相比之下,传递该内存块的地址更加高效——无需复制,只需传递地址,函数结束后也不必删除副本。通过传递变量的地址使其在函数中可访问,这种方式称为传引用;而通过创建新的局部变量并复制其值来传递,则称为传值。

一个实际例子

要使用指针,我们需要掌握如何声明指针、将内存地址存入其中,并利用它访问对应的内存位置。

声明指针

以下是声明指针并存储地址的方法:

1#include <stdio.h> 2 3int main(void) 4{ 5 int age = 30; 6 int *p; 7 p = &age; 8 printf("age = %d\n", age); 9 printf("p = %p\n", p); 10 printf("*p = %d\n", *p); 11 printf("sizeof(p) = %ld\n", sizeof(p)); 12 *p = 40; 13 printf("*p = %d\n", *p); 14 printf("age = %d\n", age); 15 return 0; 16}

我们先从第5到第7行开始,通过图示理解其执行过程,再详细解释代码中指针的声明与使用方式。

  • 第5行:我们声明了一个整型变量 age,并初始化为 30。

  • 第6行:我们声明了一个变量 p,其类型为指向整型的指针。

    星号 * 的语法用于声明一个变量为指向特定类型的指针。通常,我们必须明确指针所指向的具体类型,唯一的例外是所谓的空指针(void pointer),即指向未知类型的指针。目前不必担心空指针。

    此时,我们有了一个指向整型的指针变量 p,但它尚未指向任何地方。我们只是在内存中预留了存储指针的空间。

  • 第7行:我们为指针 p 赋值,将其设置为变量 age 的地址。

    在C语言中,当 & 操作符出现在变量名前时,会获取该变量的内存地址。

    此时程序的状态如下:

    • 变量 age 的值是整数 30。
    • 变量 p 的值是 age 变量所在的内存地址。
    • p 所指向的内存位置中的值也是 30。
  • 第9行:打印指针 p 的值。运行程序后,输出是一串长而奇怪的字母与数字组合。这实际上是十六进制形式的内存地址。

解引用指针

访问指针所指向的值,称为解引用指针。可以通过在指针名称前加星号 * 来实现(注意:这不是在声明时使用)。例如,在上述代码中:

  • 第10行:使用 *p 语法来访问指针 p 所指向的值。此时,p 是一个整型变量的地址,而 *p 是该地址所对应变量的值。
  • 第12行:再次使用解引用,但这次是修改 p 所指向变量的值为 40。由于 p 指向 age 变量,因此相当于将 age 变量对应的内存位置的值设为 40。

指针的大小

从上面代码的第11行及其输出可以看出,我们的指针 p 占用8字节。

记住,1字节等于8位,因此一个4字节的指针最多可表示 32 位,即 2322^{32} 个不同的地址,也就是最多访问 4 GB 的内存空间。若需访问超过 4 GB 的内存,则需要更大的指针!在64位系统中,指针占8字节,支持 2642^{64} 个不同的内存地址。理论上这意味着计算机可拥有高达16艾字节(约10亿GB或100万TB)的内存——这在可预见的未来都难以实现。

指针的另一个强大用途是支持与数组的交互。

附录:环境设置

指针与数组