加入收藏 | 设为首页 | 会员中心 | 我要投稿 拼字网 - 核心网 (https://www.hexinwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

人人都做过的五个Bug!

发布时间:2021-11-03 16:31:04 所属栏目:语言 来源:互联网
导读:大家好,我是良许。 计算机专业的小伙伴,在学校期间一定学过 C 语言。它是众多高级语言的鼻祖,深入学习这门语言会对计算机原理、操作系统、内存管理等等底层相关的知识会有更深入的了解,所以我在直播的时候,多次强调大家一定要好好学习这门语言。 但是,
大家好,我是良许。   计算机专业的小伙伴,在学校期间一定学过 C 语言。它是众多高级语言的鼻祖,深入学习这门语言会对计算机原理、操作系统、内存管理等等底层相关的知识会有更深入的了解,所以我在直播的时候,多次强调大家一定要好好学习这门语言。   但是,即使是最有经验的程序员也会写出各种各样的 Bug。本文就盘点一下学习或使用 C 语言过程中,非常容易出现的 5 个 Bug,以及如何规避这些 Bug。   这篇文章主要面向初学者,老鸟可以忽略哈(其实不少老鸟依然还会犯这些低级错误哦)~   1. 变量未初始化 当程序启动时,系统会给它自动分配一块内存,程序可以用它来存储数据。所以如果你在定义一个变量时,在未初始化的情况下,它的值有可能是任意的。   但这也不是绝对的,有些环境就会在程序启动时自动将内存「清零」,因此每个变量默认值都是零。考虑到可移植性,最好要将变量进行初始化,这是一名合格软件工程师应该养成的好习惯。   我们来看下下面这个使用几个变量和两个数组的示例程序:   #include <stdio.h>   #include <stdlib.h>   int main()   {     int i, j, k;     int numbers[5];     int *array;     puts("These variables are not initialized:");     printf("  i = %dn", i);     printf("  j = %dn", j);     printf("  k = %dn", k);     puts("This array is not initialized:");     for (i = 0; i < 5; i++) {       printf("  numbers[%d] = %dn", i, numbers[i]);     }     puts("malloc an array ...");     array = malloc(sizeof(int) * 5);     if (array) {       puts("This malloc'ed array is not initialized:");       for (i = 0; i < 5; i++) {         printf("  array[%d] = %dn", i, array[i]);       }       free(array);     }     /* done */     puts("Ok");     return 0;   }  这段程序没有对变量进行初始化,所以变量的值有可能是随机的,不一定是零。在我的电脑上它的运行结果如下 :   These variables are not initialized:     i = 0     j = 0     k = 32766   This array is not initialized:     numbers[0] = 0     numbers[1] = 0     numbers[2] = 4199024     numbers[3] = 0     numbers[4] = 0   malloc an array ...   This malloc'ed array is not initialized:     array[0] = 0     array[1] = 0     array[2] = 0     array[3] = 0     array[4] = 0   Ok  从结果可以看出,i 和 j 的值刚好是 0,但 k 值为 32766。在 numbers 数组中,大多数元素也恰好是零,除了第三个(4199024)。   在不同的操作系统上编译这段相同的程序,运行的结果有可能又是不一样的。所以千万不要觉得你的结果就是正确唯一的,一定要考虑可移植性。   例如,这是在 FreeDOS 上运行的相同程序的结果:   These variables are not initialized:     i = 0     j = 1074     k = 3120   This array is not initialized:     numbers[0] = 3106     numbers[1] = 1224     numbers[2] = 784     numbers[3] = 2926     numbers[4] = 1224   malloc an array ...   This malloc'ed array is not initialized:     array[0] = 3136     array[1] = 3136     array[2] = 14499     array[3] = -5886     array[4] = 219   Ok  可以看出来,运行的结果跟上面几乎是天差地别。所以,对变量进行初始化将为你省去很多不必要的麻烦,也便于将来的调试。   2. 数组越界 在计算机世界里,都是从 0 开始计数,但总有人有意无意忘记这点。比如一个数组长度为 10 ,想要获取最后一个元素的值,总有人用 array[10] ……   别问,问就是我写过……   新手朋友犯这种低级错误特别多。我们来看下数组越界会发生什么。   #include <stdio.h>   #include <stdlib.h>   int main()   {     int i;     int numbers[5];     int *array;     /* test 1 */     puts("This array has five elements (0 to 4)");     /* initalize the array */     for (i = 0; i < 5; i++) {       numbers[i] = i;     }     /* oops, this goes beyond the array bounds: */     for (i = 0; i < 10; i++) {       printf("  numbers[%d] = %dn", i, numbers[i]);     }     /* test 2 */     puts("malloc an array ...");     array = malloc(sizeof(int) * 5);     if (array) {       puts("This malloc'ed array also has five elements (0 to 4)");       /* initalize the array */       for (i = 0; i < 5; i++) {         array[i] = i;       }       /* oops, this goes beyond the array bounds: */       for (i = 0; i < 10; i++) {         printf("  array[%d] = %dn", i, array[i]);       }       free(array);     }     /* done */     puts("Ok");     return 0;   }  请注意,程序初始化了数组 numbers 所有元素的值(0~4),但是越界读取了第 0~9 元素的值。可以看出来,前五个值是正确的,但之后鬼都不知道这些值会是什么:   This array has five elements (0 to 4)     numbers[0] = 0     numbers[1] = 1     numbers[2] = 2     numbers[3] = 3     numbers[4] = 4     numbers[5] = 0     numbers[6] = 4198512     numbers[7] = 0     numbers[8] = 1326609712     numbers[9] = 32764   malloc an array ...   This malloc'ed array also has five elements (0 to 4)     array[0] = 0     array[1] = 1     array[2] = 2     array[3] = 3     array[4] = 4     array[5] = 0     array[6] = 133441     array[7] = 0     array[8] = 0     array[9] = 0   Ok  所以大家在写代码过程中,一定要知道数组的边界。像这种数据读取的还好,如果一旦对这些内存进行写操作,直接就 core dump !   3. 字符串溢出 在 C 编程语言中,字符串是一组 char 值,也可以将其视为数组。因此,你也需要避免超出字符串的范围。如果超出,则称为字符串溢出。   为了测试字符串溢出,一种简单方法是使用 gets 函数读取数据。gets 函数非常危险,因为它不知道接收它的字符串中可以存储多少数据,只会天真地从用户那里读取数据。   如果用户输入字符串比较短那很好,但如果用户输入的值超过接收字符串的长度,则可能是灾难性的。   下面我们来演示一下这个现象:   #include <stdio.h>   #include <string.h>   int main()   {     char name[10];                       /* Such as "Beijing" */     int var1 = 1, var2 = 2;     /* show initial values */     printf("var1 = %d; var2 = %dn", var1, var2);     /* this is bad .. please don't use gets */     puts("Where do you live?");     gets(name);     /* show ending values */     printf("<%s> is length %dn", name, strlen(name));     printf("var1 = %d; var2 = %dn", var1, var2);     /* done */     puts("Ok");     return 0;   }  在这段代码里,接收数组的长度为 10 ,所以当输入数据长度小于 10 的话,程序运行就没问题。   例如,输入城市 Beijing ,长度为 7 :   var1 = 1; var2 = 2   Where do you live?   Beijing   <Beijing> is length 7  var1 = 1; var2 = 2   Ok  威尔士小镇 Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch 是世界上名字最长的城市,这个字符串有 58 个字符,远远超出了 name 变量中可保留的 10 个字符。   如果输入这个字符串,其结果是程序运行内存的其它位置,比如 var1和var2 ,都有可能被波及:   var1 = 1; var2 = 2   Where do you live?   Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch   <Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch> is length 58   var1 = 2036821625; var2 = 2003266668   Ok   Segmentation fault (core dumped)  在中止之前,程序使用长字符串覆盖内存的其他部分。请注意,var1 和 var2 不再是它们的起始值 1 和 2 。   所以我们需要使用更安全的方法来读取用户数据。例如,getline 函数就是一个不错的选择,它将分配足够大的内存来存储用户输入,因此用户不会因输入太长字符串而意外溢出。   4. 内存重复释放 良好的 C 编程规则之一是,如果分配了内存,就一定要将其释放。   我们可以使用 malloc 函数为数组和字符串申请内存,系统将开辟一块内存并返回一个指向该内存起始地址的指针。内存使用完毕后,我们一定要记得使用 free 函数释放内存,然后系统将该内存标记为未使用。   但是,这个过程中,你只能调用 free 函数一次。如果你第二次调用 free 函数,将导致意外行为,而且可能会破坏你的程序。   下面我们举个简单的例子:   #include <stdio.h>   #include <stdlib.h>   int main()   {     int *array;     puts("malloc an array ...");     array = malloc(sizeof(int) * 5);     if (array) {       puts("malloc succeeded");       puts("Free the array...");       free(array);     }     puts("Free the array...");     free(array);     puts("Ok");   }  运行此程序会导致第二次调用 free 函数时出现 core dump 错误:   malloc an array ...   malloc succeeded   Free the array...   Free the array...   free(): double free detected in tcache 2   Aborted (core dumped)  那么怎么避免多次调用 free 函数呢?一个最简单的方法就是将 malloc 和 free 语句放在一个函数里。   如果你将 malloc 放在一个函数里,而将 free 放在另一个函数里,那么,在使用的过程中,如果逻辑设计不恰当,都有可能出现 free 被调用多次的情况。

(编辑:拼字网 - 核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!