基于vs环境调试和优化代码的研究

简介: 基于vs环境调试和优化代码的研究

debug和release的区别

debug包含调试信息 文件通常都比release版本大

调试代码快捷键

实例1:数组越界访问

实例1代码在bebug版本下可能会死循环(不同编译器结果不同)

局部变量是在栈区上分配内存的

实例1中 i 和 arr 都是局部变量

1.栈区的默认使用:

先使用高地址处的空间 再使用低地址的空间

2.数组随着下标的增长 地址是由低到高的

根据这两条信息可以推断出 i 和 arr 在栈区上的相对位置

死循环解释:

1.i和数组arr都是局部变量 局部变量都是在栈区上使用的

且栈区是先使用高地址再使用低地址的空间 所以i会创建在比arr高的地址上

循环开始的时候 随着数组下标的增长 地址由低到高

在数组越界访问的时候 就有可能访问到i的地址 改变i的值 从而进入死循环

但在release版本下 此代码不会报错

原因:在debug模式下,变量的分配空间顺序与变量的声明顺序一致。先声明就先分配空间。

在release版本下,对数组进行了优化,会先分配数组的空间 也就是arr在高地址 i在低地址

也就不会发生数组越界访问到i的情况

解决方法:可以先声明数组然后再声明变量i

实例2:调用堆栈的使用

如图代码 main 函数 调用 test1函数 test1函数 调用test2函数

函数的调用也是在栈上开辟空间的

调用堆栈:像栈一样的形式来展示函数调用的逻辑

从顶上放元素 也先从顶上出元素

调用完函数后 从顶上开始结束函数 最后来到main函数这里

调用堆栈很好的反映了函数是如何一步一步的调用的

实例3:计算阶乘1!+2!+3!+...+n!

此代码有错 当输入n = 3时 结果本应该时 1!+2!+3! =9 但程序结果却是15

通过F10一步一步调试 原因如下:

发现当计算2!的时候 ret 的变成了2 也就是说ret的累乘效果没有取消, ret == 2的结果一直在往后的循环中累计使用,导致ret一直在变大 结果也跟着变大。

想要计算的是1!+2!+...+n!

但是此代码算成了1!+1!2!+1!2!3!+...+1!2!...n!

计算阶乘的原理: 输出1-n的数字 然后乘到一个变量(ret)上去 也就是说 影响结果因素只有输入的n的大小,变量ret只是用来接收乘积结果的。

错误的原因就在这里:ret变量的值没有重复初始化为1

通过一步步调试 很快就找到了错误的原因

此代码还有可以进行优化 :利用 ret 的累乘效果 原理:n!=(n-1)!*n

在每一次for循环结束前 及时把ret加到sum上去

实例4:模拟字符串拷贝函数strcpy()

strcpy函数功能 拷贝source(源头)字符串到 Destination(目的地) 拷贝的字符包括'\0'

模拟strcpy函数 my_strcpy

原理:把要拷贝的数组传参之后 解引用对被覆盖的数组进行赋值 判断条件为*str != '\0'

最后再把'\0'字符拷贝

由于把'\0'字符也一并拷贝过去了 printf打印的时候 遇到\0停止打印 打印结果相同

此代码有四个地方可以优化

1.

while循环条件可以改成复合赋值符的形式

2.

给非被覆盖数组const修饰

原因:用const修饰 防止数组传参的时候不小心写反了 这时候 src就是被覆盖数组 程序就会报错 因为const修饰的常变量 无法被修改

3.

assert断言

防止dest和src传过来的时候是空指针 影响拷贝

4.

返回值

拷贝完成后,将dest的首元素地址返回

完整代码如下:

最后用相同的思路模拟求字符串长度的函数strlen()

相关文章
|
2月前
|
人工智能 测试技术 开发者
大模型自动生成并运行代码的体验与优化
随着近两年大模型的不断发展,它们在各个领域展示出了惊人的能力,可以说是在各个领域到了“开花结果”的阶段。比如最近技术圈比较火的阿里云的通义千问已经可以自己写代码、跑代码了,作为开发者,我觉得这种能力不仅提高了开发效率,还推动了编程实践向更高层次的转变和发展。但是,在使用大模型自动生成代码时,我们也会面临一些挑战,其中之一是代码可能会曲解开发者的需求。那么本文就来分享一下个个人的体验以及如何优化这种情况。
134 2
大模型自动生成并运行代码的体验与优化
|
1天前
|
传感器 数据采集 数据挖掘
LabVIEW开发压电陶瓷特性测试系统
LabVIEW开发压电陶瓷特性测试系统
|
4月前
|
NoSQL IDE 开发工具
OPENJTAG调试学习(一):嵌入式软件的交叉开发系统
OPENJTAG调试学习(一):嵌入式软件的交叉开发系统
106 0
|
13天前
|
搜索推荐 编译器 开发者
应用程序的运行:原理、过程与代码实践
应用程序的运行:原理、过程与代码实践
25 1
|
2月前
|
程序员 Python
揭秘单步调试:掌握这一技能让你代码无懈可击
揭秘单步调试:掌握这一技能让你代码无懈可击
14 0
|
2月前
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(上)-2
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(上)-2
|
2月前
|
编译器 Linux C++
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(下)
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(下)
|
2月前
|
编译器 C语言 C++
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(上)-1
【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(上)-1
|
4月前
|
Shell iOS开发 MacOS
Python 自动化指南(繁琐工作自动化)第二版:附录 B:运行程序
Python 自动化指南(繁琐工作自动化)第二版:附录 B:运行程序
47 0
|
9月前
|
Java 编译器 应用服务中间件
代码开发优化细节
带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的。为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写。如果指定了一个类为final,则该类所有的方法都是final的。Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。此举能够使性能平均提高50% 。
188 2
代码开发优化细节