我有以下程序导致分段错误。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
printf("TEST");
for (int k=0; k<(strlen(argv[1])); k++)
{
if (!isalpha(argv[1])) {
printf("Enter only alphabets!");
return 1;
}
}
return 0;
}
我已经发现正是这条线造成了问题
if (!isalpha(argv[1])) {
并将argv[1]
替换为argv[1][k]
解决了问题。
但是,我觉得很奇怪程序导致分段错误,甚至没有打印TEST
。我还希望isalpha
函数错误地检查char*
指针的低字节是否argv[1]
,但似乎并非如此。我有代码来检查参数的数量,但为简洁起见,此处未显示。
这里发生了什么?
答案 0 :(得分:19)
一般来说,讨论为什么未定义的行为导致这个结果或另一个结果是毫无意义的。
但是,尝试理解为什么会发生某些事情,即使它超出规范也许并不是一种伤害。
isalpha
的实现使用简单数组来查找所有可能的unsigned char
值。在这种情况下,作为参数传递的值将用作数组的索引。
虽然真实字符限制为8位,但整数不是。
该函数以int
为参数。这也是为了允许输入不适合EOF
的{{1}}。
如果你将一个像0x7239482342这样的地址传递给你的函数,这远远超出了所述数组的结尾,并且当CPU试图读取带有该索引的条目时,它就会脱离世界的边缘。 ;)
使用这样的地址调用unsigned char
是编译器应该提出关于将指针转换为整数的警告的地方。你可能忽略了......
库可能包含检查有效参数的代码,但它也可能只依赖于用户不传递不会传递的内容。
答案 1 :(得分:6)
printf
没有被冲洗isalpha
范围的数字。 isalpha
被实现为查找表意味着您的代码访问了表格越界,因此未定义的行为。为什么没有获取诊断信息可能属于一个部分,因为 isalpha
如何实现为宏。在我的带有Glibc 2.27-3ubuntu1的计算机上,isalpha
被定义为
# define isalpha(c) __isctype((c), _ISalpha)
# define __isctype(c, type) \
((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
宏中包含一个不幸的强制转换为int
,这会使你的错误无效!
我在其他许多人之后发布此答案的一个原因是您没有修复代码,但是在给定扩展字符和char
签名的情况下,它仍会受到未定义行为的影响(这通常是x86-32和x86-64的情况。
提供给isalpha
的正确参数是(unsigned char)argv[1][k]
! C11 7.4:
在所有情况下,参数都是
int
,其值应表示为unsigned char
或等于宏EOF
的值。 如果参数具有任何其他值,则行为未定义。
答案 2 :(得分:3)
我觉得很奇怪程序导致分段错误,甚至没有打印TEST
printf
不会立即打印,但会写入临时缓冲区。如果要将其刷新为实际输出,请使用\n
结束字符串。
用argv [1] [k]替换argv [1]解决了这个问题。
isalpha
旨在使用单个字符。
答案 3 :(得分:1)
首先,符合标准的编译器必须在此处为您提供诊断消息。不允许从指针隐式转换为if(Page.IsValid)
{}
期望的int
参数。 (这违反了简单分配规则,6.5.16.1。)
至于为什么不打印“TEST”,可能只是因为没有刷新stdout。您可以尝试在printf之后添加isalpha
,看看是否能解决问题。或者,在字符串末尾添加换行符fflush(stdout);
。
否则,只要没有副作用,编译器就可以自由地重新排序代码的执行。也就是说,允许在\n
之前执行整个循环,只要它在可能打印printf("TEST");
之前打印TEST
。这种优化可能不会在这里发生,但在其他情况下它们可能会发生。