草庐IT

c++ - 初始化 int 影响函数返回值

coder 2024-02-14 原文

很抱歉这个问题的标题含糊不清,但我不确定如何准确地提出这个问题。

以下代码在 Arduino 微处理器(为 ATMega328 微处理器编译的 c++)上执行时运行良好。返回值显示在代码的注释中:

// Return the index of the first semicolon in a string
int detectSemicolon(const char* str) {

    int i = 0;

    Serial.print("i = ");
    Serial.println(i); // prints "i = 0"

    while (i <= strlen(str)) {
        if (str[i] == ';') {
            Serial.print("Found at i = ");
            Serial.println(i); // prints "Found at i = 2"
            return i;
        }
        i++;
    }

    Serial.println("Error"); // Does not execute
    return -999;
}

void main() {
    Serial.begin(250000);
    Serial.println(detectSemicolon("TE;ST")); // Prints "2"
}

如预期的那样,这会输出“2”作为第一个分号的位置。

但是,如果我将 detectSemicolon 函数的第一行更改为 int i; 即没有显式初始化,我会遇到问题。具体来说,输出是“i = 0”(好)、“Found at i = 2”(好)、“-999”(坏!)。

因此,尽管在 return 2; 行之前立即执行了 print 语句,并且尽管从未在 return -999; 之前立即执行 print 语句,但该函数仍返回 -999 > 线。

有人可以帮助我了解这里发生的事情吗?我知道 c 函数中的变量理论上可以包含任何旧的垃圾,除非它们被初始化,但在这里我专门检查打印语句这还没有发生,但是......


编辑:感谢所有参与的人,特别是感谢 underscore_d 的出色回答。似乎未定义的行为确实导致编译器跳过任何涉及 i 的内容。下面是一些在 detectSemicolon 中注释掉了 serial.prints 的程序集:

void setup() {
    Serial.begin(250000);
    Serial.println(detectSemicolon("TE;ST")); // Prints "2"
  d0:   4a e0           ldi r20, 0x0A   ; 10
  d2:   50 e0           ldi r21, 0x00   ; 0
  d4:   69 e1           ldi r22, 0x19   ; 25
  d6:   7c ef           ldi r23, 0xFC   ; 252
  d8:   82 e2           ldi r24, 0x22   ; 34
  da:   91 e0           ldi r25, 0x01   ; 1
  dc:   0c 94 3d 03     jmp 0x67a   ; 0x67a <_ZN5Print7printlnEii>

看起来编译器实际上完全无视 while 循环并断定输出将始终为“-999”,因此它甚至不去调用函数,而是硬编码 0xFC19。我将在启用 serial.prints 的情况下再看一眼,以便仍会调用该函数,但我认为这是一个强有力的指针。


编辑 2:

对于那些真正关心的人,这里有一个完全如上所示(在 UB 情况下)的反汇编代码的链接:

https://justpaste.it/vwu8

如果仔细观察,编译器似乎将寄存器 28 指定为 i 的位置,并在 d8 行将其“初始化”为零。在 while 循环、if 语句等中,该寄存器被视为包含 i,这就是为什么代码看起来有效并且 print 语句按预期输出的原因(例如第 122 行,其中“i”得到递增)。

然而,当谈到返回这个伪变量时,这对我们久经考验的编译器来说太过分了;它画线,并将我们转储到另一个返回语句(第 120 行跳转到第 132 行,在返回 main() 之前将“-999”加载到寄存器 24 和 25 中)。

或者至少,这是我对汇编的有限掌握所能达到的程度。这个故事的寓意是当您的代码行为未定义时会发生奇怪的事情。

最佳答案

与所有非static 存储持续时间的基本类型一样,声明但不定义int 不会导致默认初始化。它使变量未初始化。那意味着 i 只是保存一个随机值。它没有(已知的、有效的)值,因此您还不能阅读它。

这是来自 C++11 标准的相关引用,来自评论中的 Angew。这不是新的限制,从那以后也没有改变:

C++11 4.1/1, talking about an lvalue-to-rvalue conversion (basically reading a variable's value): "If the object to which the glvalue refers is ... uninitialized, a program that necessitates this conversion has undefined behavior."

任何对单元化变量的读取都会导致未定义的行为,因此任何事情都可能发生。编译器可以让它做任何事情,而不是你的程序使用一些未知的默认值继续按预期运行,因为行为是未定义的,并且标准对在这种情况下应该发生什么没有强加要求.

实际上,这通常意味着优化编译器可能会简单地删除任何以任何方式依赖 UB 的代码。没有办法就做什么做出正确的决定,所以决定什么都不做是完全有效的(这恰好也是对大小和速度的优化)。或者正如评论者所提到的,它可能会保留代码,但会用手头最近的不相关值或不同语句中的不同常量等替换读取 i 的尝试。

打印变量并不像您想象的那样算作“检查它”,所以这没有区别。没有办法“检查”一个未初始化的变量,从而使自己免受 UB 的侵害。读取变量的行为仅在程序已经写入特定值时才定义。

我们没有必要推测为什么会出现特定的任意类型的 UB:您只需要修复您的代码,使其确定性地运行。

你为什么要使用未初始化的呢?这只是“学术”吗?

关于c++ - 初始化 int 影响函数返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38149969/

有关c++ - 初始化 int 影响函数返回值的更多相关文章

  1. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  2. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  5. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  7. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  8. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  9. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  10. ruby - Ruby 中的隐式返回值是怎么回事? - 2

    所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

随机推荐