let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Filtered Rows" = Table.SelectRows(Source, each Text.StartsWith([firstname], "Ab")),
TopHalfRows = Number.RoundUp(Table.RowCount(#"Filtered Rows") / 2),
KeepTopHalf = Table.FirstN(#"Filtered Rows", TopHalfRows)
in
KeepTopHalf
上面的代码不应返回任何值,并且可能会打印垃圾,但是即使在 test 函数的末尾没有return语句,它也将返回“ 嘿你好”。 你能告诉我为什么它会这样吗?
答案 0 :(得分:5)
到达没有任何return语句的非空返回函数的末尾是 不确定的行为
如果此类活动处于活动状态,则应该向编译器发出警告
允许编译器假定未定义的行为是无法实现的,因此编译器可能已删除了if语句,并仅保留了不会导致未定义行为的分支
答案 1 :(得分:1)
真诚地,我很惊讶这段代码正在编译!您应该将警告视为错误。警告很重要。
我的猜测是编译器作者认为:
只要编码器的分支没有返回语句,就可能意味着她知道无法访问该分支(例如,在调用函数之前检查了参数的前提条件),因此可以删除该分支。
这可以解释为什么编译器以这种方式运行。但是对您的问题的一个更正式的答案是:“这是未定义的行为,一切都会发生”。
尝试使用gcc进行编译时,输出取决于优化级别。
答案 2 :(得分:0)
首先,您的test
函数会导致未定义的行为,因为它不会返回,因此实际上可能发生任何事情。
但是,如果我稍作推测,该代码的行为可能是这样的,因为它意外地将曾经由rv
局部变量占用的内存块解释为返回值。而那个物体恰好持有“ Hello World”。因此,尽管该函数未正确返回,但是可以将堆栈和堆上的内存解释为本地变量是结果。
不过,这只是一个猜测,按照C ++标准,这只是未定义的行为,这意味着您知道自己在做什么,并且永远不要执行该返回路径或代码是错误的。
答案 3 :(得分:0)
正如泰克所说,此代码会导致不确定的行为,这意味着一切都会发生。
最有可能是由优化器引起的,该优化器假定每个执行分支都返回一些值,并基于此优化最终代码。您应该尝试关闭优化功能,但是请注意,每个编译器都可以生成完全不同的结果。
看看这个函数的反汇编(clang 4.0,-O3)(我用char *代替了std :: string):
test: # @test
mov eax, .L.str.1
cmp rdi, rax
je .LBB0_2 // <<-- IF .L.str.1 == "", goto .LBB0_2
// CALL PRINTF
push rax
mov edi, .L.str.2
xor eax, eax
call printf
add rsp, 8
// END OF CALL PRINTF
.LBB0_2:
mov eax, .L.str // <<--- RETURN .L.str to the caller (optimization!)
ret
.L.str:
.asciz "Hey Hello"
.L.str.1:
.zero 1
达到.LBB0_2标签,无论是否生成语句,因此在每种情况下都将返回它。