【问题标题】:Learn C The Hard Way exercise 14 Error (warning: array subscript has type ?char? [-Wchar-subscripts]Learn C The Hard Way 练习 14 错误(警告:数组下标有类型?char?[-Wchar-subscripts]
【发布时间】:2015-04-24 22:15:48
【问题描述】:

我正在关注该教程,我正在使用 cygwin 编译器运行 WindowsXP 32 位,该教程要求我运行此代码:

#include <stdio.h>
#include <ctype.h>

// forward declarations
int can_print_it(char ch);
void print_letters(char arg[]);

void print_arguments(int argc, char *argv[])
{
    int i = 0;

    for(i = 0; i < argc; i++) {
        print_letters(argv[i]);
    }
}

void print_letters(char arg[])
{
    int i = 0;

    for(i = 0; arg[i] != '\0'; i++) {
        char ch = arg[i];

        if(can_print_it(ch)) {
            printf("'%c' == %d ", ch, ch);
        }
    }

    printf("\n");
}

int can_print_it(char ch)
{
    return isalpha(ch) || isblank(ch);
}


int main(int argc, char *argv[])
{
    print_arguments(argc, argv);
    return 0;
}

但我一直遇到这个警告:

$ make ex14
cc -Wall -g    ex14.c   -o ex14
ex14.c: In function ?can_print_it?:
ex14.c:34:5: warning: array subscript has type ?char? [-Wchar-subscripts]
    return isalpha(ch) || isblank(ch);
    ^
ex14.c:34:5: warning: array subscript has type ?char? [-Wchar-subscripts]

我尝试四处搜索,虽然我发现很多线程都在谈论类似的错误,但他们的答案/解决方案都没有在我的代码上起作用,而且我找不到与 Learn C 相关的问题艰难的路 exercise 14.

那么我应该怎么做才能摆脱这个警告呢?

【问题讨论】:

  • 代码是否仍然有效?它发出警告,而不是错误......(顺便说一句,我很惊讶有一个“learn-c-the-hard-way”标签:)
  • (unsigned char)ch 。该标准要求传递给这两者(isalphaisblank)的值必须可以表示为 unsigned char,否则行为未定义。
  • @AjPerez,哇,我的错,它确实有效,但我仍然想摆脱那个警告
  • 我不明白为什么编译器认为 char 索引很危险,但是你可以将can_print_it(char ch); 更改为can_print_it(int ch); 来修复它。
  • @iharob 因为他们想要使用比可打印更严格的限制。名称不佳的can_print_it 的目标是排除除字母字符或空格之外的所有字符。标点符号、数字等都被排除在外,isprint 不会出现。

标签: c


【解决方案1】:

警告的原因是 char 值通常为正数,但有时可能为负数;这可能出乎程序员的意料(尤其是因为在某些实现中 char 总是正数),并且对数组使用负数索引显然是一件坏事。

isalpha 正是在你背后做的。您可以通过将 char 转换为 int 或首先将其存储在 int 中来修复警告——但这并不能解决问题,因为负 char 将被转换为具有完全相同问题的负 int。您可以通过将字符转换为无符号字符来解决警告和问题。

【讨论】:

  • 更改这两个函数以接受 unsigned char 的工作
  • 编译器不应为此发出警告。 默认参数提升 确保通过整数提升将 char 转换为 int。因此,给定实现的 char 的原始符号完全不相关,因为它总是会转换为(有符号的)int。此外,函数 can_print_it() 中没有数组。因此,我认为正确的答案是“编译器警告已损坏”。
【解决方案2】:

因此,如果我们查看gcc document for warnings which covers -Wchar-subscripts,它会说:

如果数组下标的类型为 char,则发出警告。这是错误的常见原因,因为程序员经常忘记这种类型是在某些机器上签名的。此警告由 -Wall 启用。

char 是有符号还是无符号由实现定义,如果您使用 unsigned char,则警告将消失。

我们可以从 C99 标准草案中看到,&lt;ctype.h&gt; 中函数的参数预计可以表示为 unsigned charEOF,来自 7.4 部分 字符处理:

在所有情况下,参数都是一个 int,其值应为 可表示为无符号字符或应等于宏 EOF 的值。如果 参数有任何其他值,行为未定义

所以很可能isalphaisblank 被实现为使用查找表的宏,因此这段代码确实会使用字符来索引数组。

【讨论】:

  • 如果 isalpha 等被实现为宏,则实现(编译器)被破坏,因为使它们成为宏会删除 char 到 int 的默认参数提升,程序员会期望发生这种情况,因为它们是函数。并且函数can_print_it()中没有数组,所以编译器不应该给出这个警告。
【解决方案3】:

isalphaisblank 的输入类型应为int,而不是char。这才是核心问题。其余的是如何实施的细节。如果使用-E 标志来预处理代码,函数can_print_it 扩展为:

int can_print_it(char ch)
{
    return (((__ctype_ptr__+sizeof(""[ch]))[(int)(ch)])&(01|02)) || __extension__ ({ __typeof__ (ch) __x = (ch); (((__ctype_ptr__+sizeof(""[__x]))[(int)(__x)])&0200) || (int) (__x) == '\t';});
}

如您所见,输入chisalpha 的实现中用作数组索引。一个临时的__x,与ch 的类型相同,在isblank 的实现中用作数组索引。

您可以通过以下方式修复警告:

  1. can_print_it的参数类型更改为int

    int can_print_it(int ch)
    {
        return isalpha(ch) || isblank(ch);
    }
    
  2. 在函数中创建一个临时变量并使用它来调用isalphaisblank

    int can_print_it(char ch)
    {
        int temp = ch;
        return isalpha(temp) || isblank(temp);
    }
    

【讨论】:

  • 两者都会修复警告,但 都不会 在传递高 ascii 值时修复负索引查找,这正是该警告旨在解决的问题。前者 (1) 更接近金属,但要正确调用它需要 if(can_print_it((unsigned char)ch)) 以确保转换为 int 不会立即退回到负索引。 (顺便说一句,我确实没有对此表示赞同)。
  • @WhozCraig,如果int 被接受为isalphaisblank 的有效参数,理论上这可能是一个负值,我不清楚gcc 是怎样的使用此警告保护使用。
  • 因为当int 被传递时,它是not 提升的(它已经int)。如果 said-int 是负数(当然可以,因为类型是肯定签名的),那么,传递负数int 的人很糟糕。但是char可能被签名(通常是)。它被 (a) 提升为int,并且 (b) 如果签名 and 是否定的,则将在提升期间扩展该符号。所以 joe Sixpack 不知道他的 char 实际上可能是一个有符号负值会发现困难的方式。
  • 顺便说一句,如果 int temp = (unsigned char)ch; 是临时的,您的 second 替代方案将起作用,但我仍然认为第一个更正确,将责任放在调用者身上演员表,就像所有标准 ctype iswhatever 函数一样。
  • @RSahu:在某些系统上,“é”字符(例如)由负数的char 表示。 isalpha 等的规则指定传递给它们的值必须可以由 unsigned char(或 EOF,但我们暂时忽略这种情况)表示,而负数则不是。
【解决方案4】:

您的编译器似乎是旧的或损坏的。 GCC 4.9.1 编译此代码没有警告/错误,给出:

gcc test.c -std=c11 -pedantic-errors -Wall -Wextra

编译器有义务将您的 char 隐式提升为 isalpha 等函数所需的 int。无论 char 的实现定义的签名如何,它都会执行此提升。 char 的大小也无关紧要,因为 char 始终是一个小整数类型。

我希望编译器从这段代码中给出的唯一警告是类似于“从 char 到 int 的隐式转换”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 2012-02-17
    • 1970-01-01
    • 2019-10-23
    • 2012-08-20
    相关资源
    最近更新 更多