c陷阱与缺陷 学习笔记

对于一些简单的1错误将不列举,这些应该是必须掌握的。

1.词法缺陷

编译器第一部分是词法分析器。这里进行2次划分标记(token)。一次划分的目的是为了宏扩展;二是将进行宏替换的程序划分标记(token)

错误1=不是==&|不是&&||

错误2:多字符记号

y=x/*p /*p指向除数*/ (错误)

良好的程序书写习惯也是避免这种错误的方法之一,可以写成y = x / p。在中间加几个空格来划分标记。或者写成y = x / (*p)

错误3:例外

组合赋值运算符实际上是2个标记

a + /**/ = 1 a += 1一样的。看起来是1个标记,实际上是2个标记,这是一个特例。

错误4:字符串和字符 ()

2.词法缺陷

理解c语言,仅了解构成他的标记是不够的。还要理解这些记号是如何构成声明、表达式、语句和程序的。

1.理解声明

声明组成:一个类型和一组具有特定格式的期望对该类型求值的表达式。最简单的表达式就是变量,如 float f, g;

如:float ff();说明ff()是一个float,应此ff()是一个返回一个float的函数。

float pf;说明pf是一个float,并且pf是一个float的指针

这些形式对与组合声明表达式是一样的。因此,

float g(), (*h)();表示*g()(*h)()都是float表达式,由于()*绑定更紧密,*g()*(g())是一样的。g是一个返回float指针的函数。h是一个指向返回float的函数的指针。

类型的模型:当我们知道如何声明一个给定类型的变量以后,就能够很容易的写出一个类型的模型(cast)。只要删除变量名和分号并将所有的东西包围在一对()中。

如:float *g()的模型就是(float *())

理解 (*(void (*)())0)()

第一步:这是一个函数,变量为指向函数的指针,把整个表达式样写成(*fp)();*fp就是函数本身,我们现在需要寻找一个合适的表达式来替换fp

第二步:本例中函数的地址就是0,可以将上面的(*fp)()写成(*0)(),但这样是不行的,因为*运算要求必须有一个可以指针作为他的操作数。另外,这个操作数还必须是一个指向函数的指针,以保证*的结果可以被真确的调用。应此我们需要将0转换为一个指向返回为void的函数的指针(void (*)())0。接下来我们用(void (*)())0来替换fp得到(*( void(*)())0();结尾的表示将这个表达式转换为一个语句。

我们可以使用typedef来简化这个过程:

typedef void (*funcp)();

(*(funcp)0)();

2)运算符号并不总是具有你所想象的优先级

避免错误的方法就是多多使用()

3.链接

一个c程序可以有多个部分组成,他们被编译,并通过链接器绑定到一起。

1)必须自己检查外部类型

如有一个c程序,被划分为2个文件。

其中一个文件声明了int n,另外一个文件声明了long n。这不是一个有效的c程序,因为一些外部名称在两个文件中被声明为不同的类型。这个错误在编译时是不能被发现的,因为编译程序只对单个文件编译。这些检查工作有链接器来完成。

这些规则只在使用全局变量时候起作用。

4.语义缺陷

1c并不总是转换实参

如:

double s;

s = sqrt(2);

printf(“%g/n”, s);

sqrt()需要一个double作为参数,但没有得到。返回一个double值但没有这样的声明。

double s;

s = sqrt(2.0);

printf(“%g/n”, s);

 

 

main() {

int i;

char c;

for(i = 0; i < 5; i++) {

        scanf("%d", &c);

        printf("%d", i);

}

printf("/n");

}

这里错误的原因是scanf去读取一个整数,他需要一个指向整数的指针,而这里是一个字符的指针,他将输入当成一个整数存储到那里。一个整数占用比字符更多的内存,这样可能影响到附近的内存。

2)指针不是数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值