《C和指针》阅读笔记(5)

《C和指针》的第6章主要介绍了指针的定义以及指针的使用。指针对于很多C语言的初学者来说,简直就是一场"噩梦",谈指针色变。其实,当你真正理解了指针后,真的非常简单,只不过在使用的时候要更加细心一点罢了。本文主要是介绍自己对于指针的理解。

地址和值

首先,要有地址和值的概念。

打个比方,平常我们去就酒店开房,工作人员就会给你一张指定房间的房卡,一般房卡上就会写着房号,你就要根据这个房号的指引,找到这个房间,然后…

那么,这个"房号"就代表这个房间的地址,我们根据"房号"的指引就能找到这个房间。

假设,刚好全市对所有酒店进行专项治理活动,需要对酒店内的每个房间的客人进行身份核验。就需要去到每个房间进行访问,然后核验身份信息。

那么,每个房间"客人"就代表这个房间的,警察叔叔对每个房间进行访问,就能获取到客人的身份信息。

类似,在计算机的世界里,内存可以存储大量的数据,我们这里可以简单的将内存类比为酒店。我们知道,内存的最小存储单位是bit,每个bit可以容纳两个值0和1。显然一个bit的信息容量太小,就需要更多的bit来组成一个更大一点的单位,那么多少bit合适呢?就是一个字节,一个字节由8个bit组成,一个字节可以表示256( 2 8 2^8 28)个数。我们假设,每个字节就类似于一个房间,那么每个字节也会有一个地址,每个字节8bit的0和1的组合就是它的。比如,地址为0x100的字节的值为8。那么,简单来说,指针就表示这个地址

变量

一个数据对象的三个要素:存储地址,存储大小,存储内容。变量名,变量类型,变量值与三个要素一一对应。因此,变量名的本质就是一段内存空间地址的别名。
对于程序员来说,通常通过变量来引用内存值。显然变量名与内存的某个地址是有关联的,而这个联系是由编译器决定的(编译器在编译时将变量名看成是一个符号,符号值即为变量的地址,各种不同的符号保存在符号表中),并不需要程序员来处理,而程序员只关心变量里存储的值。例如,int a = 10;,使用变量名来关联变量a的内存,使用变量a来获取和修改这块内存的值。但是有时我们也需要知道内存的地址,以便更好地进行数据的操作和存储。

从表述方式上,“指针”表达地址的含义(指针就是地址);而int *p = 1;指针p表示“指针变量”;

指针变量 也是一种变量,同时也会有变量的声明、定义、运算等。但是和普通变量相比,它更特殊一些。

特殊的地方在于 指针变量的值是一个地址值,而不是普通变量的数值。虽然变量的值都是由0和1构成的数字,但是不同数字背后的含义不同。

综上所述,从指针的定义上的理解就是以上内容。接下来,就是实操层面,指针的灵活使用。

指向类型的指针

通常在定义指针时,需要显示的指定指针指向的数据类型,例如,

void* p1 = NULL;
int* p2 = NULL;
char* p3 = NULL;

typedef struct _fruit_s
{
    float weight;
    float price;
}fruit_t;

fruit_t* p4 = NULL;
void* p5 = NULL;

知道了怎么定义指针,上面的简单示例在初始化时都是将指针初始化为NULL。怎样将指针指向实际的内存地址呢?例如,

// 简单数据类型
int a = 10;
int* p_a = &a;

// 复合数据类型
fruit_t apple;
apple.weight = 0.55;
apple.price = 5.98;
fruit_t* p_apple = &apple;

// 动态分配水果的内存
fruit_t* p_arr = (fruit_t*)malloc(10*sizeof(fruit_t));
for (int i = 0; i < 10; ++i)
{
    p_arr[i].weight = 0.55; // 成员访问方式1
    (p_arr+i)->price = 5.98; // 成员访问方式2
}

指针的解引用(dereference)

指针的解引用就是通过一个指针来访问它所指向的内存的内容。指针的解引用操作符为星号,例如,

int a = 100;
int* p = &a;
printf("a is %d\n", *p);

指针常量、常量指针、指向常量的指针常量

当年在学校刚开始学习C语言时,总是搞不清楚这三者之间的关系,现在回头来看,觉得是很容易理解。下面结合实际的例子,来解读三者的含义

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    int a = 10;
    int b = 10;

#if 1
    // 常量指针,指向常量的指针,指针所指向的地址的值不能修改,但指针变量的值可以修改
    const int* p1 = &a; //认为a的值不能修改
    int const *p2 = &a; 
    // 上面展示了两种常量指针的定义形式,推荐使用第一种

    //*p1 = 11;   // error: assignment of read-only location ‘*p1’
                //不能通过指针变量p1解引用来修改变量a的值
    //*p2 = 12;
    p1 = &b;    // 可以修改p1的值
#endif


#if 1
    // 指针常量,指针的值不能修改,指针所指向的地址的值可以修改
    // 也就是指针常量初始化以后,其指向的地址就不能被更改
    int* const p3 = &a;

    //p3 = &b;    // error: assignment of read-only variable ‘p3’
                // 不能修改p3的值
    *p3 = 11;   // 可以修改p3所指向的地址的值
#endif

    //常量指针 是 指针所指向的地址的值不能修改
    //指针常量 是 指针的值不能修改

    //那么,有没有指针的值不能修改而且指针所指向的地址的值也不能修改呢?
    //指向常量的指针常量
    int c = 1;
    int d = 2;
    const int* const p = &c;
    *p = d; // error: assignment of read-only location ‘*p’
    p = &d; // error: assignment of read-only variable ‘p’

    return 0;
}

总之,不管是指针常量还是常量指针,只要理解常量的值是不能修改的就能万变不离其宗,只是这个数值还是地址值。我们知道通过const关键词修饰的变量即变成了常量,那么变量名就变成了常量名,普通常量的值定义后不能再被修改;同样的道理,指针变量,如果对指针变量本身进行const修饰,那么指针变量的值(也就是内存的地址)不能被修改,该指针变量总是指向一个不变的内存地址。由于指针有两层含义,如果指针变量所指向的地址的值(普通变量)修饰为const,那么通过解引用访问该内存地址时,只具有读权限,不能写;

二级指针

工程中,我们除了常用一级指针外,在数据结构中还会使用到二级指针。因为二级指针可以简化程序的实现逻辑。虽然语法上还支持多级指针,但三级及以上的指针,使用起来太复杂,使用的较少。

所谓的二级指针就是指向指针的指针。其通过两个星号来定义或声明二级指针,例如,

int a = 1;
int* p_a = &a;
int** pp_a = &p_a; // 二级指针的值即为一级指针变量的地址 

本文不展开介绍二级指针的使用示例,在后续讲解数据结构时再展开,这里先简单了解这个概念即可。

好了,第6章的总结就写到这里,最近因为家里有事,所以更新的慢了一点。

关注我

我的公众号二维码,欢迎关注

QQ讨论群:679603305

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sif_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值