代码之家  ›  专栏  ›  技术社区  ›  Mohideen Imran Khan

调用导致分段错误的isalpha

  •  10
  • Mohideen Imran Khan  · 技术社区  · 7 年前

    我有下面的程序会导致分段错误。

    #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* 指向的指针 但情况似乎并非如此。我有代码来检查参数的数量,但这里没有显示,以实现简洁性。

    这里发生了什么?

    4 回复  |  直到 7 年前
        1
  •  19
  •   Gerhardh    7 年前

    一般来说,讨论未定义的行为为什么会导致这个结果或另一个结果是毫无意义的。

    但是,即使有些事情超出了规范,试图理解它为什么会发生也没有什么害处。

    执行 isalpha 它使用一个简单的数组来查找所有可能的 unsigned char 价值观。在这种情况下,作为参数传递的值用作数组的索引。 虽然实数字元限制为8位元,但整数不是。 函数需要 int 作为参数。这是允许进入的 EOF 也不适合 无符号字符 .

    如果将类似0x7239482342的地址传递到函数中,那么这远远超出了所述数组的末尾,当CPU尝试使用该索引读取条目时,它会从世界的边缘掉下来。;)

    打电话 是否字母 有了这样的地址,编译器就应该对将指针转换为整数发出一些警告。你可能忽略了…

    图书馆 可以 包含检查有效参数的代码,但也可能仅依赖于用户不传递不应传递的内容。

        2
  •  6
  •   Antti Haapala -- Слава Україні    7 年前
    1. printf 没有冲洗
    2. 从指针到整数的隐式转换 它应该至少为违反约束生成编译时诊断。 生成的数字超出范围 isalpha . 是否字母 实现为查找表意味着代码访问表时超出了界限,因此未定义行为。
    3. 为啥是你 没有 由于 怎样 是否字母 是作为宏实现的。在我使用glibc 2.27-3ubuntu1的电脑上, 是否字母 定义为

      # 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上是这样)。

    给予的正确论点 是否字母 (unsigned char)argv[1][k] ! C11 7.4 :

    在所有情况下,论点都是 int ,其价值应表示为 unsigned char 或等于宏的值 EOF . 如果参数有任何其他值,则行为未定义。

        3
  •  3
  •   John Kugelman Michael Hodel    7 年前

    我觉得很奇怪,程序在没有打印测试的情况下会导致分段错误。

    printf 不会立即打印,但会写入临时缓冲区。结束你的字符串 \n 如果要将其刷新为实际输出。

    用argv[1][k]代替argv[1]解决了这个问题。

    isalpha 用于单个字符。

        4
  •  1
  •   Lundin    7 年前

    首先,一致性编译器必须在这里给出诊断消息。不允许从指针隐式转换为 int 参数 isalpha 预期。(这违反了6.5.16.1中的简单分配规则。)

    至于为什么不打印“test”,可能只是因为stdout没有刷新。您可以尝试添加 fflush(stdout); 在打印之后,看看这是否解决了问题。或者添加换行符 \n 在字符串的末尾。

    printf("TEST"); ,只要打印出来 TEST 在它可能打印之前 "Enter only alphabets!" 。这种优化在这里可能不太可能发生,但在其他情况下也可能发生。