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