目录
[1. 内存](#1. 内存)
[2. 编址编辑](#2. 编址编辑)
[3. 指针变量和地址](#3. 指针变量和地址)
[4. 指针变量的大小](#4. 指针变量的大小)
[5. 指针变量类型的意义](#5. 指针变量类型的意义)
[6. 指针 +- 运算 (整数)](#6. 指针 +- 运算 (整数))
[7. void* 指针](#7. void* 指针)
1. 内存
内存划分为一个个的内存单元,每个内存单元的大小取1个字节
计算时 CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中
一个比特位可以存储一个2进制的位1或者0
bit - 比特位
byte - 字节 1byte = 8bit
KB 1KB = 1024byte
MB 1MB = 1024KB
GB 1GB = 1024MB
TB 1TB = 1024GB
PB 1PB = 1024TB
每个内存单元也都有一个编号,有了这个内存单元的编号,CPU就可以快速找到一个内存空间,同时C语言中给地址起了新的名字叫:指针 所以我们可以理解为:内存单元的编号 == 地址 == 指针
2. 编址
CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址
计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成
通过地址总线,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器
3. 指针变量和地址
1)取地址操作符(&)
在C语言中创建变量其实就是向内存申请空间
#include <stdio.h>
int main()
{
int a = 10;//创建了整型变量a,内存中申请4个字节,用于存放整数10
return 0;
}
我们可以通过取地址操作符(&)来得到a的地址
{
int a = 10;
&a;//取出a的地址
printf("%p\n", &a);
return 0;
}
推理图:
&a取出的是a所占4个字节中地址较小的字节的地址,虽然整型变量占用4个字节,我们只要知道了第一个字节地址,访问到4个字节的数据也是可行的
2)指针变量
我们通过取地址操作符(&) 拿到的地址是一个数值,比如:0x006FFD70,这个数值有时候也是需要存储起来,方便后期再使用的,那我们把这样的地址值存放**指针变量(其中的值为地址)**中
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
}
3)指针类型
int a = 10;
int * pa = &a;//pa左边写的是int* , * 是在说明pa是指针变量,而前面的int 是在说明pa指向的是整型(int)类型的对象
char ch = 'w';
pc = &ch;//同理
4)解引用操作符
通过地址(指针)找到地址(指针)指向的对象,这里必须学习一个操作符叫解引用操作符 ( * )
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;//*pa 的意思就是通过pa中存放的地址,找到指向的空间,*pa其实就是a变量了;所以*pa = 0,
//这个操作符是把a改成了0.
return 0;
}
4. 指针变量的大小
32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节才能存储
如果指针变量是用来存放地址的,那么指针变量的大小就得是4个字节的空间才可以
同理64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要8个字节的空间,指针变量的大小就是8个字节
#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%zd\n", sizeof(char *)); //4/8
printf("%zd\n", sizeof(short *)); //4/8
printf("%zd\n", sizeof(int *)); //4/8
printf("%zd\n", sizeof(double *));//4/8
return 0;
}
注意:指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的
5. 指针变量类型的意义
1)指针的解引用
//代码1
#include <stdio.h>
int main()
{
int n = 0x11223344;
int *pi = &n;
*pi = 0;
return 0;
}
//代码2
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
*pc = 0;
return 0;
}
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为 0
结论 :指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节
6. 指针 +- 运算 (整数)
#include <stdio.h>
int main()
{
int n = 10;
char *pc = (char*)&n;//强制类型装换 ( )
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
我们可以看出, char* 类型的指针变量+1跳过1个字节,int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化
结论:指针的类型决定了指针向前或者向后走一步有多大距离
7. void* 指针
在指针类型中有一种特殊的类型是void* 类型的,可以理解为无具体类型的指针(或者叫泛型指
针),这种类型的指针可以用来接受任意类型地址。
注意:void* 类型的指针不能直接进行指针的+-整数和解引用的运算
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;//nt类型的变量的地址赋值给一个char*类型的指针变量 编译器给出了一个警告
return 0;
}
由于类型不兼容。而使用void*类型就不会有这样的问题
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}
一般void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以
实现泛型编程的效果。使得一个函数来处理多种类型的数据
谢谢观看