当我注意到如果按以下方式实现我的函数无法正常工作时,我正在实现插入排序的版本。这个版本应该在元素被复制到新数组中时对元素进行排序,同时保持原始元素不变。
vector<int> insertionSort(vector<int>& heights) {
vector<int> expected(heights.size(), 0);
int j, key;
for(int i = 0; i < expected.size(); i++){
expected[i] = heights[i];
j = i-1;
key = expected[i];
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j--];
}
expected[j+1] = key;
}
return expected;
}
我注意到在执行预期[j--]时,该功能无法正常工作,但是当我在括号外递减时,它工作正常。 换句话说,两者之间有什么区别
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j--];
}
和
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j];
--j;
}
要回答这个问题,我们需要看看要expected[j+1] = expected[j--];
的参数的计算顺序。查看 cppreference 的评估顺序页面,我们看到以下内容适用于 C++17 及更高版本:
在每个简单的赋值表达式
- 排序的
E1 = E2
和每个复合赋值表达式E1 @= E2
中,每个值计算和E2
的副作用都是在每次值计算和副作用之前E1
在您的情况下,这意味着expected[j--]
的每个值计算和副作用都是在开始评估expected[j+1]
之前计算的。特别是,这意味着j+1
将基于j
在你用j--
递减后的值,而不是它之前的值。
在C++17之前,不确定分配操作的左侧还是右侧是先排序的。这意味着在 C++14 及更早版本中,您的代码表现出未定义的行为:
- 如果相对于使用同一内存位置中任何对象的值进行的值计算,则对内存位置的副作用是未排序的,则行为是未定义的。
在这种情况下,j
"内存位置",并且相对于值计算j+1
,j--
递减是未排序的。这与 cppreference 的未定义行为示例非常相似:
a[i] = i++; // undefined behavior until C++17
在代码的第二个版本中,在分配完成之前不会递减到j
。