目录
C/C++语言程序设计缺陷(1)
符号误用问题
符号是程序的一个基本组成单元,其作用相当于句子中的单词,每个符号都有其特定的含义。在程序语句中,符号的误用常常引起语句错误,导致程序异常。
1.布尔型变量被赋值
例1:
void f(bool flag)
{
int a;
bool ok;
ok=true;
if(flag=ok) // flag是一个bool型变量
a++;
}
例1中,第6行条件判断语句,存在布尔型变量flag被赋值的错误。
2.“==”运算符与“=”赋值符混用
由于“= =”运算符和“=”赋值符形似,二者经常混用。例2和例3中的if语句应使用运算符“==”,但误用了赋值符“=”。
例2:
void foo(int i, int j)
{
int t;
if(i=j)
t++;
}
例2中,第4行语句本意是比较i和j的值是否相等,而“if(i=j)”执行的操作是把j值赋给i,再判断i的值是否为0。
例3:
class A
{
int t;
int q();
};
main()
{
int num;
A a;
if (i=a.q())
{
num++;
}
}
例3中,第10行条件判断语句中,“==”判断符误用为“=”赋值符,函数调用语句出现在if条件判断语句“=”赋值符的右边。
例4:
f()
{return 1;}
main()
{
int a;
int array[3]={1,12,13};
if ((a==array[2])< 0)
f();
else return -1;
}
例4中,第7行语句本意是将array[2]赋值给a后,再将a的值和0作比较,如果条件为真则执行函数f();当使用“==”后,“(a= =array[2])”的值只能是0或1,不会小于0,函数f()永远不会执行。
3.按位运算符“&”、“|”和逻辑运算符“&&”、“||”混用
例5:
f()
{return 1;}
main()
{
int a=8;
int b=1;
if(a&b) f();
}
例5中,第7行条件判断语句“if(a&b)”本意是逻辑与运算“&&”,被误用为按位与运算符“&”后,“8&1”结果为0,函数f()将不会执行。
4.单引号‘ ’和双引号“ ” 字符混用
程序中单引号的字符,比如’a’代表一个字符,字符在编译器中有其对应字符集中的序列值,也可以说单引号字符代表一个整数;而双引号代表一个字符串,字符串在编译器中代表一个指向无名数组起始字符的指针。
例6:
main ()
{
char * p;
p='a';
}
例6中,指针P是字符型的指针,‘a’代表一个整数,赋值号两边的数据类型不匹配。
5.赋值表达式错用其他操作符
例7:
int main(int k)
{
int j=1;
k=0;
if(j)
{
j>k;
return j;
}
else
{
k++;
return k;
}
}
例7中,第7行语句错用操作符“>”代替赋值表达式操作符“=”,使得该语句没有任何意义。
6.分号使用不当
程序中不小心多了或少了一个分号,这个分号也许会作为不会产生任何实际效果的空语句,但在某些情况下,却可能造成不良后果。
例8:
#include <stdlib.h>
main()
{
int a, c ;
int array[3]={10, 06, 12};
scanf("%d",&a);
if(a < 0);
c=a;
}
例8中,第7行语句多了一个分号,那么紧跟在if语句之后的语句“c=a;”就是一条单独的语句,和条件判断部分完全没有任何关系了。
例9:
#include <stdlib.h>
void f()
{
int a;
float array[3];
scanf("%d",&a);
…
if (a < 0)
return
array[2]=a;
}
例9中,第9条语句少了一个分号,程序会把“array[2]=a;”语句中分号之前的内容作为return的返回内容,而该程序中函数f()不应返回任何内容。
有时程序中会误用中文分号作为程序中所使用的英文分号,在这种情况下,编译器会对这个错误的分号产生一条告警信息。
7.自增/自减(++/–)运算符和变量间有空格
程序中无论是自增或自减运算符,运算符和变量之间不能有空格。
例10:
main()
{
int i=6;
int j=7;
-- i;
j ++;
}
例10中,自减或自增符号和变量i、j之间都有空格,为了增加程序的可读性,应该去除自减或自增符号和变量i、j间的空格。
8.错误使用自增/自减(++/–)运算符
自增/自减运算符(++/–)表达简练,因此在C/C++编程中会经常用到,但就是这个几乎在每个程序中都会用到的运算符,如果不注意细节,就会产生错误。
例11中的程序遍历数组arr[10]的每个元素,如果元素的值不等于2,则执行自增/自减操作,并打印自增/自减后的值。
例11:
#include<stdio.h>
int main()
{
int arr[10] = {2,3,1,2,3,3,1,2,2,3};
int tmp = 0;
for(int i = 0; i<10; i++)
{
if(arr[i] < 2)
{
tmp = arr[i]++;
printf("arr[%d] = %d\n", i, tmp);
}
else if(arr[i] > 2)
{
tmp = arr[i]--;
printf("arr[%d] = %d\n", i, tmp);
}
}
return 0;
}
按照程序设计意图,每次打印语句中的元素值应该都是2,但是查看结果发现,打印出来的值没有变,这是因为错误使用了自增/自减运算符。
单独使用a++和++a时,其结果是一样的,都相当于a=a+1,但tmp= a++和tmp= ++a得到的结果是不一样的:
tmp= a++,相当于tmp = a;a=a+1。
tmp= ++a,相当于a=a+1;tmp = a。
前者先赋值再自增,后者先自增再赋值,即包含的赋值操作的顺序不一样,结果也就不一样了,同时两者的效率也不一样,对于a++,需要用一个临时变量来保存a的值,然后执行自增操作;而对于++a来说,整个表达式的值就是a的值,无需进行中间值的复制操作,因此其效率要高一些。
在例11中,第11行语句和第17行语句赋给tmp的值并非是数组元素自增/自减后的值,而是数组元素本来的值。
在需要使用自增/自减运算符时,如无特殊情况,建议使用++a(–a)格式的语句。
9.字符串结束符被误用
C语言中没有专门的字符类型,通常用字符数组来存放字符串,以’\0’作为字符串的结束符。如果结束符’\0’被误用为"\0"时,可能导致程序崩溃。
例12:
Main()
{
char a[10];
for(i=0;i<9;i++)
a[i]=i;
a[9]= '\0';
…
f(a);
}
例12中,第6行语句欲把‘\0’作为结束符赋值给字符型数组a的最后一个元素,在这种情况下,C++编译器不会把字符‘\0’赋值给一个数组元素,但C编译器会这样做。这种错误在编程时不易察觉,需要注意。
10.对浮点型变量进行相等比较
浮点数在内存中的存储机制和整数不同,有舍入误差,在计算机中用以表示某个近似实数,但无法精确。具体来说,这个实数由一个整数或定点数乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学记数法。因为浮点数是非精确存储,所以比较浮点数是否相等时时,不能用关系运算符中的“= =”、“!=”、“>=”和“<=”进行比较运算。
例13:
int main()
{
float a;
float b;
…
if(a==b)
return 1;
…
}
例13的第6行语句中,直接使用关系运算符“==”比较两个浮点数是否相等。当需要判断两个浮点数是否相等时,应该先设定一个精度,比较两个浮点数差的绝对值是否在这个精度范围内。
修改方法:将第6行语句改为“if(fabs(a-b) < 1.0E-10)”。
11.程序中运算符的优先级使用错误
编程时如果不知道运算符的优先级,可能导致程序中运算表达式错误,进而引起程序异常。
对于运算符之间的优先级,记住关键两点:
① 所有逻辑运算符的优先级都低于任何一个关系运算符;
② 移位运算符的优先级比算术运算符低,但比关系运算符高。
例14:
#include <stdlib.h>
main()
{
int a, b, c;
scanf("%d",&a);
scanf("%d",&b);
c = ( a & b!=0 )
}
例14中,第7行语句本意是“c = ( (a & b)!=0 )”,即a和b先做按位与运算,运算后的值再和0作比较,但由于运算符“!=”的优先级大于“&”,所以条件表达式变为“c=(a&(b!=0))”,即b和0比较的结果,再和a值按位与运算,导致错误的结果。
12.使用逗号导致程序发生错误
例15:
void FpParser(USHORT *pUnit,USHORT carrierNumber,USHORT frameLen)
{
…
if(snmp_var.syntax==SNMP_SYNTAX_OCTETS)
{
memcpy(cMsg,snmp_var.value.string.ptr,snmp_var.value.string.len),
FpParser((USHORT*)cMsg,g_config.m_CarrNum, g_config m_FramLen);
}
…
}
例15中第6行语句,函数之间用逗号分隔,在这种情况下,只返回最右边函数的值。
修改方法:分开写,都用分号。