C学习记录6——指针
一、计算机存储简介
1.内存
- 存储器:计算机组成中用来存储程序、数据,辅助CPU进行运算处理的重要部分
- 内存:内部存储器,暂存程序/数据,断电丢失。一些内存:SRAM、DRAM、DDR、DDR4
- 外存:外部存储器,长时间保存程序/数据。一些外存:ROM(只读存储器)、ERRROM、FLASH(快存)、硬盘、光盘
内存是沟通CPU和硬盘的桥梁(暂存CPU中运算数据/与外存交换的数据)
2.物理存储器和存储地址空间
- 物理存储器:实际存在的具体存储芯片,如:主板上的内存条、显示卡上的显示RAM芯片、适配卡上的RAM/ROM芯片
- 存储地址空间:对存储器编码的范围
- 编码:对每个物理存储单元(一个字节)分配一个号码(32位或64位,与处理器有关)
- 寻址:根据分配号码找到相应的存储单元,完成数据读写
3.内存地址
将内存抽象位为一个很大的一维数组,编码的内存编号称为内存地址
PS:Windows电脑数据存储时小端对齐
如:0xaabbccdd
二、指针基础
1.指针与指针变量
指针是一种数据类型,指针变量指向谁,谁就将其地址赋给指针变量
定义什么类型的变量,就用什么类型的指针
int a = 10;
int* p = &a;
a = 100; //直接改变变量值
*p = 100; //*:解引用。通过指针间接改变变量a的值
&取地址,升维度;*取值,降维度。
2.指针大小
所有指针类型存储的都是一个无符号的十六进制整形数,在32位(x86)下大小为4字节,64位(x64)下大小为8
3.野指针和空指针
- 任意数值赋给指针变量毫无意义,这样的指针称为野指针,指向区域未知,不会直接引发错误,操作野指针指向的内存空间(*p)才出现问题
- 操作系统将0~255作为系统占用不允许访问(读写)操作,指向此处操作不报错(VS2019报错……)
- 为标志此指针变量未指向任何变量(空闲可用),C中可将NULL赋值给此指针,标志其为空指针。(NULL是一个值为0的宏常量)内存地址编号为0的空间,操作空指针对应的空间必报错
- 空指针可用于条件判断
4.万能指针viod*
可指向任意的内存空间,可接收任意内存地址
在通过万能指针修改变量的值时,需要找到变量对应的指针类型
int a = 10;
void *p = &a;
*((int *)p) = 11; //使用指向的内存时转换为int*
5.const修饰的指针变量
const常量可以用指针间接改值
- const修饰指针类型(
const int* p = &a;
常量指针)可修改指针变量的指向(p的值),不可修改指向内存空间的值(*p的值) - const修饰指针变量(
int* const p = &a;
指针常量)不可修改指针变量的指向(p的值),可修改*p的值(只读指针)
const离谁近,谁的值不变
- const同时修饰指针类型,指针变量,p、p*都不可改
const一级指针可以用二级指针改值
int** p1 = &p;
*p1 = p = &a;
**p1 = a;
三、指针和数组
1.数组名
数组名是数组的首元素地址,但它是一个常量
2.指针操作数组元素
int arr[]={};
int* p = arr;
arr[i]与p[i]相同(地址+下标偏移量(允许负数))
arr[i]下标从0(原位置)偏移i,与*(arr+i)、*(p+i)相同
指针类型变量+1等同与内存地址+sizeof(类型)
区别:
- p为常量,arr为变量
- p是指针,4字节大小,arr为数组(4*元素个数)字节大小
3.指针加减法运算
- 加法、减法
int*+1结果是增加一个int的大小,指向数组下一元素 - 指针相加减
两指针相加,得野指针,报错
两指针相减,得两指针的偏移量(步长)=数值差/sizeof(int)(所有指针类型,相减结果都是int型) - 其它运算
指针不允许乘除取余,允许比较,逻辑判断(&&……),赋值
4.指针数组
数组中每个元素都是指针类型
int a = 10,b=20,c=30;
int * arr[3] = {&a,&b,&c};
指针数组是一个特殊的二维数组模型
int a[]={1,2},b[]={3,4};
int* arr[] = {a,b};
printf("%d\n",arr[0][1]);输出2 //arr[0]=a=&a[0]
//arr[i][j] = *(arr[i]+j) = *(*(arr+i)+j))
指针数组对应二级指针
四、多级指针
二级指针指向一级指针变量地址
int a[]={1,2},b[]={3,4};
int* arr[] = {a,b};
int** p = arr;
//二级指针加偏移量相当于调过了一个一维数组大小(1行)
printf("%d\n ",**(p+1));输出3
//一级指针加偏移量,相当于跳过一个元素
printf("%d\n ",*(*p+1));输出2
五、指针和函数
参数中形参改为指针,可减少内存空间占用,不会复制新的副本
1.值传递
形参不影响实参
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
swap(a,b);
printf("%d\n",a);
return 0;
}
输出10
2.地址传递
形参可改变实参
void swap(int* a,int* b)
{
int temp = *a;
*a = b;
*b = temp;
}
int main()
{
int a = 10;
int b = 20;
swap(&a,&b);
printf("%d\n",a);
return 0;
}
输出20
PS:数组名做函数参数,函数会退化为指针
六、指针和字符串
1.字符指针
(1)char* p = "hello world";
可执行,但不可用p[i] = 'x’修改(数据区常量区字符串)
char ch[] = "hello world";
栈区字符串
若再写char* p1 = "hello world";
仍与(1)指向同一地址
2.指针数组做为main函数形参
int main(int argc,char* argv[])
argc:命令行参数个数,程序名本身算一个参数
argv:命令行参数的字符串数组,指向输入参数