C语言指针5分钟教程-阿里云开发者社区

开发者社区> yuanrengu> 正文

C语言指针5分钟教程

简介: 指针、引用和取值 什么是指针?什么是内存地址?什么叫做指针的取值?指针是一个存储计算机内存地址的变量。在这份教程里“引用”表示计算机内存地址。从指针指向的内 存读取数据称作指针的取值。指针可以指向某些具体类型的变量地址,例如int、long和double。
+关注继续查看

指针、引用和取值

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

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

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

1
2
3
4
5
6
7
8
9
// 声明一个int指针
int *ptr;
// 声明一个int值
int val = 1;
// 为指针分配一个int值的引用
ptr = &val;
// 对指针进行取值,打印存储在指针地址中的内容
int deref = *ptr;
printf("%d\n", deref);

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

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

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

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

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int *uninit; // int指针未初始化
int *nullptr = NULL; // 初始化为NULL
void *vptr; // void指针未初始化
int val = 1;
int *iptr;
int *castptr;
 
// void类型可以存储任意类型的指针或引用
iptr = &val;
vptr = iptr;
printf("iptr=%p, vptr=%p\n", iptr, vptr);
 
// 通过显示转换,我们可以把一个void指针转成
// int指针并进行取值
castptr = (int *)vptr;
printf("*castptr=%d\n", *castptr);
 
// 打印null和未初始化指针
printf("uninit=%p, nullptr=%p\n", uninit, nullptr);
// 不知道你会得到怎样的返回值,会是随机的垃圾地址吗?
// printf("*nullptr=%d\n", nullptr);
// 这里会产生一个段错误
// printf("*nullptr=%d\n", nullptr);

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

1
2
3
iptr=0x7fff94b89c6c, vptr=0x7fff94b89c6c
*castptr=1
uninit=0x7fff94b89d50, nullptr=(nil)

第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
2
3
4
5
6
7
8
int myarray[4] = {1,2,3,0};
int *ptr = myarray;
printf("*ptr=%d\n", *ptr);
 
// 数组变量是常量,不能做下面的赋值
// myarray = ptr
// myarray = myarray2
// myarray = &myarray2[0]

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

 

指针与结构体

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

1
2
3
4
5
6
7
8
9
10
11
12
13
struct person {
  int age;
  char *name;
};
struct person first;
struct person *ptr;
 
first.age = 21;
char *fullname = "full name";
first.name = fullname;
ptr = &first;
 
printf("age=%d, name=%s\n", first.age, ptr->name);

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

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

原文链接: Dennis Kubes   翻译: 伯乐在线 - 唐尤华
译文链接: http://blog.jobbole.com/25409/

img_e00999465d1c2c1b02df587a3ec9c13d.jpg
微信公众号: 猿人谷
如果您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】
如果您希望与我交流互动,欢迎关注微信公众号
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
无影云桌面和服务器有什么区别?阿里云无影电脑有什么优势?
阿里云推出无影云桌面,云桌面是一种安全高效的云上桌面服务,一般用于企业办公。云桌面支持快速便捷的桌面环境创建、部署、统一管控与运维。企业选择云桌面无需前期传统硬件投资,云桌面可以快速构建安全、高性能、低成本的企业桌面办公体系。云桌面和传统PC、VDI之间的区别:
8 0
项目中用了 Disruptor 之后,性能提升了2.5倍
存储设备往往是速度越快价格越昂贵,速度越快价格越低廉。在计算机中,CPU 的速度远高于主存的速度,而主存的速度又远高于磁盘的速度。为了解决不同存储部件的速度不对等问题,让高速设备充分发挥性能,引入了多级缓存机制。
13 0
Spring Security 授权整体流程
统一鉴权安全框架Security的授权分析
6 0
关于点播视频文件生成封面的实现方案设计思考
对于多媒体存储服务,点播视频文件生成封面应该是非常基础的功能之一,目前自己正在开发的多媒体存储服务就面临这样的需求。因为这个多媒体存储服务是从零开始的,缺少部分基础功能的情况,各位大佬别见笑。 但是,正是因为从零开始开发,因此很多之前被忽略的问题也渐渐浮出水面,映入了我们的眼帘。这个经历过程是非常可贵的,自己从中也学到了很多东西。
17 0
从零实现一个日志框架(带源码)
Java里的各种日志框架,相信大家都不陌生。Log4j/Log4j2/Logback/jboss logging等等,其实这些日志框架核心结构没什么区别,只是细节实现上和其性能上有所不同。本文带你从零开始,一步一步的设计一个日志框架
17 0
你还在用命令看日志?快用 Kibana 吧,一张图片胜过千万行日志!
Kibana是一个开源的分析和可视化平台,设计用于和Elasticsearch一起工作。 你用Kibana来搜索,查看,并和存储在Elasticsearch索引中的数据进行交互。
9 0
关于HTTP你需要知道的基础知识
作为一个 web 开发,HTTP 几乎是天天要打交道的东西,但我发现大部分人对 HTTP 只是浅尝辄止,对更多的细节及原理就了解不深了,在面试的时候感觉非常吃力。这篇文章就是为了帮助大家树立完整的 HTTP 知识体系,并达到一定的深度,从容地应对各种灵魂之问,也同时提升自己作为一个 web 开发的专业素养吧
3 0
关于HTTP你需要知道的基础知识
作为一个 web 开发,HTTP 几乎是天天要打交道的东西,但我发现大部分人对 HTTP 只是浅尝辄止,对更多的细节及原理就了解不深了,在面试的时候感觉非常吃力。这篇文章就是为了帮助大家树立完整的 HTTP 知识体系,并达到一定的深度,从容地应对各种灵魂之问,也同时提升自己作为一个 web 开发的专业素养吧
6 0
一文了解ASCII、Unicode、UTF-8前生今世
我们常常在开发是出现乱码问题或者常常或多或少注意过UTF-8,这是什么东西呢。本文当你走进它们的世界。
10 0
+关注
529
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载