这是我的代码:

#include <studio.h>
#include <string.h>

int main() { 
    char possibilities[21][5] = {
        "", "I", "II", "III", "IV", "V", "VI", 
        "VII", "VIII", "IX", "X", "XI", "XII", 
        "XIII", "XIV", "XV", "XVI", "XVII", 
        "XVIII", "XIX", "XX"
    }; 

    char input1[6]; 
    char input2[6]; 
    int total = 0; 
    char output[10] = "";

    printf("the first Roman numeral: ");
    scanf("%s", input1);

    printf("Enter the second Roman numeral: ");
    scanf("%s", input2);

    for (int i = 0; i < 21; i++) {
        if (strcmp(input1, possibilities[i]) == 0) {
            total += i;
        }
        if (strcmp(input2, possibilities[i]) == 0) {
            total += i;
        }
    }

    while (total > 0) {
        if (total >= 10) {
            total -= 10;
            strcat(output, "X");
        } else {
            strcat(output, possibilities[total]);
            total = 0;
        }
    }
    printf("Result = %s", output);
    return 0;
}

我测试了 III 和 XVII、IV 和 XVI,基本上是“围绕”II 和 XVIII 的所有东西,并得到了正确的输出。我不知道问题出在哪里。

6

  • 2
    投入?预期产出?实际产出?


    – 

  • 3
    这似乎是学习如何程序的绝佳机会。例如,使用逐行执行代码,同时监视变量及其值。


    – 

  • 还启用了额外的警告,并将其视为错误。


    – 

  • 1
    @Someprogrammerdude 在这种特定情况下,不会出现针对该问题的警告。这在 C 中是合法的


    – 

  • 3
    XVIII需要 6 个字符(5+1 为 NULL):char possibilities[21][6]


    – 



最佳答案
2

您的字符串数组太“短”。

char possibilities[21][5]

只能容纳最多 4 个字符的字符串,因为 C 样式字符串末尾需要一个零终止符。但是,“XVIII”有 5 个字符,因此没有空间容纳终止符。因此possibilities[18]不是有效的 C 样式字符串。

使用

char possibilities[21][6]
                       ^

为终止腾出空间。

调试提示:

那么我是如何找到这个 bug 的呢?我使用了一个非常有名的调试器,它实际上是 C 语言自带的。不需要安装任何东西——它已经在那里了。它的名字是printf

for (int i = 0; i < 21; i++) {
    printf("Compare with %s\n", possibilities[i]); // DEBUGGER
    if (strcmp(input1, possibilities[i]) == 0){
        printf("Match on input1 - add %d\n", i); // DEBUGGER
        total += i;
    }
    if (strcmp(input2, possibilities[i]) == 0){
        printf("Match on input1 - add %d\n", i); // DEBUGGER
        total += i;
    }
}

调试器显示:

Compare with I
Compare with II
Match on input1 - add 2
Compare with III
Compare with IV
Compare with V
Compare with VI
Compare with VII
Compare with VIII
Compare with IX
Compare with X
Compare with XI
Compare with XII
Compare with XIII
Compare with XIV
Compare with XV
Compare with XVI
Compare with XVII
Compare with XVIIIXIX
Compare with XIX
Compare with XX

很容易看出问题出在“XVIIIXIX”附近,因为这不是正确的比较字符串。

顺便提一句:

绝不要

scanf("%s", input1);

因为它允许用户溢出你的输入缓冲区。

实际上,永远不要使用scanf。忘掉它吧。fgets用于用户输入。

8

  • 3
    并不是说你对scanfvs. 的看法是错误的fgets,但指出如何scanf使用来防止溢出问题也许并不为过。


    – 

  • @Chris 没必要这么做。一旦 OP 忘记了scanf,OP 就不需要知道了。


    – 

  • 1
    @4386427 这是避免错误的好方法,但它并没有教会 OP 太多关于安全代码实践的知识:)


    – 


  • @mono 哦,是的。使用fgets。永远不要使用scanf。这是“安全代码实践”的一个很好的建议


    – 

  • 很多年过去了,我还是想问,为什么 scanf 这么糟糕?在 scanf 参数字符串中限制扫描大小有什么问题?


    – 

您提供的代码当前使用减去值并附加字符的循环,但它不能完全涵盖所有情况,尤其是总数小于 10 时。

while (total > 0){
    if (total >= 10) {
        total -= 10;
        strcat(output, "X");
    } else {
        strcat(output, possibilities[total]);
        total = 0;
    }
}

尝试使用元组数组,允许将整数直接映射回罗马数字字符。

    ...
    scanf("%5s", input1); // prevents potential overflow
    ...
    scanf("%5s", input2); // prevents potential overflow
    ...
    struct {
        int value;
        const char *symbol;
    } roman_map[] = {
        {10, "X"}, {9, "IX"}, {8, "VIII"}, {7, "VII"}, {6, "VI"},
        {5, "V"}, {4, "IV"}, {3, "III"}, {2, "II"}, {1, "I"}
    };

    for (int i = 0; i < 10 && total > 0; i++) {
        while (total >= roman_map[i].value) {
            strcat(output, roman_map[i].symbol);
            total -= roman_map[i].value;
        }
    }

1

  • 1
    OP 的代码对小于 10 的值进行表查找。那部分对我来说看起来不错。


    –