C陷阱和缺陷

本文详细阐述了C语言编程中的一些常见陷阱,包括运算符优先级、悬挂else问题、数组与指针的关系、整数溢出、声明与定义的差异,以及预处理中的宏定义问题。强调了理解这些细节对于编写正确、高效的C程序至关重要。
摘要由CSDN通过智能技术生成

2. 语法陷阱

2.1 运算符优先级

在这里插入图片描述
看上去琳琅满目,但有规律可循:

  1. 优先级最高为括号、后缀、下标这一类
  2. 单目运算符 的优先级仅次于上述, 高于双目运算符。
  3. 双目运算符:算数运算符 ->移位运算符 ->关系运算符 ->逻辑运算符 ->赋值运算符 ->条件运算符
  4. 单目运算符、赋值运算符和三目运算符的结合顺序是自右向左,其余都是自左向右

举例:

  1. *p++会是*(p++) :自右向左结合
  2. while(c=get(in) != EOF)有错,赋值运算符优先级低,结合有错,应为 while((c=get(in)) != EOF)

2.6 悬挂else引发的问题

if(x == 0)
	if( y== 0)
		error();
else
{
	z = x + y;
	f(&z);
}

其实际含义为:

if(x == 0)
{
	if( y== 0)
		error();
	else
	{
		z = x + y;
		f(&z);
	}
}

3. 语义陷阱

  • 对于数组a,除了被用作sizeof的参数这一情形,在其他所有场景中数组名a都代表指向数组a中下标为0的元素的指针。
  • sizeof(a)得到的是整个数组的大小而不是指向数组a的元素的指针的大小。

对于二维数组 int calendar[12][31]

  • calendar[4] : int*指针,指向数组calendar[4]中下标为0的元素
  • *(calendar[4] + 7)
  • *(*(calendar+4) + 7)
  • calendar : int(*p)[31]类型—指向一个拥有31个整形元素数组的指针;
  • int *day = &calendar : calendar[0][0]

3.9 整数溢出

有符号数和无符号数结合时,有符号数会被转换为无符号数


4. 连接

4.2 声明和定义

如果两个源文件无意定义了相同的全局变量,系统应该能够处理这种情况。
但是若本意该全局变量只在该文件中使用,建议使用static修饰。
static同样可以修饰函数,表示该函数只在该函数体内生效。

4.3 形参和实参

一些编译器似乎支持这样的定义和声明:

/* 声明 */
int isvowel();

/* 定义 */
int isvowel(char c)
{
	return c == 'a' || c == 'e'; 
}

声明中省略了形参类型,调用时,传参会默认为int类型。
虽然这个做法可能是可以的,但绝对不推荐这么做。

4.5 全局变量类型检查

int a = 5;
extern long a;

上面这种写法可以称之为错误,但大多数编译器无法检测出这种错误,或者自作聪明使用一个空间存储两个实例。

类似的:

char filename[] = "/etc/passwd";
extern char *filename;

这两种无法以一个合乎情理的方式共存,需要改为:

char filename[] = "/etc/passwd";
extern char filename[];

char *filename = "/etc/passwd";
extern char *filename;

5. 库函数

/* 看完UNIX环境编程再看 */

6. 预处理

6.1 宏定义中的空格

#define f(x)  ((x)-1)
#define f (x)  ((x)-1)

上面第二种写法能够达到预期的效果吗?答案是不能。其表达的含义是f 代表 (x) ((x)-1).

但调用时f(3)和f (3)都能达到预期的效果.

6.4 宏不是类型定义

考虑下面的代码

#define T1 struct foo *

typedef struct foo *T2;

上面两种写法T1和T2从概念上完全符合,都是指向结构foo的指针

T1 a;
T2 b;

但是当试图声明多个变量时,问题就来了

T1 a, b;
T2 a, b;

第一个声明会被扩展为:

 struct foo *a, b;

一个为指针类型,另一个不是.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值