C语言进阶:C陷阱与缺陷(读书笔记二)

继续分享读书笔记,精简笔记可做提醒,此部分读完5min。

三、语义陷阱

1、 指针与数组

1)C语言只有一维数组,但是数组内可以放任何类型的对象,当然也可以放进去一个数组。
2)对于数组我们只能做两件事:确定数组的大小,以及获得指向该数组下标为0的元素的指针,其他的有关数组的操作,实际上都是通过指针进行的。
理解数组声明:

     int a[3];
     int *p;
     struct {
           int p[4];
           double  x;
}b[17];
 int cal[12][31];   cal[4]含义是什么?    p=cal[4];   p的含义是什么?

int *p ; p = cal; 非法
int (*p)[31]; p = cal; 合法
int *p ; int i; p=&i; 合法
int *p ; int i[3] p= i; 合法
int *p ; int i[3] p=& i; 在ANSI C中不合法

2、 非数组的指针

C语言以空字符(’\0‘)作为字符串常量的结束标志,将字符串s和r连接成一个字符串,标准操作为char *r ;

r =malloc(strlen(s)+strlen(t)+1);
if(!r)
{
 exit(1);
}
strcpy(r,s);
strcat(r,t);
free(r);

3、 作为参数的数组声明

char hello[]=”hello“;

传参 hello 等价于 &hello[0]
将数组作为参数传参时
函数strlen(char s[ ])等价于strlen(char *s)
但作为定义声明char s[ ] 与 char *s 截然不同。

4、 避免”举隅法“

char *p,*q;
p=”xyz”;
q=p;
q[1] = ?  q[1] = ‘y’; 

不要混淆指针与指针指向的数据,复制指针时数据并不复制。

5、 空指针并非空字符串

将0赋值给指针变量时,绝对不能使用该指针指向的内存中存储的内容。
if(p==(char ) 0) 合法
if(strcmp(p,(char
)0)==0) 非法

6、 边界计算与不对称性

注意数组下标与数组大小。

7、 求值顺序

|| 首先对左侧操作数求值,若为1,则不对右侧求值,若为0,则对右侧求值。
&& 对左求值,若为0,则不对右侧执行,若为1,则对右侧求值。
a ? b:c 三目运算符,若a为真,则执行b,若为假,则执行C。

8、 运算符&&、|| 和 !

按位运算符&、|、~位运算
逻辑运算符&&、||、!逻辑运算

9、 整数溢出

两个无符号整数运算不存在溢出,一个有符号整数,一个无符号整数运算,将有符号整数转换为无符号整数,也不会发生溢出,只有两个有符号整数运算时才有可能溢出,且溢出的结果是未知的。
例如去检查溢出:if(a+b <0)
complain( );
如果a+b发生溢出,那么if的检查就会失败,因为加法运算将设置一个内部寄存器的四个状态为:正、负、零、溢出。
正确的方式是将a与b都要转换为无符号整数,INT_MAX代表可能的最大整数值

if((unsigned) a+(unsigned)b >INT_MAX)
complain( );

10、 为函数main提供返回值

main 函数执行完后需要增加返回值。

四、 连接

1、 什么是连接器

C语言中的重要思想是分别编译,若干个源程序可以在不同的时候单独进行编译,连接器将若干个C源程序合并为一个整体。该整体能够被操作系统直接执行。如果C语言实现中提供了lint程序,一定要使用,可以检测出很多错误。

2、声明与定义

int a; 如果未初始化,编辑器应该默认初始化为0,(有些编辑器不能保证)
extern int a;外部引用别的地方的定义的int a;
如果在不同的源文件中定义了同一个变量,并各指定一个初始值,例如一个文件中指定int a=7,另一个文件中定义int a =9,这个时候外部引用时,大多数系统会拒绝接受该程序,如果有多个定义但未初始化,一些系统会接受。最好的解决办法是,每个外部变量只定义一次。

3、命名冲突与static修饰符

1)避免与库函数命名冲突
2)static可以将变量的作用域限制在一个源文件中,以减少命名冲突。,同样 static可以修饰函数,效果一样。

4、形参、实参与返回值

函数的形参有无都行,如果函数的形参列表为空,那么被调用时实参列表也为空。
任何C函数都有返回值,任何函数在调用它的每个文件中,都在第一次被调用之前进行声明或定义,就不会有任何与返回类型相关的麻烦。
要注意形参与实参数据类型的一致。

5、 检查外部类型

如果一个源文件中声明 extern int n; 另一个源文件中却是 long n;这样会导致什么问题?
1)如果编译器足够强大,可以检测出来这种错误。
2)如果是32位计算机,可能能正常工作,但是这种写法绝对是错误的
3)虽然两个实例的存储空间大小不同,但是却共享存储空间的方式能够满足,赋给其中一个的值,对另一个也有效,错误的程序因为某个巧合可以工作。
4)两个变量n共享存储空间的方式,对其中一个赋值,等同于给另一个赋了完全不同的值,程序不能正常工作。

6、 头文件

为了避免上面的错误,我们遵循一个简单的规则:每个外部对象只在一个地方声明。这个地方就是在一个头文件中,定义该外部对象的模块也应该包括这个头文件。例如:
在file.h中包含声明 extern char filename[ ];
如果其他外部c源文件中需要用到filename时,只需要在C文件中添加
#include “file.h”
C陷阱与缺陷(读书笔记一)
C陷阱与缺陷(读书笔记三)
C陷阱与缺陷(读书笔记四)
C陷阱与缺陷(读书笔记总)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式小师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值