当前位置: 首页 > 后端技术 > Java

每个人都写过的5个错误!

时间:2023-04-01 21:37:26 Java

大家好,我是良旭。学计算机专业的朋友在学校肯定学过C语言。它是许多高级语言的鼻祖。深入学习这门语言,会让你对计算机原理、操作系统、内存管理等底层知识有更深入的了解。学习这门语言。然而,即使是最有经验的程序员也能写出各种错误。本文将盘点在学习或使用C语言过程中非常容易出现的5个bug,以及如何避免这些bug。本文主要针对新手,老手可以无视(其实很多老手还是会犯这些低??级错误)~1.变量未初始化。当程序启动时,系统会自动为其分配一块内存,程序可以用它来存储数据。所以如果你定义了一个变量,如果它没有被初始化,它的值可能是任意的。但这不是绝对的。有些环境会在程序启动时自动“清除”内存,因此每个变量的默认值为零。考虑到可移植性,最好对变量进行初始化,这是一个合格的软件工程师应该养成的好习惯。让我们看看下面使用几个变量和两个数组的示例程序:#include#includeintmain(){inti,j,k;整数[5];整数*数组;puts("这些变量没有初始化:");printf("i=%d\n",i);printf("j=%d\n",j);printf("k=%d\n",k);puts("这个数组没有初始化:");对于(i=0;i<5;i++){printf("numbers[%d]=%d\n",i,numbers[i]);}puts("malloc一个数组...");array=malloc(sizeof(int)*5);if(array){puts("这个malloc数组没有初始化:");对于(i=0;i<5;i++){printf("array[%d]=%d\n",i,array[i]);}免费(数组);}/*完成*/puts("Ok");return0;}这个程序没有初始化变量,所以变量的值可能是随机的,不一定为零。在我的电脑上它是这样工作的:这些变量没有被初始化:i=0j=0k=32766这个数组没有被初始化:numbers[0]=0numbers[1]=0numbers[2]=4199024numbers[3]=0numbers[4]=0mallocanarray...Thismalloc'edarrayisnotinitialized:array[0]=0array[1]=0array[2]=0array[3]=0array[4]=0Ok从结果可以看出,i和j的值恰好为0,但是k的值为32766。在numbers数组中,大部分元素也恰好为0,除了第三个(4199024).在不同的操作系统上编译同一个程序可能会产生不同的结果。所以不要认为你的结果是正确的、唯一的,一定要考虑便携性。例如,这是在FreeDOS上运行同一程序的结果:这些变量未初始化:i=0j=1074k=3120该数组未初始化:numbers[0]=3106numbers[1]=1224numbers[2]=784numbers[3]=2926numbers[4]=1224mallocanarray...这个malloc'edarray没有初始化:array[0]=3136array[1]=3136array[2]=14499array[3]=-5886array[4]=219Ok可以看出运行结果和上面几乎完全不同。因此,初始化变量会为你省去很多不必要的麻烦,也方便以后的调试。2、数组越界在计算机世界中,从0开始计数,但总有人有意无意地忘记了这一点。比如一个数组的长度是10,如果要获取最后一个元素的值,总会有人用array[10]。。。别问了,我之前写过。。。新手朋友make这种低级错误很多。让我们看看当数组越界时会发生什么。#include#includeintmain(){inti;整数[5];整数*数组;/*test1*/puts("这个数组有五个元素(0到4)");/*初始化数组*/for(i=0;i<5;i++){numbers[i]=i;}/*糟糕,这超出了数组范围:*/for(i=0;i<10;i++){printf("numbers[%d]=%d\n",i,numbers[i]);}/*测试2*/puts("mallocanarray...");array=malloc(sizeof(int)*5);if(array){puts("这个malloc数组也有五个元素(0到4)");/*初始化数组*/for(i=0;i<5;i++){array[i]=i;}/*糟糕,这超出了数组范围:*/for(i=0;i<10;i++){printf("array[%d]=%d\n",i,array[i]);}免费(数组);}/*完成*/puts("Ok");return0;}注意,程序初始化了数组numbers(0~4)所有元素的值,但是0~9元素的值被读出越界了。如您所见,前五个值是正确的,但之后您就不知道这些值是什么了:这个数组有五个元素(0到4)numbers[0]=0numbers[1]=1个数字[2]=2个数字[3]=3个数字[4]=4个数字[5]=0个数字[6]=4198512个数字[7]=0个数字[8]=1326609712个数字[9]=32764malloc一个数组...这个malloc'ed数组也有五个元素(0到4)array[0]=0array[1]=1array[2]=2array[3]=3array[4]=4array[5]=0array[6]=133441array[7]=0array[8]=0array[9]=0Ok所以大家写代码的时候一定要知道数组的边界。读这样的数据没问题。如果你写这些内存,你会直接核心转储!3、字符串溢出在C语言中,字符串是一组char值,也可以看成是一个数组。因此,您还需要避免超出范围的字符串。如果超过,则称为字符串溢出。要测试字符串溢出,一种简单的方法是使用gets函数读取数据。gets函数是非常危险的,因为它不知道它接收到的字符串中可以存储多少数据,它只是天真地从用户那里读取数据。如果用户输入的字符串很短,那很好,但如果用户输入的值比接收到的字符串长,那将是灾难性的。让我们来演示一下这个现象:#include#includeintmain(){charname[10];/*比如“北京”*/intvar1=1,var2=2;/*显示初始值*/printf("var1=%d;var2=%d\n",var1,var2);/*这很糟糕..请不要使用gets*/puts("Wheredoyoulive?");得到(名字);/*显示结束值*/printf("<%s>是长度%d\n",name,strlen(name));printf("var1=%d;var2=%d\n",var1,var2);/*完成*/puts("Ok");return0;}在这段代码中,接收数组的长度是10,所以当输入数据的长度小于10时,程序是可以正常工作的。例如输入城市北京,长度为7:var1=1;var2=2你住在哪里?北京<北京>是长度7var1=1;var2=2Ok威尔士小镇Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch是世界上名字最长的城市,这个字符串共有58个字符,远远超过了name变量中可以保留的10个字符。如果输入这个字符串,结果是程序运行内存的其他位置,如var1和var2,可能会受到影响:var1=1;var2=2Wheredoyoulive?Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch的长度为20var58=20var582003266668OkSegmentationfault(coredumped)注意var1和var2不再是它们的起始值1和2。所以我们需要使用更安全的方式来读取用户数据。例如,一个不错的选择是getline函数,它分配足够大的内存来存储用户输入,因此用户不会因输入太长的字符串而意外溢出。4.重复释放内存良好的C编程规则之一是,如果分配了内存,就必须释放它。我们可以使用malloc函数为数组和字符串申请内存,系统会开辟一块内存,返回一个指向内存起始地址的指针。内存用完后,一定要记得使用free函数释放内存,然后系统将内存标记为未使用。但是,在这个过程中,你只能调用一次free函数。如果您第二次调用free函数,它将导致意外行为并可能破坏您的程序。这是一个简单的例子:#include#includeintmain(){int*array;puts("malloc一个数组...");array=malloc(sizeof(int)*5);if(array){puts("malloc成功");puts("释放数组...");免费(数组);}puts("释放数组...");大批);puts("Ok");}运行这个程序会在第二次调用free函数时出现coredump错误:mallocanarray...mallocsucceededFreethearray...Freethearray...free():doublefreedetectedintcache2Aborted(coredumped)那么如何避免多次调用free函数呢?最简单的方法之一是将malloc和free语句放在一个函数中。如果你把malloc放在一个函数里,free放在另一个函数里,那么在使用过程中,如果逻辑设计不当,free可能会被调用多次。5.使用无效的文件指针文件是操作系统中一种非常常见的存储数据的方式。例如,您可以将程序的配置信息存储在名为config.dat的文件中。程序运行时,可以调用该文件读取配置信息。因此,从文件中读取数据的能力对所有程序员来说都很重要。但是,如果您要读取的文件不存在怎么办?在C语言中,读取一个文件,首先使用fopen函数打开文件,然后函数返回一个指向文件的流指针。如果您尝试读取的文件不存在或您的程序无法读取它,则fopen函数返回NULL。在这种情况下,我们仍然对其进行操作,会发生什么?让我们看一下:#includeintmain(){FILE*pfile;内部通道;puts("打开FILE.TXT文件...");pfile=fopen("FILE.TXT","r");/*你应该检查文件指针是否有效,但我们跳过了它*/puts("NowdisplaythecontentsofFILE.TXT...");while((ch=fgetc(pfile))!=EOF){printf("<%c>",ch);}fclose(pfile);/*完成*/puts("Ok");return0;}当你运行这个程序时,如果FILE.TXT文件不存在,pfile将返回NULL。在这种情况下,如果我们也写入pfile,会立即引起coredump:打开FILE.TXT文件...现在显示FILE.TXT的内容...Segmentationfault(coredumped)所以,我们必须Always检查文件指针是否有效。例如调用fopen打开文件后,使用if(pfile!=NULL)保证指针可用。总结无论多么有经验的程序员,都可能会出错,所以我们在写代码的时候一定要严谨严谨。但是,如果您养成一些良好的习惯并添加一些额外的代码来检查这五种类型的错误,则可以避免严重的C编程错误。以上介绍的5个常见错误,你写过哪些错误?留言与大家交流,看看谁才是虫王!最后,最近有很多朋友找我要一份Linux学习路线图,所以我结合自己的经验,利用业余时间熬夜一个月,整理了一本电子书。无论你是面试还是自我提升,相信都会对你有所帮助!免费送给大家,只求大家给我点个赞!电子书|LinuxDevelopmentLearningRoadmap也希望有小伙伴可以和我一起把这本电子书做得更完美!获得?希望老铁们来个三连击,让更多人看到这篇文章。推荐阅读:干货|程序员进阶架构师必备资源免费书单|程序员必读经典书籍清单(高清PDF版))