为啥这个程序会在 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";
但请确保您注意有关类似函数的宏的警告。我很少推荐它们,但它们在这种情况下还可以,因为它们控制得很好。
例如,当在不带大括号的 if
或 while
语句中使用时,它们可能会导致问题,除非您明确地满足这一要求(臭名昭著的 #define X(Y) do something with Y while(0)
,您无疑可以在本网站的其他地方找到)。
(a) 强迫症,但该死的顺序是正确的:-)
【讨论】:
以上是关于为啥这个程序会在 C 中给出 Invalid memory access 错误? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个 ffmpeg 过滤器会导致“Invalid Size”错误?
为啥 reduce 会在 Clojure 中给出 ***Error?
cmake install() 行为?如果给出此指令,为啥二进制会在 PWD 中查找