我们不会用bit去表达一个数据,因为只能放0和1,能表达的数据太少了,内存地址最小单位是字节
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
0x0011 |
1字节8bit,8bit才算作一个地址,地址是以字节为最小单位,如0x0011表示这8个小格的地址
指针两要素
1.地址 2.要操作的内存大小
指针的大小
在32位系统中,cpu会通过地址总线在2^32个地址中寻找其中的某一个地址值的数据,所以4个字节的指针就可以代表内存中任意位置的一个地址值,所以指针占4个字节就足够用
所以在32位系统中,所有指针大小位4字节
指针的类型
为什么要有类型的区分呢
是为了告诉编译器,处理这个地址时,必须按照对应类型的规则来处理,声明不同类型的指针变量既是规定了该变量结合指针运算符时读取内存中的字节数,同样规定了在指针移动和指针的运算时(加、减)在内存中移动的最小字节数。
例如定义“int *pa = &a”,取值时,int类型占4个字节,指针就从首地址开始移动,读取4个字节。同理,short类型占2字节,指针就移动2字节。通过声明指针类型,告诉指针每次移动多少字节,来获取变量的值。
思考
long ua { 1000 };
long long* uptr { (long long*)&ua };
std::cout << *uptr;
这段代码输出了一个很大的负数,是为什么呢?
因为指针的类型仅仅表示该指针指向的变量的内存大小,而不是指针的内存大小,所有指针的内存大小都是4字节。这里是按照long long 型来读取ua的地址,所以是一个很大的值。
知识扩展*:“值相同的两个指针变量”,意思是两个指针变量指向同一个首地址。但是如果指针变量的类型不同,因为指针移动的字节数量不同,就可能读取出不同的数据。
long long | long | &ua |
long long 和long型指向的地址都是一样的,但是读取方式却不同,long读取了四个字节的内存,long long读取了八个字节,所以,输出后是不同的
指针的运算
指针自增时,加的是1*数据类型的大小
自增是按指针指向的变量的数据类型长度来计算的,与指针本身的内存大小无关
知识扩展*:++优先级高于*,即自增优先级高于间接运算符
多重指针
对于多级指针,可以利用表格来进行运算,更加一目了然
int array[3]{ 1001,1002,1003 };
int* ptr{ &array[0] };
int** pptr{ &ptr };
*pptr ={ &array[1] };
std::cout << *ptr;
代码 | 值 | 地址 |
int array[0] | 1001 | 0x5005 |
int* ptr | 0x5005 | 0x5006 |
int **pptr | 0x5006 | 0x5007 |
*pptr | 0x5006 |
常量指针
指向常量内存地址的指针,它指向的常量的内存不能修改,因为是常量,它的指向可以修改
const int a {1000};
const int b {2000};
const int *p {&a};
p=&b; //可以这么写
*p=2000; //错误
指针常量
这个指针是一个常量,一旦指向了某个值,它的指向就不能变了,但是里面的值可以修改
const修饰指针又修饰 常量,则两者都不能修改
参考