0.1 地址
(1)字节(Byte)
一个字节存储8位无符号数(1Byte = 8 bit),每个字节储存的数值范围为0-255
(2)内存
1G = 1024M 、1M = 1024K 、 1K = 1024Byte 、1Byte = 8 bit
(3)地址
在计算机中一个地址代表一个字节,比如:地址0x00到0x01的大小为一个字节
栈和堆知识点
0.2 变量的存储
int a = 5 :在栈中定义了一个变量a,并且在内存中开辟了一个int类型大小的空间, 即4个字节。
在a的自己的那片空间里面存放数值5 ,把5转换成二进制,存到a的4个字节的空间 。
1.1 创建指针
(1)先定义后赋值
int *p;
p = &a; 可理解为类型为int *(整型的指针类型),变量名为p
(2)定义赋值一步完成
int *p = &a;
1.2 指针的存储
int a = 5 ;int *p = &a :&a 拿到 a 的首地址0x43c00060,然后把0x43c00060这个值存放到p的存储空间中(另一个地址)。0x43c00060的二进制:
01000011
11000000
00000000
01100000 把变量a的地址赋给了指针变量p
1.3 创建多级指针
int **q;
q = &p; p本身也是一个指针变量,它的值为a的地址
二级指针只能用于存储一级指针变量的地址。即只能指向一级指针。
代码如 int a = 5 ;int **q = &a 这在语法上是不被允许的,
n级指针同理,只能用于存储n-1级指针变量的地址。
2.1 解引用
*(解引用)的作用是引用指针指向的变量值,&(取地址)其实就是引用该变量的地址。
p = 6618636 *p = 5
q = 6618624 *q = 6618636 **q = 5 * 本质上就是访问存储的地址所存储的数据
a = 5 &a = 6618636
p = 6618636 &p = 6618624 & 与 * 互为逆运算
2.2 指针运算
(1)指针 +- 整数
*p++ = 0; 先给p指向的地址赋值,再移动指针
指针+1所移动的距离为指针步长。
指针步长由指针数据类型决定,如int *p的指针步长为4 Byte 。
(2)指针 - 指针
&arr[9] - &arr[0]; 结果为9
指针减去相同类型的指针,得到的结果为相差的存储元素个数。
不同类型指针相减,系统报错。
(3)指针比较大小
实际是比较指针所存储地址的大小:*p >*q 等同于 &a >*q。
注:标准规定,允许指向数组的指针与指向数组最后一个元素后面的那个内存位置的比较,
但是不与允许与指向第一个元素之前那个内存位置的指针进行比较。
如图,指针P1可以与指针P3进行比较,但不可与指针P2进行比较。
2.3 指针与数组
(1)数组地址与首元素地址
int arr [10] ;
arr为首元素的地址,和&arr[0]是等价的。而&arr为这个数组的地址。
区别在于:arr+1移动4个字节,&arr+1移动4*10个字节。
(2)数组指针与指针数组
int *parr[3] = { &a, &b, &c};
int (*arrp ) [3] = { 1, 2, 3}; 前者为指针数组,后者为数组指针
*parr[ 3 ]定义了一个数组元素为指针变量的数组;
(*arrp)[ 3 ]定义了一个指针,该指针指向数组{1,2,3}的首元素地址。
关键在于进入括号的 * 将会和变量名一起变成指针,指向数组的地址。
(3)指针与数组的复杂声明
int arr1 [ 5 ];
int arr2 [ 5 ];
int arr3 [ 5 ];
int (*parr[3] ) [5] = { &arr1, &arr2, &arr3 };
定义了一个长度为3的数组parr,里面是三个指针变量。
每个指针变量各指向一个长度为5的整型数组。
int *parr[10] = {......};
int * (*arrp ) [10] = &parr;
定义了一个指针arrp,指向长度为10的数组,且该数组的元素均为指针变量。
数组、指针复合变量赋值的关键在于看最内层括号里的数据是指针还是数组。
声明的是指针变量则赋地址;数组赋元素。