[英]Why is the use of unrelated printf statement causing changes in my program output?
我陷入一个程序,其中只有一个printf
语句会导致输出更改。
我有n
元素的数组。 对于每d
个连续元素的median
,如果第(d+1)th element
大于或等于它的两倍(中位数),则我将增大notifications
的值。 完整的问题陈述可以在这里参考 。
这是我的程序:
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#define RANGE 200
float find_median(int *freq, int *ar, int i, int d) {
int *count = (int *)calloc(sizeof(int), RANGE + 1);
for (int j = 0; j <= RANGE; j++) {
count[j] = freq[j];
}
for (int j = 1; j <= RANGE; j++) {
count[j] += count[j - 1];
}
int *arr = (int *)malloc(sizeof(int) * d);
float median;
for (int j = i; j < i + d; j++) {
int index = count[ar[j]] - 1;
arr[index] = ar[j];
count[ar[j]]--;
if (index == d / 2) {
if (d % 2 == 0) {
median = (float)(arr[index] + arr[index - 1]) / 2;
} else {
median = arr[index];
}
break;
}
}
free(count);
free(arr);
return median;
}
int main() {
int n, d;
scanf("%d %d", &n, &d);
int *arr = malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
scanf("%i", &arr[i]);
}
int *freq = (int *)calloc(sizeof(int), RANGE + 1);
int notifications = 0;
if (d < n) {
for (int i = 0; i < d; i++)
freq[arr[i]]++;
for (int i = 0; i < n - d; i++) {
float median = find_median(freq, arr, i, d); /* Count sorts the arr elements in the range i to i+d-1 and returns the median */
if (arr[i + d] >= 2 * median) { /* If the (i+d)th element is greater or equals to twice the median, increments notifications*/
printf("X");
notifications++;
}
freq[arr[i]]--;
freq[arr[i + d]]++;
}
}
printf("%d", notifications);
return 0;
}
现在,对于像这样的大输入,程序将输出936
作为notifications
值,而当我仅排除语句printf("X")
,程序将输出1027
作为notifications
值。 我真的无法理解是什么导致了我的程序中的这种行为,以及我缺少/监督了什么。
您的程序在这里具有未定义的行为:
for (int j = 0; j <= RANGE; j++) {
count[j] += count[j - 1];
}
您应该从j = 1
开始循环。 按照编码,您可以在数组count
的开始之前访问内存,这可能导致崩溃或产生不可预测的值。 在运行环境中更改任何内容都可能导致不同的行为。 实际上,即使不进行任何更改也无法实现。
其余代码很难一眼就能理解,但是考虑到索引值的计算,那里可能还会有更多问题。
对于初学者,您应该添加一些一致性检查:
scanf()
的返回值以确保正确的转换。 arr
的值,它们必须在0..RANGE
的范围内 int index = count[ar[j]] - 1;
永远不会产生负数。 count[ar[j]]--;
median = (float)(arr[index] + arr[index - 1]) / 2;
永远不会使用index == 0
进行评估。 您的程序有未定义的行为 ( 几次 )。 您确实应该被吓到 (而且还不够被吓到)。
我真的不明白是什么导致了我的程序中的这种行为
对于UB,这个问题毫无意义 。 您需要深入研究实现细节(例如,研究程序生成的机器代码以及C编译器和标准库的代码),以了解更多内容。 您可能不想这样做(可能需要花费很多年的时间)。
请尽快阅读Lattner的博客,其中有关每个C程序员应了解的未定义行为
我所缺少/监督的内容。
您对UB的了解不够深。 请注意,编程语言是一种规范(以及针对它的代码),而不是软件(例如,编译器)。 程序语义很重要。
正如我在评论中所说:
编译所有警告和调试信息( gcc -Wall -Wextra -g
和GCC )
改进您的代码以不发出警告; 也许也可以尝试使用另一种编译器,例如Clang,并且也不要从中获得任何警告(因为不同的编译器会给出不同的警告)。
考虑更多关于您的程序及其内部不变式的信息 。
使用调试器( gdb
),尤其是与watchpoints配合使用,以了解您的进程的内部状态; 并且有几个测试用例可以在调试器下运行,而没有调试器。
使用检测工具,例如GCC的address sanitizer -fsanitize=address
和valgrind之类的工具。
使用橡皮鸭调试方法
阅读有关编程(例如SICP )和C编程语言的更多信息。 下载并研究C11编程语言规范n1570 (并非常注意其中的UB)。 仔细阅读所使用的每个标准或外部功能的文档 。 还研究编译器和其他工具的文档 。 处理错误和失败的情况(例如calloc和scanf可能失败)。
调试是困难的 (例如,由于停止问题 , Heisenbugs等),但是有时很有趣且具有挑战性。 您可以花费数周的时间来发现一个错误。 而且,如果不深入研究实现细节(研究由编译器生成的机器代码,研究编译器的代码),通常就无法理解错误程序的行为。
PS。 您的问题显示出错误的心态-应该改善-对UB的误解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.