《你必须知道的的495个C语言问题》阅读笔记-(第4章)指针篇
指针是C的精华,也是初学者的梦魇
4.1
FAQ:指针的好处有哪些?
Answer:(1)可以动态分配数组;
(2)可以对多个相似变量一般访问;
(3)按照引用传递函数参数;
(4)适合各种动态分配的数据结构,尤其是树和链表;
(5)指针方式遍历数组,比如解析字符串;
(6)高效,按照引用 “复制”数组和结构,尤其在作为函数参数的时候。
(7).etc
4.2
FAQ:想通过以下代码声明一个指针并且为它分配一些空间,但是不行,为什么?
char *p;
*p=malloc(10);
Answer:代码本意声明的指针是 p;类型是 char * ,而不是 *p,当操作指针本身时,比如对指针赋值,或者修改指向时,只需要使用指针的名字即可;
p=malloc(10);
当需要访问指针指向的内存时,才需要 *作为间接操作符:
*p='A';
然而,如果在定义的时候初始化该指针可能会容易犯以上错误比如
char *p=malloc(10);
所以分开写的时候应该注意;malloc时去掉*;
题外话:如果C++中还是有点差别的,malloc需要显示的强制类型转换
char *p;
p=(char *)malloc(10);
当然使用完成后需要free释放;
free(p);
不要写成free(*p)哦;
指针指向的空间被free后,没有被重置为NULL,或者指向一个有效的内存区域、
在free后要及时处理被free的指针,例如令其指向NULL。
野指针该如何避免?
在指针声明时,要养成将其初始化的习惯。如下:
int *p = NULL;
默认将其初始化为NULL
在free后面,加上重置指针的代码,如下:
free(p);
p = NULL;
重置为NULL
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc,char *argv[]){char *p = NULL; //初始化为NULLp = (char *)malloc(100);if(p==NULL){return 0;}strcpy(p,"faq"); //向内存拷贝值printf("p : %s\n",p); //打印值if(p!=NULL){free(p);p = NULL; //在释放完后,再将指针的值置为空}//free释放的是指针所指向的内存空间,而不是指针的值。//所以释放后指针还是指向原来的地址if(p1!=NULL){free(p1); //如果再次释放,就会出现野指针错误。}getchar();return 0;}
4.3
FAQ:*p++是自增p还是p所指向的变量:
Answer:“*和++”两者优先级相同,(
primer上说++优先级高,但我网上查了下,好多说二者优先级一样,从右向左。
这两种解释的结果是一样的。哪个是对的?
* 有两个意思,理论上
作为 乘法 运算符,级别低于 ++(自增)。
作为 指针取值 运算符,级别同 ++(自增)一样。
这里不仅仅是优先级的问题!
)需要按从右到左的顺序执行。;所以 *p++ 等价*(p++),所以结果是自增p返回增加前所指向的值;
如果需要增加指向的变量++ 那就使用(*p)++;如果副作用的顺序无关紧要也可以 ++*p,等价于 ++(*p);返回++后的值,
关于 ++ 和--在前在后的影响:谁在前先返回哪个!
ref.:https://jingyan.baidu.com/article/eae07827809adf1fec54851f.html
*++p
//相当于*(++p),指针p先后移,如果是数组,则指向下一个元素,然后++p返回指针下移后的指针。然后*取下移指针后所指向的值.
4.4 指针操作
FAQ:我用指针操作int数组的时候遇到了麻烦,下边的代码有什么问题?
int arry[5],i,*ip;
for(i=0;i<5;i++)
arry[i]=i;
ip =arry;
printf("%d\r\n",*(ip+3*sizeof(int)));
我以为最后会打印3 ,但是打印了一堆垃圾;
Answer:这样打印不对,做了一些无用功,c语言当中的指针算术运算是自动采纳它所指向的对象对象的大小;
printf("%d\r\n",*(ip+3));这样打印数组第3个数据,这样的运算不用考虑指针指向的元素的大小。
4.5 FAQ:我有一个char *类型的指针碰巧指向了一个int 型变量,我想跳过他们,((int*)p)++;这样的代码不行吗?
Ansswe:在C语言中,类型转换操作符并不意味着“把这些而二进制位看作另一种类型,并作相应处理”,这是一个转换操作符,根据定义它只能生成一个右值,(rvalue)不能进行赋值或者自增操作,所以上述写法是不合法的,(编译器如果过了,要么错误,要么进行了非标准拓展)。
如果达到目的可以尝试下边操作
p=(char *)((int*)p+1);
或者因为 p是char *;
p+=sizeof(int);
明白操作的话下边可能是合适的;
int *ip=(int*)p;//先强制把p 是 int* ;
p=(char *)(ip+1);//操作之后转回来char*;
为了不李代桃僵,实际使用最好还是同类型操作;
4.6 为什么不能对void*进行指针进行算术操作?(参考11.26)
4.7 关于解析外部结构的代码,但是却崩溃了,显示出 unsigned access(未对齐的访问)的信息,这是什么意思?
作为函数参数的指针
4.8FAQ:我有个函数,它应该接受并初始化一个指针;
void f(int *p)
{
static int dummy=5;
p=&dummy;
}
但是当我如下调用时,
int *ip;
f(ip);
调用者的指针没有任何变化。
Answer:你确定函数初始化的是你希望它初始化的东西吗?C语言中参数是通过值传递的,上述代码中被调用函数仅仅修改传入的指针副本,薇乐大道期望的效果你需要传入指针的地址(函数需要变成接受指向指针的指针)
void f(int **p)
{
static int dummy=5;
*p=&dummy;
}
。。。。
调用时
int *ip;
f(&ip);
这里实际是在模拟通过引用传递参数,另一种办法就是函数返回指针:
int * f()
{
static int dummy=5;//考虑一下为什么这里是static ,可以阅读博客《C语言深度剖析内存管理》学习笔记;
return &dummy;
}
。。。
int *p=f();
4.9 FAQ:能否像下边这样用 void **通用指针作为参数,使函数模拟按照引用传递参数?
void f(void **);
double *dp;
f((void**)&dp);
Answer:这样的代码不可移植,但是可能是有效的,有时候也会鼓励这样使用,但是它依赖一种假设:所有的内部表示都是一样的(这很常见,但并不一定如此)
C语言中没有通用的指针类型。void *可以作为通用指针的原因:当它和其他类型相互赋值的时候,如果需要,他可以自动转换为其他类型。但是如果试图这样转换所指类型为 void *之外的类型的 void ** 指针时,就不会自动转换了。当使用 void **指针的时候 (例如:使用*操作符访问 void **指针所指向的 void*的值的时候),编译器无法知道void * 的值是否是其他类型指针转换而来的,因此,编译器只能认为它仅仅是个void *类型的指针,不能对它进行任何隐式转换。换而言之,你使用的任何 void **的值必须的确是某个位置的void* 值的地址。(void **)&dp这样的类型编译器虽然可以接收,但是不能移植,而且也不会达到你要的目的,如果void**指针指向的值不是void*类型,而且他的大小或内部大小也和 void*不相同,则编译器就不能正确的访问它。
如果要使上边的代码正确工作,你需要使用一个中间的void*变量;
double *dp;
void *vp=dp;
f(&vp);
dp =vp;
类似的
void incme(double *p)
{
*p+=1;
}
可以这样做
int i=1;
double d=i;
incme(&d);
i=d;
但是如果像以下这样:
int i=1;
incme((double*)&i);这个代码就不对了;
4.10FAQ:我有一个函数extern int f(int*);它接收指向int型的指针。我怎样用引用方式传入一个常量?调用f(&5),视乎不行。
Answer:C99中可以使用“复合字面量”:
f((int[]){5});
C99之前是不能这样做的,必须先定义一个临时变量,然后取地址传给函数
int five =5;
f(&five);
C语言中接收指针而不是值的函数可能往往是希望改变指针指向的值,因此传入一个常数指针可能不是一个好主意。事实上,如果f被定义成接收 int*,则向他传入
const int 的指针的时候是需要被诊断的,(如果函数能保证不修改传入指针指向的值,则它可以被定义成接收 const int *参数)。
4.11FAQ: C语言可以”按照引用传参”吗?
Answer:C是不支持的,C++是可以的。C编译器本质只是在模拟按照引用传参。另一方面,类似函数的宏可以提供一种“名称传参”的形式。
其他指针问题(函数指针)
4.12FAQ:我看到了指针调用函数的不同语法形式,这是什么情况?
Answer:最初,函数指针操作必须*操作符和(一对括号)转换为一个”真正的“,函数才能调用。
int r,(*fp)(),func();
fp=func;
r=(*fp)();
fp是个函数指针 *fp是个函数,括号内加上函数参数,在*fp外加上括号为了优先级正确,就完成了一个完整函数调用;,而函数总数通过指针进行调用的,所有 “真正的”函数名在表达式和初始化中总是隐式的退化为指针,所以无论 fp是函数名还是函数的指针 r=fp();都是合法的,(函数指针后跟参数列表的方法,除了调用它所指的函数外,其他什么也做不了,没有歧义)使用显示*号调用被允许是因为为了保证老版本编译器移植编译,也是推荐的用法。
4.13FAQ:通用指针类型是什么?当我把函数指针赋值给 void * 的时候,编译不过?
Answer:没有什么通用指针类型,void*指针只能保存对象(也就是数据)指针,将函数指针转换为void*的指针是不可移植的,(某些机器上,函数指针可能很大,比任何一个数据类型指针都大),
但是可以确保的是,所有函数指针类型都可以互相转化,只要在调用前转回了正确类型就行。因此,可以使用任何函数类型 (通常是 int (*)()或者 void (*)(),返回int或者void 函数),作为通用函数指针。
如果你需要一个既能容纳数据指针又能容纳函数指针的地方,可移植的方案是,void * 和 通用函数指针的组合,比如任务控制块结构。
未完待续;后续会把运行截图上传完善。
如有错误欢迎留言指正;
《你必须知道的的495个C语言问题》阅读笔记-(第4章)指针篇相关推荐
- 《CUDA高性能并行计算》----2.2 需要知道的CUDA API和C语言拓展
本 节 书 摘 来 自 华 章 出 版 社 <CUDA高性能并行计算> 一 书 中 的 第2章,第2.2节, 作 者 CUDA for Engineers: An Introduction ...
- c语言中变量可以用x1表示没,你必须知道的495个C语言问题 读书笔记
1.数值类型的选择:溢出特征重要而负值不重要,操作二进制位时避免符号扩展的问题,应该使用unsigned(无符号值) char 8位 -127~127 最大值255 short int 和 int 均 ...
- 《抓住听众心理——演讲者要知道的100件事》一20.人们学习的最优长度是20分钟...
本节书摘来异步社区<抓住听众心理--演讲者要知道的100件事>一书中的第1章,第20节,作者: [美]Susan M. Weinschenk 译者: 杨妩霞 , 杨煜泳 责编: 赵轩,更多 ...
- 《抓住听众心理——演讲者要知道的100件事》一2.听众需要上下文
本节书摘来异步社区<抓住听众心理--演讲者要知道的100件事>一书中的第1章,第1.2节,作者: [美]Susan M. Weinschenk 译者: 杨妩霞 , 杨煜泳 责编: 赵轩,更 ...
- 《抓住听众心理——演讲者要知道的100件事》一第 1 章 人们是怎样思考和学习的...
本节书摘来异步社区<抓住听众心理--演讲者要知道的100件事>一书中的第1章,第1.1节,作者: [美]Susan M. Weinschenk 译者: 杨妩霞 , 杨煜泳 责编: 赵轩,更 ...
- 项目经理应该知道的97件事
项目经理应该知道的97件事 基本信息 原书名: 97 Things Every Project Manager Should Know 原出版社: O'Reilly Media 作者: (美)Barb ...
- 你应该知道的25个非常有用的CSS技巧
在我们的前端CSS编码当中,经常要设置特殊的字体效果,边框圆角等等,还要考虑兼容性的问题, CSS网页布局,说难,其实很简单.说它容易,往往有很多问题困扰着新手,在中介绍了非常多的技巧,这些小技巧与知 ...
- 关于机器学习,你应该知道的3个热门专业术语
https://www.toutiao.com/a6683842829510246923/ 2019-04-25 22:43:48 关于机器学习,你应该知道的3个热门专业术语 原创: 吴郦军.罗人千 ...
- 有哪些事情是你成为程序员之后才知道的?
来源 | 三太子敖丙(ID:JavaAudition) 昨天我教练问我:"有哪些事情是你成为程序员之后才知道的."我就写下来了. 身穿一件微微起球的格子衫,背着工整的双肩包,头发乱 ...
最新文章
- 关于双目立体视觉的三大基本算法及发展现状的总结
- Wix学习整理(7)——在开始菜单中为HelloWorld添加卸载快捷方式
- 嵌入式程序员应知道的0x10个基本问题
- 比特币现金扬声器系列II将领先的比特币一起带来
- 九度 1550 分糖果
- 前端需要了解的 Cookies 和 WebStorage
- Nginx 访问日志轮询切割
- Python爬虫实战之解密HTML
- linux防火墙之牛刀小试
- 七牛云主机全线升级,「两大资源池」实现多云容灾
- 一文读懂机器学习(转)
- 学前端的记录帖html+css b站千峰19版
- android 行政区域,最新Android使用Jsoup获取省市县行政区划代码行政编码(附源码与Json数据)...
- itchat 运行记录
- 域策略(4)——设置统一锁屏壁纸(此策略仅适用于企业版、教育版和 Server SKU版)
- Python 自动化开发【初级】到精通-张子夜-专题视频课程
- php中文拼音模糊,两种php中文字符转拼音问题解决方法
- Python基础Pro | (17) 电子邮件
- 从零开始构建一个高可靠的RabbitMQ镜像集群
- 九宫怎么排列和使用_剪映零基础入门教程第三十七篇:一学就会系列之九宫格小程序配音...