陷阱11:不要使用==
运算符来比较两个字符串是否相等
- 字符串是C语言中常用的数据类型之一,它是由一系列字符组成的数组,以空字符
\0
结尾。 - 有时候,我们需要比较两个字符串是否相等,比如在验证用户输入,或者在查找匹配的数据,或者在排序字符串等,就需要使用
==
运算符或strcmp
函数来比较两个字符串是否相等。 - 但是,如果我们使用
==
运算符来比较两个字符串是否相等,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
#include <stdio.h>
int main(void)
{
char name1[10] = "Alice"; // 定义一个长度为10的字符数组,用来存储姓名,初始化为"Alice"
char name2[10] = "Alice"; // 定义另一个长度为10的字符数组,用来存储姓名,初始化为"Alice"
if (name1 == name2) // 使用==运算符来比较两个字符串是否相等
{
printf("The names are equal\n"); // 如果相等,打印相等的信息
}
else
{
printf("The names are not equal\n"); // 如果不相等,打印不相等的信息
}
return 0;
}
为什么会出错呢?
- 因为使用
==
运算符来比较两个字符串是否相等,会把两个字符串的地址(也就是第一个字符的地址)进行比较,而不是把两个字符串的内容进行比较,这就意味着,如果两个字符串的内容相同,但是存储在不同的内存空间,就会被认为是不相等的,因为它们的地址是不同的。 - 在上面的例子中,我们使用
==
运算符来比较两个字符串是否相等,if (name1 == name2)
,它会把name1
和name2
的地址进行比较,而不是把它们的内容进行比较,因为name1
和name2
是两个不同的字符数组,它们的地址是不同的,即使它们的内容都是"Alice"。这就导致了比较的结果是不相等的,打印的信息是The names are not equal
,而不是The names are equal
。
正确的代码
#include <stdio.h>
#include <string.h> // 引入string.h头文件,定义strcmp函数
int main(void)
{
char name1[10] = "Alice"; // 定义一个长度为10的字符数组,用来存储姓名,初始化为"Alice"
char name2[10] = "Alice"; // 定义另一个长度为10的字符数组,用来存储姓名,初始化为"Alice"
if (strcmp(name1, name2) == 0) // 使用strcmp函数来比较两个字符串是否相等,返回值为0表示相等,非0表示不相等
{
printf("The names are equal\n"); // 如果相等,打印相等的信息
}
else
{
printf("The names are not equal\n"); // 如果不相等,打印不相等的信息
}
return 0;
}
陷阱12:不要使用=
运算符来赋值给一个常量
- 常量是C语言中的一种字面量,它表示一个固定的值,不能被修改,它可以是一个整数,一个浮点数,一个字符,一个字符串,或者一个枚举值等。
- 有时候,我们需要使用一个常量来表示一个固定的值,比如在定义一个宏,或者在声明一个枚举类型,或者在初始化一个变量等,就需要使用一个常量来赋值给一个标识符。
- 但是,如果我们使用
=
运算符来赋值给一个常量,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
#include <stdio.h>
int main(void)
{
10 = 20; // 使用=运算符来赋值给一个常量
printf("%d\n", 10); // 打印10的值
return 0;
}
为什么会出错呢?
- 因为使用
=
运算符来赋值给一个常量,会违反常量的不可修改的特性,从而影响程序的正确性和一致性。 - 当我们使用
=
运算符来赋值给一个常量时,它会试图把右边的值存储到左边的常量所表示的内存空间中,但是这是不可能的,因为常量所表示的内存空间是只读的,不能被修改。这就导致了编译器会报错,提示左边的操作数不是一个左值(lvalue),也就是说,它不是一个可以被赋值的对象。 - 在上面的例子中,我们使用
=
运算符来赋值给一个常量,10 = 20
,它会试图把20存储到10所表示的内存空间中,但是这是不可能的,因为10是一个常量,它的内存空间是只读的,不能被修改。这就导致了编译器会报错,提示error: lvalue required as left operand of assignment
,也就是说,左边的操作数必须是一个左值,而不是一个常量。
正确的代码
#include <stdio.h>
int main(void)
{
int x = 10; // 定义一个整数变量,赋值为10
x = 20; // 使用=运算符来赋值给一个变量
printf("%d\n", x); // 打印x的值
return 0;
}
陷阱13:不要使用char
类型来存储非字符的数据
char
类型是C语言中的一个基本数据类型,它可以用来存储一个字符,比如'a'
,'1'
,'+'
等,它的大小通常是一个字节(8位),它的取值范围通常是-128到127(有符号)或0到255(无符号)。- 有时候,我们需要使用一个字节的数据来存储一些非字符的数据,比如一个布尔值,一个枚举值,一个位域等,就需要使用
char
类型来存储这些数据。 - 但是,如果我们使用
char
类型来存储非字符的数据,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
#include <stdio.h>
int main(void)
{
char flag = 1; // 定义一个char类型的变量,用来存储一个布尔值,赋值为1
if (flag) // 使用if语句来判断flag的值是否为真
{
printf("Flag is true\n"); // 如果为真,打印真的信息
}
else
{
printf("Flag is false\n"); // 如果为假,打印假的信息
}
flag = flag + 1; // 把flag的值加1
if (flag) // 再次使用if语句来判断flag的值是否为真
{
printf("Flag is true\n"); // 如果为真,打印真的信息
}
else
{
printf("Flag is false\n"); // 如果为假,打印假的信息
}
return 0;
}
为什么会出错呢?
- 因为使用
char
类型来存储非字符的数据,会涉及到char
类型的表示和转换的问题,从而影响数据的正确性和一致性。 - 当我们使用
char
类型来存储非字符的数据时,它会根据char
类型的取值范围来存储和处理数据,而不是根据数据的本身的含义和规则来存储和处理数据,这就意味着,如果数据的值超过了char
类型的取值范围,就会发生溢出或截断的问题,或者如果数据的值和char
类型的值有冲突,就会发生混淆或误解的问题。 - 在上面的例子中,我们使用
char
类型来存储一个布尔值,char flag = 1
,然后使用if
语句来判断flag的值是否为真,if (flag)
,这样做没有问题,因为char
类型的值1可以被当作布尔值真来处理,所以打印的结果是Flag is true
。但是,当我们把flag的值加1,flag = flag + 1
,就会发生溢出的问题,因为char
类型的最大值是127(有符号)或255(无符号),如果再加1,就会变成-128(有符号)或0(无符号),这样就和原来的值不一致了。然后,当我们再次使用if
语句来判断flag的值是否为真,if (flag)
,就会发生混淆的问题,因为char
类型的值-128(有符号)或0(无符号)都可以被当作布尔值假来处理,所以打印的结果是Flag is false
,而不是Flag is true
。
正确的代码
#include <stdio.h>
#include <stdbool.h> // 引入stdbool.h头文件,定义bool类型
int main(void)
{
bool flag = true; // 定义一个bool类型的变量,用来存储一个布尔值,赋值为true
if (flag) // 使用if语句来判断flag的值是否为真
{
printf("Flag is true\n"); // 如果为真,打印真的信息
}
else
{
printf("Flag is false\n"); // 如果为假,打印假的信息
}
flag = !flag; // 把flag的值取反
if (flag) // 再次使用if语句来判断flag的值是否为真
{
printf("Flag is true\n"); // 如果为真,打印真的信息
}
else
{
printf("Flag is false\n"); // 如果为假,打印假的信息
}
return 0;
}
陷阱14:不要使用float
类型或double
类型来存储精确的小数
float
类型和double
类型是C语言中的两种浮点数类型,它们可以用来存储一个带有小数部分的数值,比如3.14
,1.23e-4
,-6.78
等,它们的大小通常是4字节(32位)和8字节(64位),它们的取值范围和精度取决于具体的实现。- 有时候,我们需要使用一个带有小数部分的数值来存储一些精确的小数,比如在计算金钱,或者在处理科学计算,或者在测试程序等,就需要使用
float
类型或double
类型来存储这些小数。 - 但是,如果我们使用
float
类型或double
类型来存储精确的小数,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
#include <stdio.h>
int main(void)
{
float x = 0.1; // 定义一个float类型的变量,赋值为0.1
float y = 0.2; // 定义另一个float类型的变量,赋值为0.2
float z = x + y; // 定义第三个float类型的变量,赋值为x和y的和
printf("%f\n", z); // 打印z的值
return 0;
}
为什么会出错呢?
- 因为使用
float
类型或double
类型来存储精确的小数,会涉及到浮点数的表示和运算的问题,从而影响数值的正确性和一致性。 - 当我们使用
float
类型或double
类型来存储精确的小数时,它会根据浮点数的标准(IEEE 754)来存储和处理数值,而不是根据数值的本身的含义和规则来存储和处理数值,这就意味着,如果数值的精度超过了float
类型或double
类型的精度,就会发生舍入或截断的问题,或者如果数值的运算涉及到一些特殊的情况,就会发生溢出或下溢或非数或无穷的问题。 - 在上面的例子中,我们使用
float
类型来存储精确的小数,float x = 0.1
,float y = 0.2
,然后使用+
运算符来计算它们的和,float z = x + y
,这样做没有问题,因为float
类型的精度可以表示0.1和0.2,而且+
运算符可以正确地计算它们的和。但是,当我们打印z的值,printf("%f\n", z)
,就会发生舍入的问题,因为float
类型的精度不能精确地表示0.3,所以它会把0.3舍入为一个最接近的值,比如0.300000012,这样就和原来的值不一致了。打印的结果是:
0.300000
而不是:
0.300000
正确的代码
#include <stdio.h>
int main(void)
{
int x = 10; // 定义一个整数变量,赋值为10,表示0.1
int y = 20; // 定义另一个整数变量,赋值为20,表示0.2
int z = x + y; // 定义第三个整数变量,赋值为x和y的和,表示0.3
printf("%d.%d\n", z / 100, z % 100); // 打印z的值,使用除法和取余运算来分离整数部分和小数部分
return 0;
}
请注意,float
类型和double
类型在一些特定的场合是有用的,比如在存储一个近似的小数,或者在实现高精度的计算,但是在一般的情况下,不建议使用float
类型或double
类型来存储精确的小数,而应该使用更合适的数据类型,比如int
,long
,decimal
等,以提高数值的正确性和一致性。
陷阱15:不要使用int
类型来存储大数
int
类型是C语言中的一个基本数据类型,它可以用来存储一个整数,比如1
,-2
,100
等,它的大小通常是4字节(32位),它的取值范围通常是-2147483648到2147483647(有符号)或0到4294967295(无符号)。- 有时候,我们需要使用一个整数来存储一些大数,比如在计算阶乘,或者在处理大数据,或者在测试程序等,就需要使用
int
类型或long
类型或long long
类型来存储这些大数。 - 但是,如果我们使用
int
类型来存储大数,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
#include <stdio.h>
int main(void)
{
int x = 1000000; // 定义一个int类型的变量,赋值为1000000
int y = x * x; // 定义另一个int类型的变量,赋值为x和x的乘积
printf("%d\n", y); // 打印y的值
return 0;
}
为什么会出错呢?
- 因为使用
int
类型来存储大数,会涉及到int
类型的表示和运算的问题,从而影响数值的正确性和一致性。 - 当我们使用
int
类型来存储大数时,它会根据int
类型的取值范围来存储和处理数值,而不是根据数值的本身的含义和规则来存储和处理数值,这就意味着,如果数值的值超过了int
类型的取值范围,就会发生溢出或截断的问题,或者如果数值的运算涉及到一些特殊的情况,就会发生错误或异常的问题。 - 在上面的例子中,我们使用
int
类型来存储大数,int x = 1000000
,int y = x * x
,这样做没有问题,因为int
类型的取值范围可以表示1000000,而且*
运算符可以正确地计算它们的乘积。但是,当我们打印y的值,printf("%d\n", y)
,就会发生溢出的问题,因为int
类型的最大值是2147483647(有符号)或4294967295(无符号),而1000000和1000000的乘积是1000000000000,超过了int
类型的取值范围,所以它会把1000000000000截断为一个最接近的值,比如-727379968(有符号)或3579139424(无符号),这样就和原来的值不一致了。打印的结果是:
-727379968
而不是:
1000000000000
正确的代码
#include <stdio.h>
int main(void)
{
long long x = 1000000; // 定义一个long long类型的变量,赋值为1000000
long long y = x * x; // 定义另一个long long类型的变量,赋值为x和x的乘积
printf("%lld\n", y); // 打印y的值
return 0;
}
请注意,int
类型在一些特定的场合是有用的,比如在存储一个小整数,或者在实现位操作,但是在一般的情况下,不建议使用int
类型来存储大数,而应该使用更合适的数据类型,比如long
,long long
,bigint
等,以提高数值的正确性和一致性。