C语言程序开发中,两个初学者经常犯的错误

在《》一文中,我们讨论了C语言程序开发中基础的赋值操作,涉及到了一些数据类型转换的过程,虽然简单,但还是有不少初学者弄错,因此文章较为详细的分析了易错点以及原因。

文章发出后,我收到了一些读者回复,但是有些回复其实是不准确的,甚至是错误的。为此,本文挑出两个典型,做进一步的解析。

“对常数使用强制类型转换毫无意义”

这是网友 @Neverdies 的回复。那这句话对不对呢?严格来说,这句话是不准确的,强制类型转换可以限定常数的宽度。例如下面这行C语言赋值语句:

int res = (unsigned char)0xffff;

该语句执行完成后,res 中的数值将为 0xff,而不是 0xffff。当然了,在实际的C语言程序开发中,也可以将 ret 声明为 unsigned char 型,这样就可以省去对常数 0xffff 的强制类型转换了。

如果读者觉得上面这个例子没有实际应用价值,再来看下面这段C语言代码:

#include <stdio.h>

int main()
{
    unsigned long long i = 0;

    i = 146*100000000;
    printf("i: %lld\n", i);

    return 0;
}

编译并执行这段C语言代码,会输出什么呢?请看:

# gcc t.c
# ./a.out 
i: 1715098112

居然不是 14600000000!事实上,这段简短的C语言代码常常会让初学者百思不得其解,甚至会怀疑C语言或者计算机有问题,出故障了。

其实原因很简单,就是数据溢出了而已。以我的机器为例,在C语言程序的计算过程中,所有乘数默认都是 int 型的,上述C语言代码中的两个常数的乘法运算结果显然超出了 int 型能够表示的最大数据,当然会出错了。

这其实是一个隐藏的较深的错误,稍微有些经验的C语言程序员能够避开变量的数据溢出,因为C语言中的变量总是需要使用数据类型定义的,而某种数据类型能够表达的数据范围,程序员心里一般都有一把标尺。但是常数导致的数据溢出,很多程序员就容易忽略了。

要改正上述C语言代码的错误其实很简单,只需将任意一个乘数转换为宽度更宽的数据类型就可以了,相关C语言代码如下,请看:

#include <stdio.h>

int main()
{
    unsigned long long i = 0;

    i = 146*(unsigned long long)100000000;
    printf("i: %lld\n", i);

    return 0;
}


编译并执行这段C语言代码,发现输出与预期一致了。

# gcc t.c
# ./a.out 
i: 14600000000

“char 类型加上 signed 是多余,因为char默认就是有符号的”

计算机存储的最小单位是一个字节(Byte,有人习惯称为“大B”),一个字节通常是 8 个位(bit,有人习惯称为“小b”)。C语言规定 char 型占一个字节的存储空间,还规定了 signed 和 unsigned 两个关键字,分别用于表示有符号数和无符号数。

那么不带 signed 和 unsigned 关键字的 char 型变量到底是有符号还是无符号呢?C语言标准规定这是 Implementation Defined,也即未定义,究竟有无符号取决于编译器,有些编译器默认 char 型是有符号的(但不是 所有编译器都如此)。

C语言标准的原则之一是:优先考虑效率,可移植性其次。这就要求C语言程序员非常清楚这些规则,如果项目要求了可移植性,那么在编写代码时,就必须了解哪些写法是不可移植的,应该避免使用。

当然了,也没有必要视“不可移植”为洪水猛兽,例如 Linux 的内核C语言代码使用了很多 gcc 的特性,以得到最佳的执行效率,在编写代码时就没打算使用别的编译器编译,“可移植性”也就无从说起。

不过,这也不是说C语言程序员就可以随意写代吗,如果要写不可移植的代码,就必须明白代码中的哪些部分不可移植,以及这么设计的原因,一般来说,如果不是为了执行效率,就完全没有理由故意写不可移植的C语言代码。

小结

本节主要讨论的两个问题是C语言初学者容易忽略的。但是也有些程序员认为没有必要太抠细节。

对于其他编程语言也许如此,但是对于C语言程序员来说,如果不够“抠门”,不想着如何尽量多节约一个字节出来,如何写出可移植性强以及效率高的代码出来,对自身的能力发展是没有好处的。这是由嵌入式C语言程序开发本身的特性决定的。

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif