为什么使用不相关的printf语句会导致程序输出发生变化?

时间:2017-09-25 15:56:50

标签: c printf

我坚持使用只有printf语句导致输出发生变化的程序。

我有一组n个元素。对于每个median个连续元素的d,如果(d+1)th element大于或等于它的两倍(中位数),我会增加{{1}的值}。完整的问题陈述可能会被引用here

这是我的计划:

notifications

现在,对于像this这样的大型输入,程序输出#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时,程序输出{{1} } {作为printf("X")的值。 我真的无法理解在我的程序中导致此行为的原因,以及我缺少/监督的内容。

2 个答案:

答案 0 :(得分:6)

您的程序在此处有未定义的行为:

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

答案 1 :(得分:3)

您的计划有undefined behavior几个次)。你真的应该是scared(你并不害怕)。

  

我真的无法理解在我的程序中导致此行为的原因

使用UB,这个问题毫无意义。您需要深入了解实现细节(例如,研究程序生成的机器代码,以及C编译器和标准库的代码)以了解更多信息。你可能不想这样做(可能需要多年的工作)。

请尽快阅读Lattner的博客What Every C Programmer Should Know on Undefined Behavior

  

我缺少/监督。

你不太了解UB。请注意,编程语言是规范(以及针对它的代码),而不是软件(例如您的编译器)。计划semantics很重要。

正如我在评论中所说:

  • 使用所有警告和调试信息进行编译(gcc -Wall -Wextra -gGCC

  • 改进您的代码以获取警告;也许还可以尝试另一个编译器,如Clang,并努力不从中获取任何警告(因为不同的编译器会给出不同的警告)。

  • 考虑使用version control之类的git系统来保留代码的各种变体,以及一些build automation工具。

  • 详细了解您的计划及其中的invariants

  • 使用调试器(gdb),尤其是watchpoints,来了解流程的内部状态;并且有几个test个案例可以在调试器下运行而没有它。

  • 使用instrumentation设施,例如GCC的地址清理工具-fsanitize=addressvalgrind等工具。

  • 使用rubber duck debugging方法

  • 有时会考虑使用static source code analysis工具(例如Frama-C)。他们需要使用专业知识,和/或给出许多误报。

  • 详细了解编程(例如SICP)和C编程语言。下载并研究C11编程语言规范n1570(并且在每次提及UB时要非常小心)。仔细阅读您正在使用的每个标准或外部功能的documentation。还要研究编译器和其他工具的documentation。处理错误和失败案例(例如callocscanf可能会失败)。

调试很困难(例如Halting ProblemHeisenbugs等等) - 但有时充满乐趣和挑战性。你可以花上几周时间找到一个bug。如果不深入了解实现细节(研究编译器生成的机器代码,研究编译器代码),您通常无法理解错误程序的行为。

PS。你的问题表明了一种错误的心态 - 你应该改进 - 以及对UB的误解。