繁体   English   中英

为什么使用不相关的printf语句导致程序输出更改?

[英]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 -gGCC

  • 改进您的代码以不发出警告; 也许也可以尝试使用另一种编译器,例如Clang,并且也不要从中获得任何警告(因为不同的编译器会给出不同的警告)。

  • 考虑使用诸如git之类的版本控制系统来保留代码的各种变体,以及一些构建自动化工具。

  • 考虑更多关于您的程序及其内部不变式的信息

  • 使用调试器( gdb ),尤其是与watchpoints配合使用,以了解您的进程的内部状态; 并且有几个测试用例可以在调试器下运行,而没有调试器。

  • 使用检测工具,例如GCC的address sanitizer -fsanitize=addressvalgrind之类的工具。

  • 使用橡皮鸭调试方法

  • 有时会考虑使用静态源代码分析工具(例如Frama-C )。 他们需要使用专业知识和/或给出许多误报。

  • 阅读有关编程(例如SICP )和C编程语言的更多信息。 下载并研究C11编程语言规范n1570 (并非常注意其中的UB)。 仔细阅读所使用的每个标准或外部功能的文档 还研究编译器和其他工具的文档 处理错误和失败的情况(例如callocscanf可能失败)。

调试是困难的 (例如,由于停止问题Heisenbugs等),但是有时很有趣且具有挑战性。 您可以花费数周的时间来发现一个错误。 而且,如果不深入研究实现细节(研究由编译器生成的机器代码,研究编译器的代码),通常就无法理解错误程序的行为。

PS。 您的问题显示出错误的心态-应该改善-对UB的误解。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM