真的猛士, 敢于直面惨淡的人生, 敢于正视淋漓的鲜血. 这是怎样的哀痛者和幸福者? 然而造化又常常为庸人设计, 以时间的流驶, 来洗涤旧迹, 仅使留下淡红的血色和微漠的悲哀. 在这淡红的血色和微漠的悲哀中, 又给人暂得偷生, 维持着这似人非人的世界. 我不知道这样的世界何时是一个尽头! 我们还在这样的世上活着; 我也早觉得有写一点东西的必要了. -- 鲁迅
本文已经收录至我的 GitHub, 欢迎大家踊跃 star 和 issues.
https://github.com/midou-tech/articles
点关注, 不迷路!!!
看完我上一篇指针的讲解之后很多同学反馈很不错, 有网友给私信说之前在大学里面一直搞不懂指针的问题, 说到指针都是云里雾里, 老师讲的也是很难听懂 , 点击即可进入 指针 (上). 也有很多网友表示非常期待指针下的文章, 所以我就马不停蹄的继续写, 下 主要讲解指针的特性以及指针安全问题.
指针的特性
指针和常量
先说下什么是常量, 常量就是不可变的量, 一旦定义该常量, 其值在整个程序生命周期都是不可变的, 常量存放在虚拟地址空间的常量区.
在 C 语言里面有两种定义常量的方法.
使用 const 关键字 ,const 定义的是变量不是常量, 只是这个变量的值不允许改变是常变量, 带有类型. 编译运行的时候起作用存在类型检查.
使用 #define 预处理器, define 定义的是不带类型的常数, 只进行简单的字符替换. 在预编译的时候起作用, 不存在类型检查.
其实很多时候我们错误的以为常量就是 const 修饰的变量, 这个说法其实是有瑕疵的.
指针常量
很多网友在学习指针和指针的特性等问题上总是会绕进去, 其实不要绕进去最重要的一点是 要把握住核心本质.
本质上是一个常量, 指针用来说明常量的类型, 表示该常量是一个指针类型的常量. 在指针常量中, 指针自身的值是一个常量, 不可改变, 始终指向同一个地址. 在定义的同时必须初始化
- int num = 5;
- int *const p = # // p 为一个常量, 拥有常量的属性.
- *p = 70;
- int snum = 100;
- int *sp = &snum;
- p = sp;
聪明的你一定看出上面代码有个地方会报错, 是的 p 被我们声明为一个指针常量, 此时指针 p 具有了常量的属性, 其不能在改变指向, 但是其指向的值是可以改变的. 所以报错的代码是 p = sp 这句.
常量指针
常量指针本质上是一个指针, 常量表示指针指向的内容, 说明该指针指向一个 "常量". 在常量指针中, 指针指向的内容是不可改变的, 指针看起来好像指向了一个常量.
- int num = 5;
- int const *p = # // 常量指针
- const int *sp = # // 常量指针
- *p = 20;
- int snum = 100;
- p = &snum; // 改变指向
- sp = &snum;
是不是又发现上面的代码有一处报错, 你太聪明了, 基本搞懂了常量和指针的本质. 指针 p 和 sp 只是申明格式不同, 本质完全一样. p 被声明为一个指针, 指向一个常量. 换句话说就是一个常量的地址存放在指针 p 中. 此时报错的就是 * p = 20, 因为常量是不可变的.
到这里你基本掌握了常量和指针的关系, 其实还是很简单的, 也没大家在学校学的那么绕. 接下来给大家在介绍一个进阶的关系.
常量指针常量
本质上是一个常量, 该常量被一个常量指针指向. 也就是说一个常量指针里面放置一个常量的地址, 千万不要多看一眼这句话, 你会被绕进去.
- const int num = 5; // 一个不可变的常量
- const int * const p = # // 一个存放常量地址的常量指针
千万不要绕进去了, 其实认真理解了上面的指针常量和常量指针的问题, 这个问题看起来会简单很多, 就是一个常量, 和一个常量指针. num 是一个不可改变的常量, p 只一个指针, 该指针也是不可改变指向的.
指针和常量这个问题在面试中会被问到, 好好理解下, 同时有助于你更好的理解指针.
指针和函数
函数指针
什么是函数指针
如果在程序中定义了一个函数, 那么在编译时系统就会为这个函数代码分配一段存储空间, 这段存储空间的首地址称为这个函数的地址. 而且函数名表示的就是这个地址. 既然是地址我们就可以定义一个指针变量来存放, 这个指针变量就叫作函数指针变量, 简称函数指针.
函数指针的定义和普通指针不太一样. 函数返回值类型 (* 指针变量名) (函数参数列表);
1bool(*p)(char, int);
还是很简单的, 这就知道怎么定义一个函数指针变量了, 当然也有很复杂的函数指针变量, 面试的时候面试官可能会问一些变态的面试题, 比如:
- int (*(void (*)())0)();
- void (*signal(int , void(*)(int)))(int
不过还是那句话, 要把握核心本质, 函数指针的核心本质是: 函数返回值类型 (* 指针变量名) (函数参数列表);
函数指针使用
很多人会说, 搞这么难干嘛, 平时有使用么? 哈哈, 还真的经常用到, 尤其是标准库中用的那叫一个多, 比如 sort 中的比较函数就是一个函数指针.
指针作为函数参数
用指针变量作函数参数可以将函数外部的地址传递到函数内部, 使得在函数内部可以操作函数外部的数据, 并且这些数据不会随着函数的结束而被销毁.
这不得不使我想起一个经典案例, 大学老师一定会讲的, 而且当时也是很多同学一直半解的.
- void swap(int a,int b){
- int tmp = a;
- a = b;
- b = tmp;
- }
- int main(){
- int x = 10;
- int x = 20;
- printf("swap before:%d,%d",x,y);
- swap(a,b);
- printf("swap after:%d,%d",x,y);
- return 0;
- }
是不是历历在目...
- void swap(int *pa,int *pb){
- int tmp = *pa;
- *pa = *pb;
- *pb = tmp;
- }
- int main(){
- int x = 10;
- int x = 20;
- printf("swap before:%d,%d",x,y);
- swap(&a,&b);
- printf("swap after:%d,%d",x,y);
- return 0;
- }
- struct Person{
- string name;
- string addr;
- string number;
- int age;
- string hobby;
- ...
- };
- // 方案一
- int Fun(struct Person person){
- //TODO
- }
- // 方案二
- int Fun(struct Person *person){
- //TODO
- }
- char *strlong(char *str1, char *str2){
- if(strlen(str1) >= strlen(str2)){
- return str1;
- }else{
- return str2;
- }
- }
- void *malloc(size_t __size) __result_use_check __alloc_size(1);
- void *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
- int main(){
- int arr[10] = {1,2,3,4,5,6,7,8,9,10};
- int *p = arr;
- printf("value:%d,%d,%d,%d\n",p[0],p[-2],p[16],p[100]);
- }
- int main(){
- char c = 'a';
- int *p = (int *)&c;
- *p = 1314;
- printf("value:%d\n",*p);
- }
- int *p = (int *) malloc(sizeof(int));
- *p = 100;
- free(p);
- *p = 200;
- int *p = (int *) malloc(sizeof(int)*1000);
- *p = 100;
- free(p);
- p = NULL;
来源: https://www.cnblogs.com/zhonglongbo/p/12199365.html