目录
一、内存和地址
1.内存
生活中,每个房间有房间号,就能提高效率,快速找到房间,每个内存单元也有编号(这个编号相当于宿舍房间门牌号)
把内存划分成一个个单元,一个内存大小为一个字节
一个字节就好比一个学生宿舍,宿舍是八人间,每个人就是一个比特位
因此:内存单元的编号 == 地址 == 指针
扩展:
计算机储存单位:计算能够识别存储、运算的时候都是使用2进制的
2.理解编址
计算机访问内存空间必须知道内存空间的地址
地址总线的一根线有0/1两种信号,两根线就会有四种信号,如果电脑是32位那就是32个地址线, 如果电脑是64位那就是64个地址线
数据总线:就是要传输的数据
控制总线:控制数据的操作
下图是大致的计算机操作图,在计算机硬件上有约定好的地址
二、指针变量和地址
1.变量
要想要知道指针变量,那么我们就要知道变量,其实变量的本质就是在内存中开辟一块空间
代码如下(示例):
#include<stdio.h>
int main()
{
int a = 20;
printf("%p",&a);
return 0;
}
//&取地址操作符 ;%p是专门打印地址的
//取出的是a的4个字节中的第一个地址(较小的地址)
2.指针变量
2.1 * :解引用操作符
如下图:
int* p中 * 是在说明p是指针变量;int是在说明p指向的是int类型
用代码表示
2.2指针变量的大小
这里的p也是变量,也是需要向内存申请一块空间,这样才能存放地址
那么p指针变量需要多大的空间?
存放的地址,地址存放需要多大,指针就多大
下面我们使用vs编译器来看看
可以看到指针变量的大小和类型无关
- (x64)64位机器:地址是64个0/1的二进制序制,存储起来就需要64个bit位,也就是8个字节
- (x86)32位机器:地址是32个0/1的二进制序制,存储起来就需要32个bit位,也就是4个字节
因此只要指针类型的变量,在相同平台下大小都一样
【注】口头语中指针一般是指针变量
- 指针其实就是地址
- 指针变量是存放指针的
三、指针变量类型意义
指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各 种各样的指针类型呢?
1.指针解引用
代码1
代码2
通过上述两段代码的调试:我们观察到代码1会将a的4个字节全部改为0,但是代码2只是将a的第⼀个字节改为0。
结论:所以指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)
- int*的指针解引用访问4个字节
- char*的指针解引用访问1个字节
- short*的指针解引用访问2个字节
2.指针+-解引用
从上述代码可以看出
- int*的指针类型+1跳过4个字节
- char*的指针类型+1跳过1个字节
type* p
p+i 是跳过i个type类型的数据,相当于跳过i * sizeof(type)个字节
int* p
p+2相当于跳过2个int类型的数据(2*sizeof(int)==8个字节)
结论:指针类型决定了指针的步长,就是向前/向后走一步有多大距离
【注】根据实际需要,选择适当的指针类型
3.void指针
#include<stdio.h>
int main()
{
int a = 10;
int* pa=&a;
char* pc = &a;
return 0;
}
如果是上述这样写的代码编译器就会报警告
#include<stdio.h>
int main()
{
int a = 10;
void* pv=&a;
double i = 2.0;
void* po = &i;
return 0;
}
使用void指针可以接受任意类型地址
局限性:void*的指针不能直接进行指针的+-整数和解引用的运算
四、const 修饰指针
1.const 修饰变量
代码1:
#include<stdio.h>
int main()
{
int n = 10;//n是变量
n = 0;
printf("%d\n", n);
}
#include<stdio.h>
int main()
{
//语法 - 语言的法则
const int n = 10;//n是变量//const 常属性 - 不能被修改了
//const修饰了n之后,n不能被修改了,但是n还是变量
//n是常变量
n = 0;//不能修改n
printf("%d\n", n);
}
代码2
#include<stdio.h>
int main()
{
int arr[10] = {0};
const int n = 10;//c++ 中const修饰的n是常量
int arr[n] = { 0 };
return 0;
}
代码3
#include<stdio.h>
int main()
{
const int n = 10;
n=0;//err
printf("%d\n", n);
return 0;
}
2.const 修饰指针变量
前提
- p是指针变量,里边存放了其他变量的地址
- p是指针变量,*p是指针指向的对象n
- p是指针变量,p也有自己的地址,&p
这段代码是const修饰变量
2.1const修饰指针,有两种情况
- cconst 放在*的左边,限制的是*p,不能通过p来改变指向对象的内容,但是p本身是可以改变的
const int* p = &n;
- const放在*的右边,限制的是p,不能修改p本身是可以改变的,但是p指向的内容可以通过p来改变的
int* const p = &n;
五、指针运算
1.指针+-整数
如图所示:
数组在内存中是连续存放的,随着数组下标的增长,地址也是由低到高的
代码:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int* p = &arr[0];
for (i = 0; i < sz; i++)
{
printf("%d ", *p);
p++;//p=p+1
}
return 0;
}
画图演示:
2.指针-指针
指针 - 指针,运算的前提是两个指针指向了同一块空间
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9]-&arr[0]);//指针 - 指针
printf("%d\n", &arr[0]-&arr[9]);//指针 - 指针
//指针 - 指针的绝对值是指针和指针之间的元素个数
char ch[6] = { 0 };
printf("%d\n", &arr[9] - &arr[4]);//err
return 0;
}
3.指针的关系运算
strlen的模拟实现
#include<stdio.h>
#include<string.h>
int my_strlen(char* p)
{
int count = 0;
/*while (*p != '\0')
{
count++;
p++;
}*/
return count;
}
int my_strlen(char* p)
{
char* p1 = p;
while (*p != '\0')
{
p++;
}
return p - p1;//指针 - 指针
}
int main()
{
char arr[] = "abcdef";
//数组名其实就是数组首元素地址
//arr == &arr[0]
int len = my_strlen(arr);
printf("%d\n", len);//指针 - 指针
return 0;
}
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
while (p < arr + sz)//指针的关系运算
{
printf("%d ", *p);
p++;
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
while (p < arr + sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
总结
这里是指针的第一小部分,主要包括了内存和地址、指针变量和地址、指针变量类型意义、const修饰指针、指针的运算。