为什么C语言程序不可以运行,难以捉摸的C语言程序,if条件明明为假,为什么还是执行了呢?...

2024-03-26 14:59

本文主要是介绍为什么C语言程序不可以运行,难以捉摸的C语言程序,if条件明明为假,为什么还是执行了呢?...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

昨天浏览外文论坛帖子,发现一个有趣的问题。这个问题产生的原因是程序员编写C语言代码不规范造成的,这也是很多初学者容易犯的错——只关注核心功能,而不关注细节。

739588af80d506096fad453cd15e4470.png问题是这样的

问题

小明在源文件 f1.c 里定义了一个布尔函数,相关的C语言代码是下面这样的:

#include

bool f1(){

int var1 =1000;

int var2 =2000;

int var3 = var1 + var2;

return(var3 ==0)? true : false;

}

e8dbf5a1726bbbcd5b78ebca46a8bb39.pngf1()函数的C语言代码

显然,函数 f1() 执行后,变量 var3 的值等于 3000,因此必定会返回 false。但是小明在 main.c 文件里编写 main() 函数调用 f1() 后,发现结果似乎有些奇怪,main() 函数的C语言代码如下:

#include

#include

intmain(){

printf(f1()== true ?"true\n":"false\n");

if(f1()){

printf("executed\n");

}

return0;

}

22ebb74b2c3870755e86a6826cfe57c8.pngmain()函数的C语言代码

按理说,既然 f1() 函数总是返回 false,上述 main() 函数被编译执行后,应该只会输出“false”才对,但是小明得到的实际执行结果却是下面这样的:

$ gcc main.c f1.c -o test

$ ./test

false

executed

这是怎么回事呢?小明使用的编译器版本为 gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2。

分析

仔细观察小明的编译命令gcc main.c f1.c -o test,应该能够发现他并没有指定任何别的编译选项,因此 gcc 编译器默认实现的是 1990 年(很老了)的 C90 标准。

a77fe0b429dde5a2b6301134feb151da.pnggcc 编译器默认实现的是 C90 标准

C90 标准在处理C语言代码时,有一个主要缺点就是:如果某段C语言代码调用函数时,程序员未能实现指定被调用函数原型,那么将使用默认的函数原型,即默认被调用函数的原型为:

int func();

也即默认被调用函数可以接收任意多的参数,并且返回值类型为 int。这样的默认规则在处理函数返回值时可能会有一个转换过程(当被调用函数原型返回值不是 int 类型时),但是它并不会修改实际的函数实现。

现在小明遇到的问题就清楚了:f1() 函数原本的返回值类型为 bool 型,但是他在编写 main() 函数时,并未事先指定 f1() 函数的原型,因此编译器默认将其当作是 int 返回值类型了。

而 bool 型和 int 型的 size 并不一致,所以小明编写的C语言程序行为就属于“未定义”的了,出现什么样的结果都是不足为奇的。

7558da4d3d8dac7c8f1a8071413e6b48.png出现什么样的结果都是不足为奇的

能够看出,C90 标准在遇到未知函数原型时,会默认将其当作 int func(); 原型的特性其实是一种危险的特性,因此,从 C99 标准开始,这样的特性就被禁止了。

但是不幸的是,直到 5.x.x 版本的 gcc 默认属性仍然是较老的 C90 标准,这可能是为了兼容之前的C语言代码。要解决小明遇到的问题也是简单的,只需要告诉 gcc 编译器希望使用的标准就可以了。例如:

gcc -std=c11 -pedantic-errors -Wall -Wextra-std=c11 用于告诉编译器遵循 C11 标准。-pedantic-errors 告诉编译器全心全意的编译C语言代码,一旦发现错误就给出错误提示。-Wall 可以尽可能的让编译器发现一些不规范的代码,并给出相应的警告提示。-Wextra 类似于 -Wall 选项,它能够让编译器发现更多不规范的代码。使用上述命令编译小明的C语言代码,应该会发现此时编译器不再能够完成编译了,而是给出了错误提示:“implicit declaration of function ‘f1’”:

52ce63a8bdbb325e8172028e13732ffb.png错误提示

此时,编译器不再“猜测”f1() 函数的原型,而是强制我们在调用函数前指定其原型。因此对小明的C语言代码稍作修改,如下:

cd60fe97145d43e32c43c817fc981f8a.png请点强制我们在调用函数前指定其原型击

此时再编译执行,发现一起符合预期了:

# gcc -std=c11 -pedantic-errors -Wall -Wextra main.c f1.c -o test

# ./test

false

小结

很多C语言初学者觉得细节不重要,重要的是核心算法或者代码,这样的心态其实很危险,很容易写出难以捉摸的程序,本节就是一个例子。事实上,越是初学者就越应该严格遵守规范,这样才能尽可能的避免出现奇怪的,难以捉摸的结果,打击自己的学习信心。

7b4cb9ee83cb2031bb49272b63a3ba0a.png

点个赞再走吧

欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。

未经许可,禁止转载。

举报/反馈

这篇关于为什么C语言程序不可以运行,难以捉摸的C语言程序,if条件明明为假,为什么还是执行了呢?...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/848889

相关文章

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

java中ssh2执行多条命令的四种方法

《java中ssh2执行多条命令的四种方法》本文主要介绍了java中ssh2执行多条命令的四种方法,包括分号分隔、管道分隔、EOF块、脚本调用,可确保环境配置生效,提升操作效率,具有一定的参考价值,感... 目录1 使用分号隔开2 使用管道符号隔开3 使用写EOF的方式4 使用脚本的方式大家平时有没有遇到自

mybatis直接执行完整sql及踩坑解决

《mybatis直接执行完整sql及踩坑解决》MyBatis可通过select标签执行动态SQL,DQL用ListLinkedHashMap接收结果,DML用int处理,注意防御SQL注入,优先使用#... 目录myBATiFBNZQs直接执行完整sql及踩坑select语句采用count、insert、u

java程序远程debug原理与配置全过程

《java程序远程debug原理与配置全过程》文章介绍了Java远程调试的JPDA体系,包含JVMTI监控JVM、JDWP传输调试命令、JDI提供调试接口,通过-Xdebug、-Xrunjdwp参数配... 目录背景组成模块间联系IBM对三个模块的详细介绍编程使用总结背景日常工作中,每个程序员都会遇到bu

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)

《uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)》在uni-app开发中,文件上传和图片处理是很常见的需求,但也经常会遇到各种问题,下面:本文主要介绍uni-app小程序项目中实... 目录方式一:使用<canvas>实现图片压缩(推荐,兼容性好)示例代码(小程序平台):方式二:使用uni

一个Java的main方法在JVM中的执行流程示例详解

《一个Java的main方法在JVM中的执行流程示例详解》main方法是Java程序的入口点,程序从这里开始执行,:本文主要介绍一个Java的main方法在JVM中执行流程的相关资料,文中通过代码... 目录第一阶段:加载 (Loading)第二阶段:链接 (Linking)第三阶段:初始化 (Initia

python语言中的常用容器(集合)示例详解

《python语言中的常用容器(集合)示例详解》Python集合是一种无序且不重复的数据容器,它可以存储任意类型的对象,包括数字、字符串、元组等,下面:本文主要介绍python语言中常用容器(集合... 目录1.核心内置容器1. 列表2. 元组3. 集合4. 冻结集合5. 字典2.collections模块

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求: