最值得收藏的 C语言 " 指针 " 解析文章!通俗易懂易上手,超赞!

本文将介绍C语言的指针相关知识.

指针是什么?

       指针和其他的int, float等类似, 是一种类型. 有类型就有相应类型的变量和常量. 本文主要讨论变量的情况.

       指针变量就是一种变量, 和其他种类的变量类似, 但指针和其他变量又有区别.

       首先C语言作为一种类型语言, 每个变量都会有几个属性.

● 变量名称.

● 变量类型.

● 变量的值.

       例如int a = 3, 变量名称就是a, 变量类型是int, 变量的值是3, 如果不提供初始值, 那么变量的值可能是一个随机值.

       也就是说, 任何时候看到一个变量, 就会有这3个属性.

       对于指针变量, 可以认为有4个属性.

● 指针变量的名称.

● 指针变量的类型, 即指针类型.

● 指针变量的值, 即一个地址.

● 指针变量的值所指向的内存里的数据类型. 本文称做"指向类型".

       可以看到指针变量的关键在于指针所指向的内存里面数据的类型.

       例如int a = 3; int *b = &a;   指针变量名称是b, 指针变量类型是指针, 变量b的值是变量a的内存地址. 变量b所指向的内存的数据类型是int. 指针变量多了一个"变量b所指向的内存的数据类型是int”, 本文将指针变量所指向的内存的数据类型称做指向类型.

       任何时候看到一个指针就需要关注4点内容: 名称, 指针类型, 指针值, 指向类型. 搞清楚这几个内容, 就可以弄明白指针怎么回事, 当然还要记忆 一些例外的情形.


类型

       对于C语言来说, 搞清楚变量的类型相当重要, 涉及到指针的时候就更加重要. 看到一个指针变量后需要理解其指向类型.

       例如char * const * (*next)(), next是一个指针, 那么其指向类型是什么? 这个声明/定义比较复杂, 日常编程可能就会碰到比较 复杂的情况, 所以要搞清楚指针首先要懂得怎么看一个声明/定义的变量的类型.

       如果看到一个变量的声明或者定义, 那么就需要弄明白变量的类型. 在<<c专家编程>>这本书中有一部分内容专门讲解怎么分析 一个变量的类型, 值得参考.

理解类型的规则:

       1、从变量名称开始读取, 然后依照优先级按顺序处理.

       2、优先级从高到低 a. 括号内优先级高. b. 后缀操作符, ()表示一个函数, []表示一个数组. c. 前缀操作符,*表示"指向...的指针"

       3、如果const, volatile后面为类型(int, long等), 那么作用于类型, 其他情况下作用于const, volatile左边的指针*.

char * const * (*next)()

按照上面的规则来理解next的类型

       1、括号内的优先级最高, 即首先看(*next)

       2、next左边为*, 因此next是一个指针类型

       3、然后后缀()的优先级更高, 因此next是一个指针, 指向一个函数.

       4、接着是const右边的*, 表示next是一个指针, 指向一个函数, 该函数返回值类型为一个指针.

       5、char * const看作一个整体为指向字符的常量指针.

整个来说: next是一个指针, 指向一个函数, 函数的返回值也是一个指针, 指向一个类型为char的常量指针.


类型有什么用?

       C语言为类型语言, 即每个变量都有类型. 类型在变量的赋值, 函数传参, 编译检查等等方面都会用到.

类型可以确定数据的大小和操作.

       例如int a = 3, 那么在内存中会存储一个数据3, 那么对于int类型具体来说.

1、这个数据3会占用4字节(常见32位机器与64位机器上int类型占用4字节). 实际上是有4字节的内存, 内容是0×00000003. 因此int类型就规定了占用的内存大小.

2、对于int类型就可以进行+,-,*,/等操作, 但是不能进行取指针值(*a)的操作. 能够进行什么操作, 也是由类型规定的.

       那么对于指针来说, 其指向类型就非常重要, 指向类型就规定了指针的值所指向的内存的数据是什么类型, 也就是占用多大内存, 可以进行什么操作.


sizeof

       只要类型确定, 那么便可以用sizeof计算类型占用的内存大小, 这个是编译阶段便可以确定的.

       对于指针类型来说, 所有指针类型占用的内存大小基本都是一样的, 例如在32bit的机器上占用4字节, 在64bit的机器上占用8字节.

       下面代码的变量a和变量b都是指针类型, 但是指向类型不同. 因此sizeof(a)和sizeof(b)的值相等, 但是sizeof(*a)和sizeof(*b)不相等.

int*a;

double*b;

sizeof(a) ==sizeof(b);

sizeof(*a) !=sizeof(*b);


指针类型的操作

可以对指针变量进行+操作.

       double a[3] = {1,2,3};

       double *b  = a;

       printf("b: %p, content: %f\n", b, *b);

       printf("b+1: %p, content: %f\n", b+1, *(b+1));

       int c[3] = {1,2,3};

       int*d  = d;

       printf("d: %p, content: %d\n", d, *d);

       printf("d+1: %p, content: %d\n", d+1, *(d+1));

运行结果:

       b:0x7fff5f9ec7e0,content:1.000000

       b+1:0x7fff5f9ec7e8,content:2.000000

       d:0x7fff5f9ec7d0,content:1

       d+1:0x7fff5f9ec7d4,content:2

       可以看到b+1的值比b要大8. d+1的值比b要大4. b+1实际上是指向a[1]的内存地址. d+1是指向c[1]的内存地址.

       有如下公式成立, 指针做加法后的指针变量值和指向类型占用的内存大小相关.

指针变量 + 数字 = 指针变量值 + 数字 * sizeof(指向类型)

       可以看到指向类型除了告诉你指针指向的内存里面的数据类型, 在指针变量的相关运算上也是有用的.


数组类型

       数组与指针有一定的相似, 同时又很不一样.

       数组与指针的关键区别在于数组名是一个常量(和const常量不同). const常量表示变量的内容不会变化, 实际上还是一个变量. 这里所说的数组名为一个常量, 可以理解数组名称是一个内存地址值, 例如0×7fff5f9ec7d4.

       以下面的例子来说, a本身不会占用内存, 占用内存的是a[0], a[1], a[2], 实际上a所表示的这块内存才是数组变量.

       int a[10] = {0};

       int*b    = a;

       int(*d)[10]= &a;

       int c;

       c = a[1];

       c = b[1];

       那么a[1]和b[1]的区别就在于数组是一个常量, 而不是变量(变量本身需要占用内存).

       执行c = a[1]是直接从a表示的内存地址偏移4字节的内存中取数据. 仅包含一次内存读操作.

       执行c = b[1]是首先从内存中取出变量b的值, 然后将变量b的值偏移4字节, 然后从这个地址的内存中取数据. 包含2次内存读操作. 第一次是读取变量b的值.

数组的其他几个需要注意的地方:

       1、数组名称相当于地址常量, 那么这个地址指向一段内存, 因此这个地址本身会有指向类型, 其指向类型就是数组的元素类型. 例如int a[10], 那么a的指向类型就是int, 因此a+1结果实际上指向a[1].

      2、sizeof(a)是计算整个数组的类型.sizeof(*a)是计算其指向类型的大小.

      3、可以对数组名进行&a操作(取地址), 实际上&a的指针值和a的指针值一样, 而且也是个地址常量, 但是&a的指向类型 是int [10], 即指向一个包含10个int元素的数组, 所以sizeof(*&a), 计算&a的指向类型的占用内存大小就是40.

      4、数组作为函数参数传递后, 在函数内使用等价于指针. 因为函数传参是进行值传递, 相当于有一个指针变量记录数组的地址值.

       可以看到有的时候a看作一个数组(例如sizeof(a)是计算数组的内存占用), 有时候a看作一个地址常量(例如计算sizeof(*a)和a+1的时候). 还有的时候完全是比较特殊的使用(例如&a得到的指向类型为int [10]的地址常量).


函数指针

函数名本身也是一个地址常量, 其指向类型为一个函数. 实际指向的是函数在内存中的指令集合的起始位置.

       int foo(int a)

       {

              return a;

       }

       int(*p_foo)(int a) = foo;

       printf("%d, %d, %d\n",sizeof(foo),sizeof(*foo),sizeof(&foo));

       printf("%d, %d\n",sizeof(p_foo),sizeof(*p_foo));

输出值如下:

              1, 1, 8

              8, 1

       1、对函数名本身计算类型占用内存大小, 其值为1, 对于函数名的指向类型计算内存占用大小其值也为1.

       2、foo, *foo, &foo的类型相同, 但是sizeof(&foo)结果为8.

       3、函数指针可以进行多次解引用,*****p_foo == *p_foo = p_foo.

       4、函数指针可以进行调用,p_foo(3);

       以上几点可以认为是函数的特殊情形, 直接记忆.

       可以将函数的指令看作是一个unsigned char []的数组. 这样函数名就好像是一个数组名一样, 都是地址常量, 其指向类型为unsigned char类型. 但是函数指令的数组的长度是未知的, 因此编译器默认输出sizeof(foo)为1, sizeof(*foo)相当于是sizeof(unsigned char)为1.


强制类型转换

       很多时候涉及到指针和强制类型转换就会感觉比较麻烦, 实际上只要抓住类型这个关键点也可以很简单.

       强制类型转换的关键是一段内存, 这段内存里面的数据你把它当作什么类型来看待.

      double a =23.456;

      int*b = (int*) &a;

       那么变量a有一段内存(8个字节), 里面存储了23.456(按标准浮点格式存储). 然后指针b指向这段内存, 而且指针b的指向类型是int, 因此指针b认为这段内存里面存储的是一个int类型的数据.


最后

       每次看到指针的时候, 记住4个特征,不管如何进行类型转换, 多少级指针, 是否包含函数指针等, 看到指针 就思考上面几点点, 尤其是最后两点. 练习多了之后就会发现指针本身不是很难, 难的是怎么判断数据的类型。

       对于热爱编程的人来说,物以类聚,人以群分!笔者的专栏有一个编程零基础入门学习交流俱乐部(群),有一群一起学习一起解答的小伙伴很重要,还有学习视频文件,欢迎初学者和正在进阶中的小伙伴们!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,302评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,563评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,433评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,628评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,467评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,354评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,777评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,419评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,725评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,768评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,543评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,387评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,794评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,032评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,305评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,741评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,946评论 2 336

推荐阅读更多精彩内容