【问题标题】:C programming warning: array subscript has type 'char' [-Wchar-subscripts]C 编程警告:数组下标的类型为 'char' [-Wchar-subscripts]
【发布时间】:2014-05-07 00:49:48
【问题描述】:

我似乎无法解决这个问题。以下是我的代码:

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

_Bool are_anagrams (const char *word1, const char *word2);

int main (void)
{
    char an1[30], an2[30];
    int j;
    printf("Enter first word: ");
    scanf("%s", an1);
    printf("Enter second word: ");
    scanf("%s", an2);
    printf("The words are");

    j = are_anagrams (an1, an2);

    if (j == 0)
    {
        printf(" not anagrams. \n");
    }else
        printf(" anagrams. \n");

    return 0;
}

_Bool are_anagrams (const char *word1, const char *word2)
{
    int i;
    int check[26] = {0};
    for(i=0; i<30; i++)
        if(word1[i] == '\0')
            i=40;
        else
        {
            word1[i] = toupper(word1[i]);
            check[word1[i]-65]++;
        }

    for(i=0; i<30; i++)
        if(word2[i] == '\0')
            i=40;
        else
        {
            word2[i] = toupper(word2[i]);
            check[word2[i]-65]--;
        }

    for(i=0; i<26; i++)
        if(check[i] != 0)
        {
            return 0;
        }

    return 1;
}

这些是错误消息:

anagram1.c:38:3: warning: array subscript has type ‘char’ [-Wchar-subscripts]
   word1[i] = toupper(word1[i]);
   ^
anagram1.c:38:3: error: assignment of read-only location ‘*(word1 + (sizetype)((long unsigned int)i * 1ul))’
anagram1.c:46:4: warning: array subscript has type ‘char’ [-Wchar-subscripts]
    word2[i] = toupper(word2[i]);
    ^
anagram1.c:46:4: error: assignment of read-only location ‘*(word2 + (sizetype)((long unsigned int)i * 1ul))’

【问题讨论】:

  • word1word2 是指向 const char 的指针。
  • 检查[toupper(word1[i])-65]++;检查[toupper(word2[i])-65]--;
  • 我没有收到那个警告。我看到的唯一警告是你试图覆盖一个常量。
  • 那些错误是可以理解的,但那些警告很疯狂,那些下标是int,而不是char
  • 65这个数字有什么意义?我想我知道答案,但是有一种更清晰的方法来写它。提示:字符常量的类型为int

标签: c arrays gcc char subscript


【解决方案1】:

警告:

warning: array subscript has type ‘char’

是 'toupper()' 需要 'int' 类型作为参数的结果,而问题代码提供了 'char' 类型。

word1[i] = toupper(word1[i]);
...
word2[i] = toupper(word2[i]);

为了消除警告,给 toupper() 'int' 值:

word1[i] = toupper((unsigned char)word1[i]);
...
word2[i] = toupper((unsigned char)word2[i]);

为了彻底,您可以将 'toupper()' 返回的值从 'int' 转换回 'char':

word1[i] = (char)toupper((unsigned char)word1[i]);
...
word2[i] = (char)toupper((unsigned char)word2[i]);

错误:

error: assignment of read-only location

是尝试使用 'const' 标志修改值的结果:

_Bool are_anagrams (const char *word1, const char *word2)

如果合适,您可以通过消除“const”标志来消除错误:

_Bool are_anagrams (char *word1, char *word2)

或者,您可以制作 'const' 字符串的本地工作副本:

_Bool are_anagrams (const char *I__word1, const char *I__word2)
   {
   int rCode = 0;
   int i;
   int check[26] = {0};
   char *word1 = strdup(I__word1);
   char *word2 = strdup(I__word2);

   for(i=0; i<30; i++)
      if(word1[i] == '\0')
         i=40;
      else
         {
         word1[i] = toupper(word1[i]);
         check[word1[i]-65]++;
         }

   for(i=0; i<30; i++)
      if(word2[i] == '\0')
         i=40;
      else
         {
         word2[i] = toupper(word2[i]);
         check[word2[i]-65]--;
         }

   for(i=0; i<26; i++)
      if(check[i] != 0)
        goto CLEANUP;

   rCode=1;

CLEANUP:
   free(word2);
   free(word1);

   return(rCode);
   }

注意:以上代码使用问题代码体,可能准确也可能不准确。此答案无意修复问题代码中的其他问题;只是为了演示通过创建参数的非“const”副本来解决参数上的“const”标志的正确方法

【讨论】:

  • 我需要有 'const' 标志。有什么办法绕过这个吗?还是我必须重写程序?
  • 为什么无缘无故分配给word1和word2?
  • 你的第一部分,关于toupper,完全是错误的。首先,无论如何将 char 提升为 int 以进行参数传递;其次,这样的数组调用与数组下标完全无关。
  • @Kevin,你是对的。我尽量避免完成作业;只是回答手头的问题。我编辑了答案,并添加了一条注释以阐明该意图。
  • 我想你没听懂我说的。你的第一句话,“警告:警告:数组下标有'char'类型是'toupper()'需要'int'类型作为参数的结果,而问题代码提供'char'类型”是错误的.虽然 toupper 确实需要一个 int,但 char 被提升了,如果这是问题的话,这个错误不会说明任何关于下标的内容。
【解决方案2】:

&lt;ctype.h&gt; 中声明的touppertolower 函数(连同is*() 函数)需要int 类型的参数。

类型不是问题,因为char 将隐式转换为int。问题是他们期望的必须在unsigned char的范围内EOF(通常是-1)。我们可以忽略EOF 的情况。

Plain char 是有符号还是无符号,由编译器开发人员随心所欲(由系统的 ABI 指导)。如果普通的char 已签名,并且您传递给toupper 的值恰好是负值(不等于EOF),那么您的行为未定义。

解决方案是将参数显式转换(强制转换)为unsigned char

而不是:

word1[i] = toupper(word1[i]);

你需要写:

word1[i] = toupper((unsigned char)word1[i]);

是的,很遗憾您必须这样做。如果toupper() 简单地接受char 类型的参数并返回char 结果会更好。但事实就是这样,我们坚持下去。

那么为什么你会收到关于char 类型数组下标的警告呢? toupper 函数通常实现为扩展为数组索引操作的宏。在预处理器扩展宏调用之后,它不再(对于编译器的其余部分)看起来像函数调用。 (任何标准库函数都可以实现为宏,只要宏具有与实际函数调用相同的行为。)

【讨论】:

  • 更准确的说法是,只要宏具有相同的行为,每个标准库函数可以实现为宏。如果Converter 是一个合适的函数指针类型,那么Converter xform = &amp;toupper; char c = xform(i); 必须工作并且做与char c = toupper(i); 相同的事情。
  • 我得到了答案。谢谢!
【解决方案3】:

正如Mahonri Moriancumer 在他的answer 中正确识别(事实上,正如Keith Thompson 在他的answer 中正确识别和解释),对toupper() 的调用的问题在于您的类型传递的是char,但函数需要int。但是既然int 可以包含char 可以包含的任何值,它为什么会抱怨呢?

答案是有效int 值的范围受标准(ISO/IEC 9888:2011)限制:

7.4 字符处理&lt;ctype.h&gt;

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

编译器警告您的是,如果您将 char 传递给此函数,并且 char 类型是有符号类型,那么您可能传递的是负数索引。 getchar()getc()fgetc() 的结果与函数参数类型的规范相匹配(这不是偶然的)。如果您使用int,编译器假定您不会传递超出范围的值,但如果您使用普通的char 来保存诸如“Å”(U+00C5,拉丁大写字母A,上面有环)和普通的char 已签名,那么您将传递一个超出有效值范围的负值。

如果是我的代码,我会转换为unsigned char

word1[i] = toupper((unsigned char)word1[i]);

简单地将已签名的 char 转换为 int 并不能正确处理该符号。

您可以查看C isupper() function 以了解更多关于为什么设置范围的原因。


另一对错误是因为您修改了一个常量字符串。您可以通过以下方式避免该错误(对于非重音字符):

unsigned char uc = word1[i];
if (isalpha(uc))
    check1[toupper(uc) - 'A']++;

这避免了输入中的空格、数字和标点符号问题。但是,如果您需要处理输入中的重音字符,那么最好的办法是将check 设为大小为 256 的数组,然后检查整个 0..255 范围内的计数是否相同。

【讨论】:

  • 这里有什么我没有在我的回答中提到的吗?
  • 否;可能不会——这就是为什么你有一个赞成票。或者是的,来自标准的引用、示例字符值以及其他次要细节。你的答案在我输入我的时候就到了(直到我点击提交后我才发现它);对相同事实的独立陈述看起来很相似。
  • 我得到了答案。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
  • 2017-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多