longger 发表于 2024-1-12 10:59:46

记一次开发踩坑,一个玄学的现象

本帖最后由 longger 于 2024-1-12 11:01 编辑

记一次开发踩坑
事情的起因是,我们从一家芯片厂家获取了一套代码,这套代码是厂商为推广他们的芯片提供的。这套代码要应用到我们的产品中还需要大量的修改,在我理清了这套代码逻辑后开始了修改之路。经过一顿操作,消灭了各种错误警告之后也算基本完成了。不出意外意外还是发生了,有问题!

然后到了各种排查调试的阶段,像这种编译器查不到的问题最难搞定,尤其是这套代码还是很复杂的。我排查了每一处修改,在我看来都没问题,翻来覆去查了两天。我觉得可能是某个运算逻辑被我改乱了,但是死活找不到在哪。经过苦苦挣扎我选择重新再来一次。

然后有一个函数引起了我的注意。
int32_t hall_rotor_location_interpolation(int32_t);                函数的声明↑int32_t hall_rotor_location_interpolation(int32_t phase_pre){    float phase_diff = 0;    uint16_t phase_diff_set = 10;    int32_t phase_temp;     phase_diff = (float)NUMBER_OF_PAIRS * hall.speed * 360 / (60 * frequency_now) * 100;     if ((MC_STATE_RUNNING == state) && (hall.speed < 500))    {       phase_diff = phase_diff_set;    }     if (DIRECTION_CW == direction)    {       phase_temp -= (phase_diff);    }    else    {       phase_temp += (phase_diff);    }     if (phase_temp > 36000)    {       phase_temp -= 36000;    }    else if (phase_temp < 0)    {       phase_temp += 36000;    }     return (phase_temp);}      函数的定义↑在第一修改时我发现这个函数的形参 phase_pre 并没有被使用,于是选删除了。当我又一次看这个函数时发现了不对,这个函数没有使用 传入参数, 而是主要在使用局部变量 int32_t phase_temp; 这个变量并没有被赋予初始值,然而在后续的逻辑代码中却直接使用了这个变量,从逻辑代码中不难看出这肯定是存在问题的,因为phase_temp的值是不确定的。于是我想 这个局部变量初始值应该是传入参数的值,即int32_t phase_temp = phase_pre 于是我做了这样的修改问题也就解决了。
但是这个函数我却看不懂了,为什么局部变量初始值会等于形参的值呢?从代码上看二者没有任何联系。hall.phase = hall_rotor_location_interpolation(hall.phase);      这是函数的调用↑                   keil调试结果我们从keil的调试结果里可以看到他们的值确实是相同的。在这里向大佬请教一下这是什么原理。我尝试模仿但都不存在这个现象。 观察这个函数,可以看出应该这样写 int32_t phase_temp = phase_pre; 或者直接使用phase_pre。如果说原理上作者这样的写法是可以的,那就很难不让人认为这是作者故意在挖坑。故意复杂化代码,让使用者掉尽头发,这种行为实属可恶。如果C语言语法上不允许这样做,作者也无意这样做,只是忘了让phase_temp = phase_pre 。程序又巧合的能运行起来,这确实很搞人心态,程序开始玄学了起来。

longger 发表于 2024-1-12 11:03:04

如果这个是语言特性,有没有大佬来解答一下。谢谢!

skyshine 发表于 2024-1-12 11:52:19

本帖最后由 skyshine 于 2024-1-12 15:05 编辑

源码里这个地方是 int32_t phase_temp = phase_pre;
2021-08-16, V1.3.0, FOC demo

Edmund1964 发表于 2024-1-12 12:29:48

现在的编译器对代码优化的程度很高, 很多时候编译后就跟你原代码的处理先后不一样, 特别是针对局部变量, 所以企图用Watch查看局部变量在过程中的值很多时候就不符合源代码的运行逻辑。
简单而言就是编译器只针对你代码的结果, 过程并不一定会按你代码的方式运算。

解决方式, 把局部变量暂时搬到函数外,并加上volatile(就是暂时让它成了全局的),用watch也能实时查看变量,这时候编就会百分百的按你的编程逻辑跑了。

skyshine 发表于 2024-1-12 13:54:42

按楼主的描述来看,调试里结果是正常的,但是没法复现,实际运行结果也是有问题的,大概率是巧合,变量初始值正好是其他函数用到的相同局部变量值。

longger 发表于 2024-1-12 13:59:51

skyshine 发表于 2024-1-12 11:52
github搜了下,看到别人上传的源码了,源码里这个地方是 int32_t phase_temp = phase_pre;
你这份代码可能 ...

啊,我手上这份是签了保密协议的,没想到GitHub能搜到,这份是 8-16 V1.3.0.邪门了

longger 发表于 2024-1-12 14:00:34

Edmund1964 发表于 2024-1-12 12:29
现在的编译器对代码优化的程度很高, 很多时候编译后就跟你原代码的处理先后不一样, 特别是针对局部变量, ...

编译器优化关了

longger 发表于 2024-1-12 14:02:28

skyshine 发表于 2024-1-12 11:52
github搜了下,看到别人上传的源码了,源码里这个地方是 int32_t phase_temp = phase_pre;
你这份代码可能 ...

老哥用啥关键词搜的

skyshine 发表于 2024-1-12 14:20:20

#include <stdio.h>
int test1(int test)
{
        int b=321;
        return b;
}
int test2(int test)
{
        int c;
        return c;
}

int main()
{
        test1(789);
        int a=345;
        a=test2(123);
        printf("%d",a);
      return 0;
}

这里test2打印出来的结果就是test1函数里局部变量的值。如果把test1函数和test2函数调换位置,打印的值又不同了。正常情况下局部变量初始化都要赋值,不然大概率就是之前某个函数的局部变量的值。

skyshine 发表于 2024-1-12 14:51:03

longger 发表于 2024-1-12 14:02
老哥用啥关键词搜的

别搜了,估计是忘了设置成私人仓库泄露出来的

skyshine 发表于 2024-1-12 15:13:50

站长能把github相关的讨论编辑一下吗,怕引起不必要的麻烦

fyyxxm 发表于 2024-1-12 15:33:20

形参就是个临时变量,你用个临时变量给他,编译器就直接省略了你定义的临时变量。没必要搞两个。一样也很正常
页: [1]
查看完整版本: 记一次开发踩坑,一个玄学的现象