C 语言指针 5 分钟教程

指针、引用和取值

什么是指针?什么是内存地址?什么叫做指针的取值?指针是一个存储计算机内存地址的变量。在这份教程里“引用”表示计算机内存地址。从指针指向的内存读取数据称作指针的取值。指针可以指向某些具体类型的变量地址,例如int、long和double。指针也可以是void类型、NULL指针和未初始化指针。本文会对上述所有指针类型进行探讨。

根据出现的位置不同,操作符 * 既可以用来声明一个指针变量,也可以用作指针的取值。当用在声明一个变量时,*表示这里声明了一个指针。其它情况用到*表示指针的取值。

&是地址操作符,用来引用一个内存地址。通过在变量名字前使用&操作符,我们可以得到该变量的内存地址。

第2行,我们通过*操作符声明了一个int指针。接着我们声明了一个int变量并赋值为1。然后我们用int变量的地址初始化我们的int指针。接下来对int指针取值,用变量的内存地址初始化int指针。最终,我们打印输出变量值,内容为1。

第6行的&val是一个引用。在val变量声明并初始化内存之后,通过在变量名之前使用地址操作符&我们可以直接引用变量的内存地址。

第8行,我们再一次使用*操作符来对该指针取值,可直接获得指针指向的内存地址中的数据。由于指针声明的类型是int,所以取到的值是指针指向的内存地址存储的int值。

这里可以把指针、引用和值的关系类比为信封、邮箱地址和房子。一个指针就好像是一个信封,我们可以在上面填写邮寄地址。一个引用(地址)就像是一个邮件地址,它是实际的地址。取值就像是地址对应的房子。我们可以把信封上的地址擦掉,写上另外一个我们想要的地址,但这个行为对房子没有任何影响。

C language logo

void指针、NULL指针和未初始化指针
一个指针可以被声明为void类型,比如void *x。一个指针可以被赋值为NULL。一个指针变量声明之后但没有被赋值,叫做未初始化指针。

执行上面的代码,你会得到类似下面对应不同内存地址的输出。

第1行我们声明了一个未初始化int指针。所有的指针在赋值为NULL、一个引用(地址)或者另一个指针之前都是未被初始化的。第2行我们声明了一个NULL指针。第3行声明了一个void指针。第4行到第6行声明了一个int值和几个int指针。

第9行到11行,我们为int指针赋值为一个引用并把int指针赋值为void指针。void指针可以保存各种其它指针类型。大多数时候它们被用来存储数据结构。可以注意到,第11行我们打印了int和void指针的地址。它们现在指向了同样的内存地址。所有的指针都存储了内存地址。它们的类型只在取值时起作用。

第15到16行,我们把void指针转换为int指针castptr。请注意这里需要显示转换。虽然C语言并不要求显示地转换,但这样会增加代码的可读性。接着我们对castptr指针取值,值为1。

第19行非常有意思,在这里打印未初始化指针和NULL指针。值得注意的是,未初始化指针是有内存地址的,而且是一个垃圾地址。不知道这个内存地址指向的值是什么。这就是为什么不要对未初始化指针取值的原因。最好的情况是你取到的是垃圾地址接下来你需要对程序进行调试,最坏的情况则会导致程序崩溃。

NULL指针被初始化为o。NULL是一个特殊的地址,用NULL赋值的指针指向的地址为0而不是随机的地址。只有当你准备使用这个地址时有效。不要对NULL地址取值,否则会产生段错误。

 

指针和数组

C语言的数组表示一段连续的内存空间,用来存储多个特定类型的对象。与之相反,指针用来存储单个内存地址。数组和指针不是同一种结构因此不可以互相转换。而数组变量指向了数组的第一个元素的内存地址。

一个数组变量是一个常量。即使指针变量指向同样的地址或者一个不同的数组,也不能把指针赋值给数组变量。也不可以将一个数组变量赋值给另一个数组。然而,可以把一个数组变量赋值给指针,这一点似乎让人感到费解。把数组变量赋值给指针时,实际上是把指向数组第一个元素的地址赋给指针。

第1行初始化了一个int数组,第2行用数组变量初始化了一个int指针。由于数组变量实际上是第一个元素的地址,因此我们可以把这个地址赋值给指针。这个赋值与int *ptr = &myarray[0]效果相同,显示地把数组的第一个元素地址赋值到了ptr引用。这里需要注意的是,这里指针需要和数组的元素类型保持一致,除非指针类型为void。

 

指针与结构体

就像数组一样,指向结构体的指针存储了结构体第一个元素的内存地址。与数组指针一样,结构体的指针必须声明和结构体类型保持一致,或者声明为void类型。

第1至6行声明了一个person结构体,一个变量指向了一个person结构体和指向person结构体的指针。第8行为age成员赋了一个int值。第9至10行我们声明了一个char指针并赋值给一个char数组并赋值给结构体name成员。第11行我们把一个person结构体引用赋值给结构体变量。

第13行我们打印了结构体实例的age和name。这里需要注意两个不同的符号,’.’ 和 ‘->’ 。结构体实例可以通过使用 ‘.’ 符号访问age变量。对于结构体实例的指针,我们可以通过 ‘->’ 符号访问name变量。也可以同样通过(*ptr).name来访问name变量。

 

总结

希望这份简短的概述能够有助于了解不同的指针类型。在后续的博文中我们会探讨其它类型的指针和高级用法,比如函数指针。

欢迎提出提问并给出评论。

 

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

1 23 收藏 21 评论

关于作者:唐尤华

唐尤华:我喜欢程序员,他们单纯、固执、容易体会到成就感;面对压力,能够挑灯夜战不眠不休;面对困难,能够迎难而上挑战自我。他们也会感到困惑与傍徨,但每个程序员的心中都有一个比尔盖茨或是乔布斯的梦想“用智慧开创属于自己的事业”。我想说的是,其实我是一个程序员。 个人主页 · 我的文章 · 18 ·    

相关文章

可能感兴趣的话题



直接登录
最新评论
  • 很不错的一片文章

  • wqooops   2013/03/10

    好文章...
    “这个赋值与*ptr = &myarray[0]效果相同” 这里是不是有问题啊? 应该是 ptr = &myarray[0]
    或者 int *ptr = &myarray[0] 吧? 不知道我理解的对不对...

  • louzp   2013/03/18

    文中的第一段代码有点问题吧。如果用C的话变量声明要全部放在main函数的开头?
    就是 int deref = *ptr; 这一行,会出现error。
    C++应该没问题。
    我是菜鸟,说错的话请见谅。

    • 类似的代码能正常编译运行。
      #include "stdio.h"
      int main(void)
      {
      int val = 1;
      int * ptr;
      ptr = &val;
      printf("%d %p",* ptr, ptr);
      int val2 = * ptr;
      printf("\n%d",val2);
      }

    • Destro 学生 2016/04/22

      对于ANSI C来说是这样的。但是对于它之后的标准(如ISO C99),都不要求这样,即不要求"声明和代码分离“。

  • 了迟早的事   2013/04/01

    char *fullname = "full name";能帮我解释这句话的含义吗?

  •   2013/04/12

    这是一篇非常好的讲解,简短的实例覆盖了指针的精华,欣赏!

  • fuqi7758521   2013/08/02

    好文章,上篇介绍函数指针的也相当不错

  • phoo   2013/08/06

    int *ptr;
    int val = 1;

    有什么区别?

    val = &ptr;
    这样子可以吗?为什么不可以
    我的理解是,不可以,占用内存不一样。

    • phoo   2013/08/07

      或者说 val是值 *ptr是地址 那值和地址有什么区别? 还是说都是内存地址,而*ptr是动态地址,val是静态的?

  • 好文章,必须得赞~~

  • gavin   2014/02/17

    这篇文章对C语言一知半解,还说什么引用。这分明就是指针。指针和引用是有区别的,不要误导别人。引用只有C++才有,可相当于变量的别名,不占额外的存储空间。而文章提到的是指针,指针是需要存储空间来存放地址的。而且在参数传递上存在本质区别,不要写这些误导别人的文章!!!

    • Libra++   2014/02/18

      大哥,人家是在讲C语言,人家说的“引用”不是C++的引用,自己没看清楚就喷。

  • Mick   2014/05/28

    都是基础知识,具有参考价值。

  • 小法老   2015/05/22

    顶好文~~~可惜没有后续><

跳到底部
返回顶部