x86-64 GCC 13.1下C++异常:编译器判定if条件恒为真
编译器判定条件恒为真的原因分析
问题现象
- 两段逻辑看似等价的代码出现截然不同的运行结果:
- 使用
if(0 <= start)(其中start = j - wordDict[i].size() + 1)时,代码运行正常 - 直接使用
if(0 <= j - wordDict[i].size() + 1)时,程序抛出错误:"basic_string::substr: __pos (which is 18446744073709551613) > this->size() (which is 8)"
- 使用
- 查看汇编代码发现,编译器认为后者的
if条件恒为真,不会执行跳转分支,导致后续substr调用传入非法参数触发越界。
核心原因
这是C++隐式类型转换导致的无符号整数溢出问题:
wordDict[i].size()返回的是size_t类型(无符号整数),而循环变量j是int类型(有符号整数)- 执行
j - wordDict[i].size() + 1时,C++会将j隐式转换为size_t类型参与运算 - 当
wordDict[i].size() > j + 1时,无符号整数的溢出绕回规则会让运算结果变成一个极大的正整数(比如报错中的18446744073709551613) - 无符号整数与0比较时,
0 <= 极大无符号数永远为真,因此编译器会优化掉分支判断,直接执行后续代码,最终触发substr越界错误
而使用int start = ...的写法时,运算结果会被强制转换为int(有符号整数),此时负数值会被正确保留,0 <= start会判断为假,跳过危险分支。
修复方案
- 方案1:显式将
size()结果转为有符号整数:if (0 <= j - static_cast<int>(wordDict[i].size()) + 1) - 方案2:保留先计算
start再判断的写法(原正常逻辑) - 方案3:调整比较逻辑,避免无符号溢出:
此写法中if (j + 1 >= wordDict[i].size())j会被转为size_t,但逻辑等价且不会出现溢出绕回的问题
修复后的完整代码示例
#include <string> #include <vector> #include <unordered_set> using namespace std; bool wordBreak(string s, vector<string>& wordDict) { vector<bool> dp(s.size(), false); unordered_set<string> all(wordDict.begin(), wordDict.end()); for (int j = 0; j < s.size(); j++) { for (int i = 0; i < wordDict.size(); i++) { int len = static_cast<int>(wordDict[i].size()); int start = j - len + 1; if (0 <= start) { string word = s.substr(start, len); if (all.find(word) != all.end() && (start == 0 || dp[j - len])) { dp[j] = true; } } } } return dp[s.size()-1]; } int main(int args, char * argv[]) { string s = "leetcode"; vector<string> wordDict = {"leet","code"}; wordBreak (s, wordDict); return 0; }
内容的提问来源于stack exchange,提问作者Cyokeo
相关产品推荐
相关产品推荐

