簡體   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