1. 算术转换
任何运算符在计算时都会引起转换,以类似的方式产生结果类型。如 int 和 double 型相加,int 型的操作数会先被转变为 double 型,而后做加法运算。
什么?已经懂了。那么看看下面的运算结果是如何?
-1 < 1
-1 > (unsigned int)1
-1 < (unsigned char)1
-1 > sizeof(int)
很多人一定对运算结果很好奇,但以上几个运算结果都是正确的。
ANSI C规定: 当执行算术运算时,操作数的类型如果不同,就会发生转变。数据类型一般朝着 浮点精度更高、长度更长的方向转换,整型数据如果转换为 signed 不会丢失信息,就转换为signed,负责转换为 unsigned。
第二个例子中,两个操作数均被转换为 unsigned int 型数据,-1 的unsigned 型为 0xFFFFFFFF,远大于1.(假设unsigned int 为32bits)
第三个例子中,两个操作数均被转换为 int 型数据。
第四个例子中,sizeof 返回 unsigned int 型,故两个操作数均被转换为unsigned int 型数据。
2. 运算次序
编写程序时,除了要注意运算符的优先级外,还要注意运算次序。下面的例子中b最后为多少?
int main() {
int a=1, b=2, c;
if(a ||(b--))
c=2;
return 0;
}
b=1?错!b仍然为2,因为b--根本就未被执行过。
在c语言中,除了&& , || 外,其他操作符都是没有操作次序的。而&& 和 || 都是先执行左边,再执行右边,一旦左边的值能确定最终运算结果,右边值将不被计算。
再看一个例子:
int a=1, b=2, c;
c = a--? a:b;
由于我们不知道编译器到底先取 ? 右边的a地址,还是先取左边的a--地址,所以c的值将不确定。
3. 不要将定义为某个函数内部局部变量的指针作为该函数的返回值
还是先看一个例子:
/* 日期被破坏问题 */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
char *localized_time(char *filename)
{
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
stat(filename, &stat_block);
tm_ptr = localtime(&stat_block.st_mtime);
strftime(buffer, sizeof(buffer), "%a %b %e %T %Y", tm_ptr);
return buffer;
}
int main()
{
char *buf;
buf = localized_time("test.c");
printf("Data:%s/n", buf);
}
该例将test.c文件的修正时间转变为本地时间,并按照一定格式打印出来。但事实是打印出来的可能是乱码。为什么呢?找到问题所在了吗? 其实问题就出在子函数 localized_time 的最后一句,它返回的指针地址指向的是该函数的一个局部变量的地址。当函数返回后,该局部变量马上被销毁,其地址也马上被回收。其地址上的数据可能马上被覆盖,也可能稍后被覆盖,这也是日期被破坏问题难以被发现的原因。