C语言指针学习

在学习指针之前,先让我们了解一下内存


内存

存储器:存储数据器件

  • 外存:
    • 外存又叫外部存储器,长期存放数据,掉电不丢失数据
    • 常见的外存设备:硬盘、flash、ROM、U盘、光盘、磁带
  • 内存:
    • 内存又叫内部存储器,暂时存放数据,掉电数据丢失
    • 常见的内存设备:RAM、DDR
    • 物理内存:实实在在存在的存储设备
    • 虚拟内存:操作系统虚拟出来的内存

我们可以把内存想象成一列很长很长的货运火车,有很多大小相同的车厢,而每个车厢正好相当于在内存中表示一个地址。这些车厢装着不同的货物,就像我们的内存要存着各式各样的数据在这里插入图片描述
是不是这样就好理解一些了呢

再比如我们生活中,平时在电脑上看的视频、听的音乐和文章,其实都是内存中每个 “车厢” 里面的数据,这些数据最终还是由二进制 0/1 演变而成

虽然视频、文章和音乐等这些信息在我们眼中是不同的,但对于计算机来说它们在内存中都是以二进制的形式来表示的

因为我们要知道去哪存或取数据,所以内存中每个字节都有对应的编号,就像火车上的车厢编号一样。而这个内存中每个字节的编号就是我们常说的内存地址,是按一个字节接着一个字节的次序进行编址。如下图所示:
在这里插入图片描述


关于内存字节

  • 1个内存地址只存1个字节(Byte)
  • 1个字节等于8位二进制,每一位二进制的0或1,叫:比特(bit)
  • 比特是最小单位,字节是比特的集合,也是一个单位

内存给数据类型地址分配如下

  • char占一个字节,分配一个地址
  • int占四个字节,分配四个地址
#include<stdio.h>
int main () 
{
    printf("sizeof(char)=%u\n",sizeof(char));	// 1
    printf("sizeof(int)=%u\n",sizeof(int));		// 4
	return 0;
}

地址总线

再让我们聊一聊地址总线

  • 地址总线(Address
    Bus)是一种计算机总线,是CPU或有DMA能力的单元,用来沟通这些单元想要访问(读取/写入)计算机内存组件/地方的物理地址。
  • 数据总线的宽度,随可寻址的内存组件大小而变,决定有多少的内存可以被访问。
  • 地址总线是一个从CPU到内存的内部信道,其主要进行数据地址的传输。每个存储单元都有一个固定地址,地址总线的宽度决定了CPU的最大寻址能力。例如:如果地址总线包括n个电线,那么处理器的寻址能力可高达2^n个独立单位。
  • 每条地址线一般只有一根导线,每根地址线输出高低电平两种状态,配合其他信号线完成寻址(选中)操作

这么说估计还是很懵对吧?

但我们结合上电脑的配置就可能会好一点;比如现在的电脑基本都是64位的,但在以前32位居多,那像这里说到的64和32,可能还有其他小型计算机的16等这些数字是什么意思呢??

这些数字正是我们所说的地址总线,但是32位电脑是32根地址总线,而64位电脑则不到64根地址总线啦。。。(这主要还是根据电脑的内部来决定的),而地址总线也就是帮我们寻找地址时所要用的

我们将32位平台为例介绍:

  • 在32位平台中,每一个进程拥有4G的空间(2^32)
  • 系统为内存的每一个字节分配一个32位的地址编号(4个字节)

指针

在理解完内存的概念之后,我们开始来学习指针。千万不要把指针想的太复杂,但也不要轻视它。

指针的实质就是内存“地址”,可以说是指针就是地址,其实指针就是保存地址的变量(不是普通变量)
在这里插入图片描述
对于其他变量来说,就是普通的数值;而对于指针变量来说就表示内存地址编号


指针变量的定义

1、定义步骤(定义的时候)

  • *修饰指针变量p
  • 保存谁的地址,你先定义谁
  • 从上往下整体替换

定义一个指针变量p,保存 int num的地址:

int *p;

定义一个指针变量p,保存数组 int arr[5]首地址:

int (*p)[5];

定义一个指针变量p,保存函数的入口地址:

int fun(int a, int b);
int (*p)(int a, int b);

定义一个指针变量p,保存结构体变量的地址:

struct stu lucy;
struct stu *p;

定义一个指针变量p,保存指针变量 int *p的地址

int **p

2、指针变量的详解

  • 在32位平台任何类型的指针变量,都是4字节
  • 在64位平台任何类型的指针变量,都是8字节

在64位的平台下测试,全都为8字节

#include<stdio.h>
void test()
{
	printf("%lu\n",sizeof(char *));		// 8
	printf("%lu\n",sizeof(short *));	// 8
	printf("%lu\n",sizeof(int *));		// 8
	printf("%lu\n",sizeof(long *));		// 8
	printf("%lu\n",sizeof(float *));	// 8
	printf("%lu\n",sizeof(double *));	// 8
	printf("%lu\n",sizeof(bool *));		// 8
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述
普通变量、指针变量、普通变量和指针变量建立关系

  • p的值就是num的地址;
  • p保存了num的地址(p保存了 …的地址);
  • p指向了num(p指向了 …)
#include<stdio.h>
void test()
{
	int num = 10;
	int *p;
	// 建立关系
	p = &num;	// p指向num
	// %p 打印地址编号 
	printf("p = %p\n", p);			// 000000000062FDE4
	printf("&num = %p\n", &num);	// 000000000062FDE4
}
int main()
{
	test();
	return 0;
}

在使用的时候,*p表示取p所保存的地址编号对应空间的内容

p等价于&num;*p等价num

在这里插入图片描述

  • *q == p == &num
  • **q == *p == num

3、指针变量的初始化(了解)

指针变量在操作前,必须是指向合法的地址空间。

  • 1、指针变量如果不初始化,立即操作会出现段错误(操作系统发现程序非法动用内存地址,操作系统则会杀死这个进程,并提醒程序猿)
    void test03()
    {
    	// p是局部指针变量 未初始化 指向不确定的内存地址
    	int *p;
    	printf("*p = %d\n", *p);
    }
    
  • 2、指针变量没有 如果没有指向合法的空间,建议初始化为NULL(不要操作指向NULL的指针变量)
    // NULL的本质: 地址编号为0
    int *p = NULL;	// NULL赋值给p
    // int *p;
    // p = NULL;
    
  • 3、将指针变量初始化合法地址(变量地址、动态申请的地址、函数的入口地址)
    int num = 10;
    int *p = &num;	// int *p;  p = #;
    
    int data = 10, *p = &data;
    

4、指针变量的类型

int *p;
  • 1、指针变量自身的类型:将指针变量名拖黑,剩下的类型就是指针变量自身的类型(指针变量自身的类型,一般用于赋值语句的判断)

    int *p; 	// p自身的类型为 int *
    
  • 2、指针指向的类型:将指针变量名和离它最近的一个* 一起拖黑,剩下的类型就是指针变量指向的类型

    int *p;		// p指向的类型为int
    

案例:int num = 10, *p = &num, **q = &p;,以下结果正确的是( ABC )

  • A、*p = 100
  • B、*q = &num
  • C、p = &num
  • D、q = &num
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值