一、指针补充
1. 定义多个指针变量
int*p;// 定义一个指针变量 pint*p,*q,*r;// 一次定义多个指针变量// * 是起修饰作用:修饰变量名,表示该变量是指针类型变量,而不是普通变量inta,b,c,*p,*q;// 普通变量和指针变量可以混合定义注意:定义时*是修饰(用来说明是指针变量),不是在取值!
2. 野指针
野指针:指针指向不明确。
- a. 指针变量未初始化——变量中可能是随机值 → 指针指向不明确(野指针)。
int*p;// 野指针!p 里是随机值,不知道指向哪里3. NULL —— 空地址(0号地址)
为了避免野指针,定义指针时先赋 NULL:
int*p=NULL;// 表示 p 的指向是明确的(空地址 0号地址)- NULL 就是 0 号地址(空地址);
- 判断指针是否有效:
if (p == NULL) { ... }。
4. void* —— 万能指针
void// 空类型void*// 空类型指针 —— 万能指针(可以接收任意类型的地址)inta=10;void*p=&a;// void* 可以指向任意类型5. & 与 * 互为逆运算
inta=10;int*p=&a;&*p// 等价于 &(*p) == p// & 与 * 互为逆运算:先解引用再取地址,等于 p 本身二、排序(指针版思路)
1. 选择排序
思想:给合适的位置选择合适的数。
// 迭代的方式实现// begin —— 起始位置(指针)// end —— 结束位置(指针)for()// 用地址代表元素的位置,条件 begin < end{for(p=begin+1;p<=end;){if()// p 位置上的值和 begin 位置上的值比较{swap();// 交换}}}voidselectArray(int*begin,int*end){int*p=NULL;for(begin;begin<=end;++begin){for(p=begin+1;p<=end;++p){if(*p<*begin){intt=*p;*p=*begin;*begin=t;}}}}2. 冒泡排序
思想:相邻两个元素两两相比,小的放前,大的放后。
for()// 控制趟数 —— 每一趟结束冒出一个数(最大值冒到最后){for(?)// 一趟的比较过程(从开头到结束位置){if()// 相邻比较{swap();// 交换}}}voidmp(int*begin,int*end){int*p=NULL;int*b=begin;int*e=end;for(begin;begin<e;++begin,--end){for(p=b;p<end;++p){if(*(p+1)<*p){intt=*(p+1);*(p+1)=*p;*p=t;}}}}3. 插入排序
思想:从无序区拿数据,插入到有序区。步骤:① 拿数 ② 找位置 ③ 插入。
for()// 通过指针拿数(begin 起始位置){// 1. t 拿数// 2. int *q 准备要插入的位置while(q>begin&&*(q-1)>t)// 前面的数比 t 大就往后挪{}*q=t;// 插入到正确位置}voidcr(int*begin,int*end){int*p=begin;int*q=NULL;for(p=begin;p<=end;++p){intt=*p;q=p;while(q>begin&&*(q-1)>t){*q=*(q-1);--q;}*q=t;}}三、查找:二分查找(指针版)
begin end// 0 1 2 3 4 5 6// b e// 1 2 3 4 5 6 8mid=begin+(end-begin)/2;// 中间位置if(*mid>n)// 要找的比中间小 → 去前半段{end=mid-1;}elseif(*mid<n)// 要找的比中间大 → 去后半段{begin=mid+1;}else// 找到了{}返回值:
mid(int*)—— 找到了,返回位置指针;NULL—— 空指针,没找到。
int*binaryFind(int*begin,int*end,intn){int*mid=NULL;int*ret=NULL;while(begin<=end){int*mid=begin+(end-begin)/2;if(*mid>n){end=mid-1;}elseif(*mid<n){begin=mid+1;}else{ret=mid;break;}}returnret;}四、快速排序
用到的知识:① 指针 ② 递归 ③ 迭代
思想:分而治之——选一个基准值,把比它小的放左边、比它大的放右边,然后对左右两部分递归。
voidquickSort(int*begin,int*end){int*p=begin;int*q=end;//1.找基准值首元素int*k=begin;if(begin>=end)//7.递归结束的条件,当begin与end错开或者相遇{return;}while(begin<end)//5.{//2.从右往左找到第一个比基准值小的while(begin<end&&*end>=*k){--end;}//3.从左往右找第一个比基准值大while(begin<end&&*begin<=*k){++begin;}//4.找到以后交换这两个值(2和3的)swap(begin,end);//5.重复2~4,直到begin与end相遇}//6.将相遇位置上的值与基准值交换swap(begin,k);//7.递归下去quickSort(p,end-1);//小的一部分quickSort(begin+1,q);//大的一部分}五、小结
- 指针操作整型一维数组;
- 指针很灵活:
- 指针可以代表位置;
- 指针也可以实现间接访问。
六、指针操作字符型一维数组
字符串本身也是按照字符型一维数组的方式存储到计算机中——字符串是一种特殊的字符型一维数组。
chars[]="hello";// 操作字符串指针操作 s 中的数据,只需要获得首元素地址:
s[0]// char 类型&s[0]// char *char*p=&s[0];// p 指向首元素=s;// 数组名就是首元素地址,等价写法七、const 修饰符
const:表示将所修饰的变量限定为只读。
修饰原则:就近原则——离谁近,就限定谁。
inta;// 普通变量,可读可写constinta;// a 此时只读,不能修改constchar*p;// 限定通过 *p 方式访问基类型为只读(*p 不能修改基类型)charconst*p;// 同上,等价写法char*constp;// 限定 p 本身为只读(p 不能被修改,不能换指向)constchar*constp;// p 不能被修改,*p 也不能被修改const 在函数参数中的应用
intputs(constchar*s);// const char *s —— 设计函数的一种方式puts 函数:功能是把 s 中的字符串输出。
建议:如果函数只是做读操作,参数建议写成 const 形式:
- 好处:避免出问题,出问题也能及早发现;
- 提高参数的适用性。
能传什么给const char *参数:
char*// 指针数组名// 如 s"hello"// 字符串常量constchar*// 常量指针八、表示字符串的两种方式
chars[]="hello";// 使用栈空间上的字符串(可修改)char*s="hello";// 使用字符串常量区中的字符串(不可修改)constchar*s="hello";// 更准确的写法:明确告知是只读的字符串常量九、strcpy 的 const 版本
char*strcpy(char*dest,char*src);// 普通写法char*strcpy(char*dest,constchar*src);// 改进:src 只读不修改原因:strcpy 只"读" src 的内容,不改 src,所以 src 用
const char *更安全。
补充总结
int *p,*q,*r;中*是修饰作用;- 野指针 = 指针指向不明确(未初始化);
NULL用来避免野指针; void *万能指针;&与*互为逆运算:&*p == p;- 三种排序(选择/冒泡/插入)、二分查找都可以用指针(begin/end/mid)实现;
- 快速排序 = 指针 + 递归 + 分而治之;
- const 就近原则;只读函数参数用
const char *; - 字符串两种表示:栈空间(可改)vs 字符串常量区(只读)。