【问题标题】:What does char a[50][50] mean in C?char a[50][50] 在 C 中是什么意思?
【发布时间】:2019-11-26 03:11:00
【问题描述】:

我正在做一个与字符串有关的作业。 这是代码


int main(){
    char a[50][50];
    int n;
    printf("Enter the value of n\n");
    scanf("%d",&n);
    printf("Enter %d names\n",n);
    fflush(stdin);
    for(int i=0; i<n; i++){
        gets(a[i]);
    }

我试图将 char a[50][50] 更改为 char a[50] 但整个程序没有运行,并出现以下错误消息:“从 'char' 到 '*char' 的转换无效 我真的不明白这是如何工作的。

【问题讨论】:

  • Beginner's Tutorial 或尝试您选择的任何其他 C 书籍。
  • 与您的问题无关的两件事:首先 never ever 使用 gets 函数,它是 dangerous 并已从C标准。使用例如fgets 代替。其次,将只读的FILE 流传递给fflush(如stdin)在C 规范中明确提到为未定义的行为

标签: c string


【解决方案1】:

char a[50][50]a 声明为由 50 个 char 组成的 50 个数组组成的数组。

那么a[0] 是一个包含 50 个char 的数组,a[1],a[2] 也是如此。 a[3],以此类推直到 a[49]。有50个独立的数组,每个数组有50个char

由于a[0] 是一个包含50 个char 的数组,所以a[0][0] 是一个char。一般来说,a[i][j] 是数组i 的字符j

gets(a[i]) 表示从输入中读取字符并将它们放入a[i]。为此,a[i] 必须是 char 的数组—gets 读取多个字符并将它们放入数组中。如果a[i] 是单个字符,gets 将无法工作。

虽然gets(a[i]) 说要将字符放入a[i],但它通过传递地址而不是传递数组来工作。当在表达式中使用数组而不是作为sizeof 的操作数或地址运算符&amp; 时,C 会自动将其转换为指向其第一个元素的指针。由于a[i] 是一个数组,它会自动转换为指向其第一个元素的指针(指向a[i][0] 的指针)。 gets 接收此指针并使用它来填充它从标准输入流中读取的字符。

【讨论】:

    【解决方案2】:

    char a[50][50]a 声明为char 的50 元素数组的50 元素数组。这意味着每个 a[i] 是一个由 50 个元素组成的 char 数组。它将像这样在内存中排列:

       +---+
    a: |   | a[0][0]
       +---+
       |   | a[0][1]
       +---+
       |   | a[0][2]
       +---+
        ...
       +---+
       |   | a[0][49]
       +---+
       |   | a[1][0]
       +---+
       |   | a[1][1]
       +---+
        ...
       +---+
       |   | a[1][49]
       +---+
       |   | a[2][0]
       +---+ 
        ...
    

    此代码在a 中存储最多50 个字符串,每个最多49 个字符(IOW,每个a[i] 可以存储一个49 个字符的字符串)。在 C 中,string 是包含 0 值终止符的字符值序列。例如,字符串"hello",表示为序列{'h', 'e', 'l', 'l', 'o', 0}。结尾的 0 标记了字符串的结尾。带有%s 说明符的putsprintf 等字符串处理函数和输出函数需要该0 终止符才能正确处理字符串。

    字符串存储在字符类型的数组中,char(用于 ASCII、UTF-8 或 EBCDIC 字符集)或 wchar_t 用于“宽”字符串(需要超过 8 位左右的字符集编码)。 N 个字符的字符串需要一个至少 N+1 个元素宽的数组来说明 0 终止符。

    除非它是sizeof 或一元&amp; 运算符的操作数,或者是用于初始化字符类型数组的字符串字面量,否则是“N 元素数组”类型的表达式T”将被转换(“衰减”)为“指向T的指针”类型的表达式,表达式的值将是数组第一个元素的地址。

    当你打电话时

    gets( a[i] );
    

    表达式a[i]从“char的50元素数组”类型转换为“指向char的指针”,表达式的值是数组第一个元素的地址(@987654345 @)1gets 将从标准输入读取字符并将它们存储到从该地址开始的数组中。请注意,gets 不再是标准 C 库的一部分——它在 2011 版标准中被删除,因为它不安全。 C 不需要对数组访问进行任何类型的边界检查 - 如果您输入的字符多于目标缓冲区的大小(在本例中为 50),那么这些额外的字符将在最后一个元素之后立即写入内存数组,这可能会导致各种混乱。缓冲区溢出是一种流行的恶意软件利用。您应该将 gets 调用替换为

    fgets( a[i], 50, stdin );
    

    它将从标准输入将最多 49 个字符读入a[i]。请注意,任何多余的字符都会留在输入流中。

    另外,fflush 的行为没有为输入流定义2 - 没有好的、安全、便携的方法可以清除多余的输入,除非使用读取它getcharfgetc


    1. 这就是当您将 achar [50][50] 更改为 char [50] 时收到错误消息的原因 - 在这种情况下,a[i] 的类型为 char,而不是 char *,并且值为a[i] 不是地址
    2. Microsoft 的 Visual Studio C 编译器是一个明显的例外 - 它会从输入流中清除多余的输入。但是,这是特定于 MSVC 的,不能跨不同的编译器移植。就“flush”语义而言,该操作也有点荒谬。

    【讨论】:

      【解决方案3】:

      基本上,在 C 中,这表示一个长度为 0 到 50 的数组,其中包含数组的每个单元格中的字符值 50

      【讨论】:

        【解决方案4】:

        该程序似乎将n 名称存储在数组a 中。它首先询问姓名的数量,然后询问姓名。方法char *gets(char *str) 将每个不同的行存储在a 的条目中。

        n 有 2 个维度。第一个是指名称的数量,第二个是每个名称的长度。类似n[number_of_names][lenght_of_name]

        但是,如果用户提供的 n > 50,或者名称包含超过 50 个字符,它可能会崩溃。

        另外,gets() 很危险。见this other post

        编辑:a 更改为一维会使程序尝试将整行存储在一个字符内,因此会出现错误

        【讨论】:

          猜你喜欢
          • 2015-01-08
          • 1970-01-01
          • 1970-01-01
          • 2019-10-17
          • 1970-01-01
          • 1970-01-01
          • 2016-12-09
          • 2012-04-17
          • 1970-01-01
          相关资源
          最近更新 更多