C语言编程陷阱(三)

本文详细介绍了C语言编程中的五个常见陷阱,包括错误使用运算符比较字符串、赋值给常量、用char存储非字符数据、float和double存储精确小数以及int存储大数。错误的代码示例及出错原因分析,以及对应的正确代码展示,帮助开发者避免这些陷阱,提高编程质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

陷阱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),它会把name1name2的地址进行比较,而不是把它们的内容进行比较,因为name1name2是两个不同的字符数组,它们的地址是不同的,即使它们的内容都是"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.141.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.1float 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类型来存储精确的小数,而应该使用更合适的数据类型,比如intlongdecimal等,以提高数值的正确性和一致性。

陷阱15:不要使用int类型来存储大数

  • int类型是C语言中的一个基本数据类型,它可以用来存储一个整数,比如1-2100等,它的大小通常是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 = 1000000int 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类型来存储大数,而应该使用更合适的数据类型,比如longlong longbigint等,以提高数值的正确性和一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

K_n_i_g_h_t_1990

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值