人人都写过的6个bug

大家好,我是知微。

程序员写bug几乎是家常便饭,也是我们每个人成长过程中难以避免的一部分。

为了缓解这份“尴尬”,今天想和大家分享一些曾经都会遇到过的bug,让我们一起来看看这些“经典之作”。

1、数组越界

#include <stdio.h>

int main() {
    int array[5];
    array[5] = 42;

    printf("幸运数字是:%d\n", array[5]);
    return 0;
}
  • 在这段代码中,我们声明了一个包含5个整数的数组,但随后我们尝试给数组的第六个元素(array[5])赋值。
  • 在C语言中,数组的索引是从0开始的,所以实际上我们只有array[0]array[4]这五个有效的元素。
  • 因此,尝试给第六个元素赋值会导致数组越界。

这种越界行为可能会导致程序崩溃、产生未定义的行为,甚至可能影响到程序的其他部分。

2、忘记给变量赋初值

忘记给变量赋初值是一个常见的编程错误,有时候它会导致程序运行时出现意外的行为。让我们来看一个例子:

#include <stdio.h>

int main() {
    int a = 5;
    int b;

    printf("a的值是:%d\n", a);
    printf("b的值是:%d\n", b);
    return 0;
}
  • 在这段代码中,我们给变量a赋了初值为5,但变量b却没有被初始化。

  • 在C语言中,未初始化的局部变量会包含一些随机的垃圾值,这样在使用它们的时候就会出现问题。

  • 修复这个问题的方式是给变量b一个明确的初值,如:

int b = 0;

或者根据实际需求选择一个适当的初值,这样可以避免在程序中使用未初始化的变量而引发的问题。

3、条件判断写成赋值

如果把 = 写成了 ==,本来是判断相等的,结果变成了赋值。条件永远成立,导致程序执行了不该执行的分支。

#include <stdio.h>

int main() {
    int x = 10;

    if (x = 5) {
        printf("条件成立!\n");
    } else {
        printf("条件不成立!\n");
    }

    return 0;
}

  • 在这个例子中,条件判断中使用了单个等号(=)而不是比较相等的双等号(==)。
  • 这将导致x被赋值为5,然后条件判断始终为真,执行了不该执行的分支。

4、内存未释放或者重复释放

a、内存未释放

#include <stdlib.h>

int main() {
    int *arr = malloc(10 * sizeof(int));

    // 忘记释放内存
    // free(arr);

    return 0;
}

  • 在这个例子中,我们使用malloc分配了一块内存来存储整数数组,但是忘记了在程序结束前释放这块内存。
  • 这样会导致内存泄漏,即分配的内存无法被回收,最终可能耗尽系统的可用内存。

b、重复释放内存

#include <stdlib.h>

int main() {
    int *arr = malloc(10 * sizeof(int));

    // 重放释放内存
    free(arr);
    free(arr);

    return 0;
}
  • 在这个例子中,我们错误地尝试释放相同的内存块两次。
  • 这种情况可能导致程序崩溃或者其他未定义的行为,因为系统会认为这块内存已经被释放,再次释放它会产生问题。

看到这里有人会有疑问,谁会这么傻,在一个地方free两次,我free一次不就好了。

的确,我们在写代码的时候,基本不会出现例子中这种情况。但是我接下来要说的场景,还是很有可能发生的。

假如在a文件中申请了动态内存,指针arr指向这块内存。b文件free(arr), 然后某次不小心,在c文件中又free(arr),这种情况就会导致重复释放内存了。

5、强制类型转换

#include <stdio.h>

int main() {
    int a = 5;
    float b = 2.5;

    // 无名的类型强制可能导致精度损失
    int result = (int)b + a;

    printf("结果是:%d\n", result);

    return 0;
}

  • 在这个例子中,我们试图将浮点数 b 强制转换为整数,并与整数 a 相加。这可能导致精度损失,因为浮点数可能包含小数部分,但在强制转换时小数部分将被丢弃,导致结果不准确。

  • 在进行类型转换时,需要仔细考虑可能发生的精度损失,并确保结果仍然符合预期。如果可能会有精度损失,最好考虑使用更合适的数据类型或者进行四舍五入等操作以确保精度。

6、使用未初始化的指针

未初始化的指针可能包含随机的内存地址,导致未定义的行为。

#include <stdio.h>

int main() {
    int *ptr;

    // 使用未初始化的指针
    *ptr = 42;

    printf("值:%d\n", *ptr);

    return 0;
}

  • 在这个例子中,指针 ptr 没有被初始化,但却尝试通过它来访问内存并赋值。

  • 这可能导致程序崩溃或者产生不可预测的结果,因为指针 ptr 实际上指向了一个未知的内存地址。

  • 为了避免这个问题,始终确保在使用指针之前对其进行初始化。例如,通过分配内存或将其指向一个已有的变量:

int value = 42;
int *ptr = &value;  // 初始化指针,指向变量 value

printf("值:%d\n", *ptr);
  • 或者,如果不想立即初始化,可以将指针设置为 NULL(空指针),然后在需要时再进行初始化:
int *ptr = NULL;  // 初始化为空指针

// 在需要时分配内存或指向已有变量
// ...

printf("值:%p\n", (void *)ptr);  // 注意:%p 用于打印指针的地址
  • 通过这样的方式,可以确保在使用指针之前,它已经指向了有效的内存地址,避免了潜在的未定义行为。

​以上就是本文的全部内容了​!

📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!

posted @ 2024-03-06 22:14  知微之见  阅读(20)  评论(0编辑  收藏  举报