【今日问题】变量未初始化引起的崩溃

时间:2022-05-04
本文章向大家介绍【今日问题】变量未初始化引起的崩溃,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

昨天写的今日问题,有小伙伴给我反馈,觉得挺有用,小编今天继续给小伙伴们总结遇到的常见问题

一、初学者经常由于没有养成良好的编程习惯,未初始化变量会引起那些问题

使用未初始化的变量是常见的程序错误,通常也是难以发现的错误。虽然许多编译器都至少会提醒不要使用未初始化变量,但是编译器并未被要求去检测未初始化变量的使用。而且,没有一个编译器能检测出所有未初始化变量的使用。

现象列举:

1、引起程序运行时突然崩溃

  这种结果已近是相当好了,至少你可以发现程序崩溃的位置,及时的修正问题

2、程序运行成功但是结果错了,这种还是比较好查的分析错误原因费点时间

3、程序在不同的机器上运行的结果不一致查找问题那就难上加难了

原因分析:

未初始化的变量事实上都有一个值。编译器把该变量放到内存中的某个位置,而把这个位置的无论哪个位模式当做是变量初始的状态。当被解释成整型值时,任何为模式都是合法的值——虽然我这个值不可能是程序员想要的,因为这个值合法,所以使用它不可能会导致程序崩溃。可能的结果是导致程序错误执行或者错误计算。

建议:

建议每一个内置类型的对象都要初始化。虽然这样做并不总是必须的,但是会更加容易和安全,除非你确定忽略初始化是不会带来风险。

二、初学者误认为宏定义函数和普通函数一致

函数式宏定义:#define MAX(a,b) ((a)>(b)?(a):(b))

普通函数 : MAX(a,b) { return a>b?a:b;}

两者区别:

1、函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。

2、调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。如果MAX是个普通函数,那么它的函数体return a > b ? a : b; 要编译生成指令,代码中出现的每次调用也要编译生成传参指令和call指令。而如果MAX是个函数式宏定义,这个宏定义本身倒不必编译生成指令,但是代码中出现的每次调用编译生成的指令都相当于一个函数体,而不是简单的几条传参指令和call指令。所以,使用函数式宏定义编译生成的目标文件会比较大。

3、函数式宏定义要注意格式,尤其是括号。

  如果上面的函数式宏定义写成 #define MAX(a, b) (a>b?a:b),省去内层括号,a=1+x, b=2+y;则宏展开就成了1+x>2+y?1+x:2+y ,运算的优先级就错了。同样道理,这个宏定义的外层括号也是不能省的。若函数中是宏替换为 ++MAX(a,b),则宏展开就成了 ++(a)>(b)?(a):(b),运算优先级也是错了。

4、若函数参数为表达式,则普通函数的调用与函数式宏定义的替换过程是不一样的。

  普通函数调用时先求实参表达式的值再传给形参,例如MAX(++a, ++b),如果MAX是普通函数,a和b只增加一次。但如果MAX函数式宏定义,则要展开成k = ((++a)>(++b)?(++a):(++b)),a和b就不一定是增加一次还是两次了。所以若参数是表达式,替换函数式宏定义时一定要仔细看好。

总结:

尽管函数式宏定义和普通函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。

如果还觉得回答不够彻底就直接加群问小编或者在公众号直接留言。有想让小编总结的话题,都可以直接留言

课后小作业:

#define f(a, b, x)  a*x+b

printf("%d, %dn", f(3,2,1), f(6, 5, f(3, 2, 1)));