一. 指针与地址
1. 指针简介
定义:指针是用来访问内存的,相当于房间的房间号便于查询,提高效率
内存单元的编号==地址==指针
大小:4/8个字节,只要是指针变量就是,和类型无关
x86(32位机器,32根总线||二进制,32个比特位) 所以需要4个字节
x64 8个字节
常见单位:
int/float==4个字节
short==2个字节
char==1个字节
一个字节==八个比特
引言:编址的理解
可以理解为CpU传地址给内存获得信息,再通过数据总线传回CPU
2. 指针变量
取地址&
及Pa向内存申请一块空间来储存a的地址
int类型是四个字节,&a会输出四个中较小的字节,可通过%p来打印
定义指针变量与解引用 *
因为都用到了*这个符号,所以对比讲解一下
定义:int * p=&a,
其中用到的*为定义类型,代表P为指针变量的标志,
int是所取的a的数据类型为int类型
前面提到了指针大小和类型无关,那么为什么要定义指针类型呢?
1.决定了解引用可以访问几个字节,例如int可以访问4个,而char解引用后只能访问1个
2.决定了指针变量+或-1 一次所走的距离
解引用(后才可赋值):* p=0,
就是对所取出的a的地址进行了解引用,就可以对a重新赋值了
触发连招:* & a=0 取出a的地址再解引用
void* 指针
无具体类型的指针
作用:
优点:可以接收不同类型的地址,使用在函数参数的一部分
缺点:无法直接进行指针运算
const 修饰指针
const *p 内容不可改 //可以将这个*p理解为解引用之后的内容了,然后const放在其之前了
* const p 对象//p指向的地址 不可改
指针运算
指针+-整数 如:p+n——跳过n*sizeof(type)//跳过n个指针类型
指针-指针 可以模拟实现strlen
让p获得s首地址后,自加直到遇见\0,用p-首地址得到长度
指针关系运算 实现字符串输出 如p<arr+sz的话p自加
3. 野指针
成因:
1.指针未初始化 2.指针越界访问 3.指针指向的空间释放
(到arr[11]了,越界
p得不到100,test可以,离开函数无法访问,还回去了
避免NULL:
1.指针初始化 int* p=NULL (值是0,0也是地址)
2.注意不能超出访问申请的空间
野指针像野狗一样,NULL像树可以把它拴起来,运用了if
3.不要返回局部变量
assert断言
运用:assert(p!=NULL)
好处:1.为假返回零,会报错文件名和行号
2.可取消 在#include<assert.h>前面定义一个宏 #define NDEBUG
(在release版本中会自动优化掉)
4. 传值和传址调用
strlen的模拟实现
用const使内容不可改
无符号整型可变为size_t %zd
传值调用:
调试观察如下代码:
这是一种传值调用,a,b无法实现交换效果
因为可以发现a,b和x,y地址不一样,x,y又是两个相对独立的空间了
实参a,b只能将数值传给形参
所以实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,对形参的修改不影响实参
这就需要应用传址调用了,可以让函数和主函数之间建立真正联系
在函数中定义参数为指针类型,取a,b地址传入,交换解引用的值
二. 数组与地址
1. 数组名的理解
数组名是首元素的地址&a[0],除了
1.sizeof(数组名) 代表整个数组
2.&数组名 整个数组的地址
&arr+1跳过的是整个数组
2.数组与指针辨析
1.数组就是数组,是一块连续的空间(大小和1.个数2.类型都有关)
2.指针变量就是一个变量(4/8个字节)
3.数组名是地址,首元素地址
4.可用指针来访问数组
int* p=arr *(p+i)==*(&arr[0]+i)
扬科维奇 i[arr]==arr[i]
[ ]==*的含义
一维数组传参的本质:传的为首元素的地址
一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式(首元素地址)。
3.冒泡排序
核心:一趟搞定一个,两两相邻比较
设立flag判断函数,实现优化
4.二级指针
指针变量也是变量,是变量就有地址,存放指针的地址--二级指针
int* * ppa=&pa
指向int*类, 最后面的* 指针变量的标志
解引用方框内所存放的地址,就能得到该地址内的内容
5. 指针 [数组]
好-孩子,也是个孩子,指针-数组,是个数组,看后缀
数组的每个元素都是指针类型
指针数组模拟二维数组
parr[i][j]==*(*(arr+i)+j)
三. 深入指针
1.字符指针变量
%s字符串不用*,给个首地址及可自动往后
和数组引用的区别:直接用指针的常量字符串不可修改
本质:把字符串首字符放到pstr中了,但会自动往后读字符串
常量字符串是不可被修改的,相同的%s没必要存2份
2.数组(指针)变量
如果要存放整个数组的地址,就得存放在数组指针变量中
int (*p)[10] = &arr 指针要括起来
p==&arr完全一致
二维数组传参的本质
本质:是传递了地址,传递的是第一行这个一维数组的地址
二维数组传参,形参的部分可以写成数组,也可以指针,但都要带列
加1来使数组指针,跳过整个指向的数组,来到下一行
3. 函数指针变量
函数名带不带&,都是函数的地址
数组和函数指针都要(指针)
4. 函数指针数组
转移表
四. 深入函数指针
1. 回调函数
定义:把函数A的指针作为参数传递给另一函数B—实现B(A),A是被调用函数,就是回调函数
模拟实现计算器
2. qsort函数
排序整型数据
void qsort(void* base,//指针,指向待排序数组首元素
size_t num//数组元素个数
size_t size//数组元素大小
int (* cmp)( const void*,const void *)//函数指针--进行两两相邻的比较函数
排列结构数据
注释:e1是一个指针,而不是一个结构体本身。因此,如果要访问结构体中的成员,需要使用箭头运算符'->',以此来得到该元素
3. 用冒泡模拟实现qsort
通用的实现:
通过 void* 来接收各种数据类型,局部变量中通过 强制类型转化 实现比较等操作
代码如下:
补充:
1. 如何输入未知长度字符串
char str(10000);
gets(str);
len=strl指针en #stdlib
//如果看完感觉还不错,感谢点赞支持一下啦,欢迎留言 互相关注交流~