【问题标题】:Why does this program give a Invalid memory access error in C? [closed]为什么这个程序会在 C 中给出 Invalid memory access 错误? [关闭]
【发布时间】:2021-10-27 19:01:30
【问题描述】:
const char *welcome(const char *language)
{
      struct options
      {
             char *language;
             char *greeting;
      };

     struct options list[17] =
     {
            {"english", "Welcome"}, {"czech","Vitejte"},
            {"danish","Velkomst"}, {"dutch","Welkom"},
            {"estonian","Tere tulemast"}, {"finnish","Tervetuloa"},
            {"flemish","Welgekomen"},{"french","Bienvenue"},
            {"german","Willkommen"},{"irish","Failte"},
            {"italian","Benvenuto"},{"latvian","Gaidits"},
            {"lithuanian","Laukiamas"},{"polish","Witamy"},
            {"spanish","Bienvenido"},{"swedish","Valkommen"},
            {"welsh","Croeso"}
     };

     for (int i = 0; i < 17; i++)
     {
         if (strcmp(language, list[i].language) == 0)
         {
            return list[i].greeting;
         }
     }  
     return "Welcome";
}

程序将字符串作为语言,然后返回该语言的问候语,但是我收到错误Test Crashed Caught unexpected signal: SIGSEGV (11). Invalid memory access,这是唯一出现的错误。为什么会这样,我该如何解决它,因为我可以看到我所做的一切都是安全的。

【问题讨论】:

  • 你确定错误来自这个函数吗?包括一个简单的main,它调用这个函数并演示错误。
  • 使用调试器。它会立即告诉您触发 seg 错误的确切代码行。如需进一步帮助,请提供完整代码minimal reproducible example
  • 您可能在程序早期导致了未定义的行为。未定义的行为并不总是会导致立即崩溃,有时它会破坏内存,从而导致后面的代码失败。
  • 调用者对这个函数的结果做了什么?如果它试图修改它,你会得到一个 SEGV。
  • 仅供参考,对关于您的帖子的重复问题保持沉默是关闭帖子的可靠方法,尤其是minimal reproducible example 请求被提交并被忽略时。

标签: c string pointers memory struct


【解决方案1】:

该函数本身没有任何问题,但 可能其中一个有问题:

  • 它的调用方式;或
  • 如何处理返回值。

不幸的是,这两个都包含在问题中未包含的代码中,所以现在是假设。

在第一种情况下,如果您传递的不是 C 字符串(例如 NULL),您可能会发现 strcmp 会导致问题。

在第二种情况下,因为您返回的是字符串文字,所以任何修改它的尝试都将是未定义的行为。我希望您返回指向 const 数据的指针这一事实可以防止这种情况发生,但如果不看到周围的代码就很难说。


而且,顺便说一句,虽然不是真正解决您的问题的一部分:虽然我通常赞成使用数据驱动的代码,例如您使用选项表,但它存在许多问题。

首先,表中的字符串最好标记为const,因为它们是不应更改的字符串文字。

我看到您将字符串返回const,但将项目本身设为const 有时可以为编译器提供更多优化代码的余地。事实上,尽可能多的应该是const,除非有迫切的理由允许调用者更改它们。


其次,由于表永远不会改变,因此最好使用静态存储持续时间,这样每次输入函数时都不会重新创建数组(一个好的优化器可能会发生这种情况,但强制它更安全) .


第三,当不需要时,您的代码中有神奇的数字17。这意味着,如果您添加一种语言,则需要更改 三项 项(数组、数组的大小和处理数组的循环),这意味着可以得到三项不同步并给您带来麻烦。


考虑到所有这些点的更好方法可能是:

const char *welcome(const char *language) {
    static const struct {
        const char *language;
        const char *greeting;
    } list[] = {
        { "czech",       "Vitejte"       },
        { "danish",      "Velkomst"      },
        { "dutch",       "Welkom"        },
        { "english",     "Welcome"       },
        { "estonian",    "Tere tulemast" },
        { "finnish",     "Tervetuloa"    },
        { "flemish",     "Welgekomen"    },
        { "french",      "Bienvenue"     },
        { "german",      "Willkommen"    },
        { "irish",       "Failte"        },
        { "italian",     "Benvenuto"     },
        { "klingon",     "nuqneH"        },
        { "latvian",     "Gaidits"       },
        { "lithuanian",  "Laukiamas"     },
        { "polish",      "Witamy"        },
        { "spanish",     "Bienvenido"    },
        { "swedish",     "Valkommen"     },
        { "welsh",       "Croeso"        }
    };

    for (int i = 0; i < sizeof(list) / sizeof(*list); i++) {
        if (strcmp(language, list[i].language) == 0) {
            return list[i].greeting;
        }    
    }
    return "Welcome";
}

使用该方案,您可以添加或删除语言,无需更改其他代码。

您会看到我已将 english 紧跟在 dutch 之后(由于我的 CDO 性质(a)),因为您的其余条目已排序,但它 可能是因为(例如)它是最有可能传入的值,所以您将它放在顶部。如果确实有原因,请随意将其更改回来。

代码的格式也很好,因此维护人员可以轻松辨别发生了什么。


另一个你可能想要检查的可能性是它是否需要数据驱动,因为它是一个简单的字符串到字符串的转换。您同样可以编写格式良好的代码,而无需数据驱动,例如:

const char *welcome(const char *language) {
    if (strcmp(language, "czech"     ) == 0) return "Vitejte"       ;
    if (strcmp(language, "danish"    ) == 0) return "Velkomst"      ;
    if (strcmp(language, "dutch"     ) == 0) return "Welkom"        ;
    if (strcmp(language, "english"   ) == 0) return "Welcome"       ;
    if (strcmp(language, "estonian"  ) == 0) return "Tere tulemast" ;
    if (strcmp(language, "finnish"   ) == 0) return "Tervetuloa"    ;
    if (strcmp(language, "flemish"   ) == 0) return "Welgekomen"    ;
    if (strcmp(language, "french"    ) == 0) return "Bienvenue"     ;
    if (strcmp(language, "german"    ) == 0) return "Willkommen"    ;
    if (strcmp(language, "irish"     ) == 0) return "Failte"        ;
    if (strcmp(language, "italian"   ) == 0) return "Benvenuto"     ;
    if (strcmp(language, "klingon"   ) == 0) return "NuqneH"        ;
    if (strcmp(language, "latvian"   ) == 0) return "Gaidits"       ;
    if (strcmp(language, "lithuanian") == 0) return "Laukiamas"     ;
    if (strcmp(language, "polish"    ) == 0) return "Witamy"        ;
    if (strcmp(language, "spanish"   ) == 0) return "Bienvenido"    ;
    if (strcmp(language, "swedish"   ) == 0) return "Valkommen"     ;
    if (strcmp(language, "welsh"     ) == 0) return "Croeso"        ;
    return "Welcome";
}

或者,如果您不喜欢输入所有常见的东西,您可以使用以下内容:

const char *welcome(const char *language) {
    #define XLAT(FROM, TO) if (strcmp(language, FROM) == 0) return TO

    XLAT("czech",      "Vitejte");
    XLAT("danish",     "Velkomst");
    XLAT("dutch",      "Welkom");
    XLAT("english",    "Welcome");
    XLAT("estonian",   "Tere tulemast");
    XLAT("finnish",    "Tervetuloa");
    XLAT("flemish",    "Welgekomen");
    XLAT("french",     "Bienvenue");
    XLAT("german",     "Willkommen");
    XLAT("irish",      "Failte");
    XLAT("italian",    "Benvenuto");
    XLAT("latvian",    "Gaidits");
    XLAT("lithuanian", "Laukiamas");
    XLAT("polish",     "Witamy");
    XLAT("spanish",    "Bienvenido");
    XLAT("swedish",    "Valkommen");
    XLAT("welsh",      "Croeso");

    #undef XLAT

    return "Welcome";
}

但请确保您注意有关类似函数的宏的警告。我很少推荐它们,但它们在这种情况下还可以,因为它们控制得很好。

例如,当在不带大括号的 ifwhile 语句中使用时,它们可能会导致问题,除非您明确地满足这一要求(臭名昭著的 #define X(Y) do { something with Y } while(0),您无疑可以在本网站的其他地方找到)。


(a) 强迫症,但该死的顺序是正确的:-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多