C语言数组排序求最值出现元素偏移1位问题排查
问题描述
使用C语言实现数组最值查找时,采用相邻元素两两交换的冒泡排序思路对数组做升序排列,计划直接取排序后数组首尾元素作为最值,但实际运行时出现疑似数组整体偏移1位的异常:
- 以初始数组
20, 10, 35, 30, 7(共5个元素,合法下标范围0~4)为例,预期排序后下标0存最小值7、下标4存最大值35,输出value[0]和value[4]即可得到正确结果 - 实际运行时必须读取下标1和下标5的元素才能拿到正确最值,不符合数组下标访问的预期
原题要求:首先输入数字N确定待输入的数字个数,随后输入N个数字,输出所输入N个数字中的最小值和最大值。
对应实现代码如下:
#include<stdio.h> #pragma warning(disable:4996) int main(void) { int input, i, j, temp, k; int value[100] = { 0 }; scanf("%d", &input); for (i = 0; i < input; i++) { scanf("%d", &value[i]); } for (k = 0; k < input; k++) { for (j = 0; j < input; j++) { if (value[j] > value[j + 1]) { temp = value[j + 1]; value[j + 1] = value[j]; value[j] = temp; } } } printf("%d %d\n", value[0], value[input-1]); return 0; }
问题原因
不存在所谓的“元素偏移1位”,核心问题是内层循环边界设置错误导致数组越界访问,越界写入破坏了内存数据,才出现了不符合预期的表现:
- 定义的
value数组长度为100,初始化时所有元素被置为0,输入N个有效数字后,有效元素仅存储在下标0 ~ N-1的位置 - 内层冒泡循环的终止条件写为
j < input,当j取到input-1(最后一个有效元素的下标)时,代码会访问value[j+1]也就是value[input],这个位置不属于存入有效元素的范围,存储的是初始化留下的0 - 每一轮冒泡排序的最后一次比较,都会拿最后一个有效元素和它后面的0做比较交换:以5个元素的测试用例为例,第一轮排序j=4时,会比较
value[4](第一轮结束后该位置值为35)和value[5](初始值0),因35>0触发交换,把0换到value[4]、35写入value[5]。多轮排序后,原本应该在value[0]位置的最小值7会被一步步交换到value[1],最大值35会被交换到value[5],就出现了需要取下标1和5才能拿到正确值的现象。 - 这类越界访问属于C语言的未定义行为,不同编译环境下可能表现出不同的异常,不会固定出现“偏移1位”的表现。
修复方法
修改内层循环边界即可,每轮比较只需要遍历到当前未排序段的倒数第二个元素,保证j+1始终在有效下标范围内。可以直接把内层循环条件改为j < input - 1,如果要做冒泡排序的常规优化(每轮排序后末尾k个元素已经是有序的最大值,不需要重复比较),可以写成j < input - 1 -k:
for (k = 0; k < input; k++) { // 修正循环边界,避免j+1越界 for (j = 0; j < input - 1 - k; j++) { if (value[j] > value[j + 1]) { temp = value[j + 1]; value[j + 1] = value[j]; value[j] = temp; } } }
内容的提问来源于stack exchange,提问作者JERry
相关产品推荐
相关产品推荐

