浅谈指针(六)——陷阱与缺陷

1.陷阱

(1)词法

符号指的是程序的一个基本组成单元,其作用相当于一个句子中的单词。从某种意义上说,一个单词无论出现在哪个句子,它代表的意思都是一样的,是一个表义的基本单元。与此类似,符号就是程序中的一个基本信息单元。而组成符合的字符序列就不同,同一组字符序列在某个上下文环境中属于一个符号,而在另一个上下文环境中可能属于完全不同的另一个符号。

而编译器将程序分解为符号的方法是,从左到右一个一个字符地读入,如果该字符能够组合成一个符号,那么再读入下一个字符,这个处理策略被称为“贪心法”。需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空格符、制表符和换行符),例如下面的表达式:

a ---  b;
a -- - b;//同上
a - -- b;

y = x/*p;
y = x/ *p;
y = x/(*p);//同上

a =- 1;//a = a - 1;
a = -1;//a = -1;

这种准二义性问题会导致很多隐藏bug。对于整型常量,10和010的含义不同,010会被编译成8进制数字。对于字符和字符串,单引号和双引号含义不同,单引号代表一个整数,字符串代表一个指向无名数组的起始指针。因为用单引号括起来的一个字符代表一个整数,双引号代表一个指针。现在的编译器一般都能检测到单双引号混用。被双括号括起来的/*属于字符串中的一部分,而在注释中出现的双引号属于注释。

(2)语法

构造表达式其实只有一条简单的规则:按照使用的方式来声明。任何变量的声明都由两部分组成:类型以及一组类似表达式的声明符,声明符从表面上看与表达式类似,对它求值应该返回一个声明中给定的类型的结果。最简单的声明符就是单个变量,例如:

float f,g;//当对其求值时,表达式f和g的类型为浮点数类型(float),因为声明符与表达式的相似,所以我们也可以在声明符中任意使用括号
float ((f));((f))为浮点数类型,推之f也为浮点数类型
//同样的逻辑也适用于函数和指针类型的声明
float ff();//表达式ff()求值结果是一个浮点数,即ff()是一个返回浮点数的函数
float *fp;//*pf是一个浮点数,pf是一个指向浮点数的指针
float *g(),(*h)();//()结合性高于*,*g()也就是*(g()),g是一个函数,该函数的返回值类型为指向浮点数的指针,h是一个函数指针,h所指向函数的返回值为浮点类型

:一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了,只需要把声明中的变量名和声明末尾的分号去掉,再将剩余部分用一个括号整体封装起来即可,例如下面声明

float (*h)();//表示h是一个指向返回值为float的函数的指针
(float (*)())//表示指向返回值为float的函数的指针的类型转换符

那么让我们看一下下面的实例:

/*硬件将调用首地址为0位置的子例程*/
(*(void(*)())0)();
/*第一步,假定变量fp是一个函数指针,即(*fp)();,因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用函数的方式,ANSI C允许简写为fp()
第二步,因为*符号需要一个指针参与运算,必须对0进行类型转换,即指向返回值为void的函数的指针,即(void (*)()) 0 
第三步,代替后添加分号,即(*(void(*)())0)();
当引用typedef解决这个的时候
typedef void (*funcptr)();
(*(funcptr)0)();

又例如在signal库函数中:

void signfunc(int n)
{
 /*特定信号的部分*/
}

void (*sfp)(int);
//然后用signfunc替换
void (*signfunc(something))(int);
/*
此处的something代表signal的传参类型,
对signal函数返回值解除引用,然后传递一个整型参数调用解除引用后所得函数,最后返回值类型为void,因此,signal函数的返回值是一个指向返回值为void类型的函数的指针
而siganl函数接受两个参数:一个整型的信号编号,以及一个指向用户定义的信号处理函数的指针,这是将sfp去掉,即void(*)(int),此外,signal函数的返回值是一个指向调用前用的用户定义信号处理函数的指针,这个指针与sfp指针类型一致,因此,可声明void(*signal(int,void(*)(int)))(int);
*/
/*使用typedef简化后*/
typedef void (*HANDLER)(int);
HANDLER signal(int,HANDLER);

对于优先级,优先级的最高者并不是真正意义上的运算符,包括数组下标和函数调用操作符各结构成员选择操作符。它们都是自左向右结合。单目运算符的优先级仅次于前述操作符,在所有操作符中,它们的优先级最高,因为函数调用的优先级高于单目运算符优先级,所以如果p是一个函数指针,应该写成(*p)()而不是*p(),类型转换也是单目运算符,其优先级与其他一样,都是自右向左结合,双目运算符的优先级比单目低,其中算术运算最高,下来位移,最后关系,接着逻辑,最后赋值,下来就是唯一的三目运算符。关系中等于判断低于比较。逗号运算最低。

概括一下就是:声明先从它的名字开始读取,然后按照优先级顺序一次读取。优先级从高到低是:先声明中括号括起来的那部分,下来是后缀操作符,例如括号()表示一个函数,方括号表示一个数组,下来前缀优先级,即星号*表示指向指针,最后如果const或volatile关键字紧跟着类型说明符,那么它作用于类型说明符,在其他情况下作用于左边紧邻的const。

(3)语义

声明指针实例化后,malloc函数可能会无法提供请求的内存,其返回一个空指针。分配的内存在使用之后应该及时释放,malloc应该分配足够的内存。

有符号整数与有符号整数会溢出,产生不可知的结果。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值