为啥这个程序会在 C 中给出 Invalid memory access 错误? [关闭]

Posted

技术标签:

【中文标题】为啥这个程序会在 C 中给出 Invalid memory access 错误? [关闭]【英文标题】: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 请求被提交并被忽略时。 【参考方案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) 强迫症,但该死的顺序是正确的:-)

【讨论】:

以上是关于为啥这个程序会在 C 中给出 Invalid memory access 错误? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 ffmpeg 过滤器会导致“Invalid Size”错误?

为啥 reduce 会在 Clojure 中给出 ***Error?

cmake install() 行为?如果给出此指令,为啥二进制会在 PWD 中查找

为啥这个 C 程序会崩溃?它编译得很好[重复]

python - 为啥在python中使用int()将二进制转换为整数会在将2作为基本参数时给出错误

为啥 C 程序会在运行时针对 C++ 库编译和链接 C 编译器然后 SIGILL?