无法创建整数数组来保存字符

时间:2018-09-08 11:56:08

标签: c arrays

我是编程的新手,几周前开始学习。我已经读过一本书,字符数组应该以{{1​​}}结尾,但是当我创建一个没有\0的数组时,它可以正常工作。怎么可能??

\0

上面的代码输出是

#include<stdio.h>
#include<string.h>
int main()
{
    char a[] = {'a','p','p','l','e'};

    printf("%d\n",strlen(a));
    printf("%s\n",a);
    return 0;
}

我也读到char是整数数据类型的子集,但是当我用int数据类型创建上述数组时,它不能正常工作。

5
apple

上面的代码输出是

#include<stdio.h>
#include<string.h>
int main()
{
    int a[] = {'a','p','p','l','e'};

    printf("%d\n",strlen(a));
    printf("%s\n",a);
    return 0;
}

为什么只考虑数组的第一个元素?

6 个答案:

答案 0 :(得分:2)

您的问题的前半部分与此等效:

  

我是新手,几周前开始学习道路交通知识。我已经读过一本书,您应该在进入交叉路口之前等待绿灯,但是当我不等待进入交叉路口时,它可以正常工作。怎么可能?

换句话说,你很幸运。碰巧的是,即使您构建了没有适当的pip install --upgrade certifi终止符的字符数组,\0e之后的内存中也恰好有一个0字节,因此无论如何工作。但这并不能保证一定能正常工作,甚至不保证您可以一直过马路,不会最终受到打击。

关于第二个问题,当您读到“ apple是整数数据类型的子集”时,这并不意味着您通常会在任何地方使用char,使用char

这是内存中的一些字符。它们每个都是一个字节大小:

int

内存中有一些整数。在现代计算机上,每个字节的大小可能约为四个字节:

char c1 = 'p', c1 = 'e', c3 = 'a', c4 = 'r';

    +---+                   +---+
c1: | p |               c2: | e |
    +---+                   +---+

    +---+                   +---+
c3: | a |               c4: | r |
    +---+                   +---+

这里是int i1 = 'p', i1 = 'e', i3 = 'a', i4 = 'r'; +---+---+---+---+ +---+---+---+---+ i1: | p | i2: | e | +---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+ i3: | a | i4: | r | +---+---+---+---+ +---+---+---+---+ 的数组,以空值结尾:

char

char ca[] = { 'p', 'e', 'a', 'r', '\0' }; +---+---+---+---+---+ ca: | p | e | a | r |\0 | +---+---+---+---+---+ 打印此字符串或printf计算其长度时,它们从头开始并沿字符串一次移动一个字节,直到找到strlen。 / p>

但这是\0的数组:

int

但是我把它稍微画错了,因为实际上,每个int中的三个额外字节没有用空格填充,而是用零字节填充。 (这就像我们要用前导零表示数字1,即0001。)因此,更准确的图片如下所示:

int ia[] = { 'p', 'e', 'a', 'r', '\0' };

    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p             | e             | a             | r             | \0            |
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

因此,当 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ia: | p \0 \0 \0 | e \0 \0 \0 | a \0 \0 \0 | r \0 \0 \0 | \0 \0 \0 \0| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ printf从头开始并一次处理数组一个字节以寻找终止的strlen时,它们会在第一个字母之后立即找到一个。

这里要考虑的重要一点是, \0printf被定义为对strlen 的数组进行操作。而且由于C的工作方式,他们无法知道您已经作弊并通过了char数组。他们实际上使用了相同的内存并将其视为int的数组,因此得到的结果与您期望的完全不同。

由于这样的错误很容易发生,因此,如果您这样做的话,好的编译器会警告您。对于您的代码,我的编译器向我发出了以下警告:

char

这些消息引用类型warning: incompatible pointer types passing 'int [5]' to parameter of type 'const char *' warning: format specifies type 'char *' but the argument has type 'int *' ,它是指向char *的指针,因为将数组传递给函数时,实际上传递的是指向数组第一个元素的指针。 (但这是另一天的话题。但这与我所说的charprintf“在字面上占用相同的内存并将其视为”一样,这与字符数组有关,而不是。)

答案 1 :(得分:2)

char a[] = {'a','p','p','l','e'};中,编译器计算您提供的值的数量,即5。然后,它将创建一个由五个char组成的数组,并使用这些值对其进行初始化。

然后,在printf("%d\n",strlen(a));printf("%s\n",a);中,行为不是C标准定义的,因为要求您在数组中使用零元素来指示结束位置。在尝试这种情况的情况下,可能发生了a数组后的内存为零,导致程序打印“ 5”和“ apple”。但是,这并不总是会发生。

另外,strlen的结果类型为size_t,应使用%zu而不是%d打印。

int a[] = {'a','p','p','l','e'};中,编译器创建int的数组。当您在printf("%s\n",a);中使用它时,当int期望指向printf的指针时,您将传递指向char的指针。 C的标准未定义其行为。常见的结果是,printfint数组中的字节当作char的数组来处理,尽管这不能依靠-C实现的实际行为可能变化。

由于intchar宽,因此包含值int的{​​{1}}通常包含一个值为a的字节和一个或多个带有值{值零。它还可能包含填充位。 a中字节的顺序不是由C标准定义的。如果包含int的字节恰好是内存中的第一个字节,而随后的字节为零,则a可能会打印“ a”。但是,如果首先包含零的字节,则printf会将其视为字符串的末尾,并且不打印任何内容。

同样,该行为不是由C标准定义的。上面的内容仅说明了您看到的内容将如何打印,而不是其他情况下的预期结果。

答案 2 :(得分:1)

int[]传递到strlen()是错误的。 strlen()需要字符。即使您提供了其他内容(并且关闭或忽略了编译器的所有警报警告),strlen()也会将给定地址解释为char*(无论其实际包含什么)。

严格来说,这是undefined behavior

进行一些调查,我们可以探讨可能发生的情况:

char a[] = {'a','p','p','l','e'};

定义5个字符的数组。从内存中转储时,看起来可能是这样:

0x61 0x70 0x70 0x6c 0x65 ???? ???? ????

int a[] = {'a','p','p','l','e'};,假设{end {1}}是32位,采用低字节序,则可能看起来像这样:

int

0x61 0x00 0x00 0x00 0x70 0x00 0x00 0x00 0x70 0x00 0x00 0x00 0x6c 0x00 0x00 0x00 0x65 0x00 0x00 0x00 ???? ???? ???? ???? 重新解释为a[]char*会做什么),这将导致字符串长度为一。

但是,它仍然是未定义的行为...

答案 3 :(得分:1)

取决于硬件和实现,int的长度可以超过2个字节。

在小尾数系统上,第一个字节为ASCII码“ a”,第二个字节为零(最大为sizeof(int))。因此,任何字符串函数都将其视为单个字符串。

Big endian系统将具有相反的字节顺序,如果我们将此int arrar解释为char数组,则第一个字符将为零,这将终止该字符串,并且其长度将为零。

您的第二个示例是错误的,因为您没有结尾的零,并将其用作字符串会调用UB。

您的char表初始化应为:

char a[] = {'a','p','p','l','e', 0};

char a[] = "apple";

由于字符串文字初始化也添加了终止nul。

答案 4 :(得分:0)

在32位编译器上,int占用4个字节,char占用1个字节。 如果将整数数组传递给strlen,它将扫描整数a中的第一个字节,下一个 3个字节为0,因此strlen在第二个字节处停止,并将length显示为1

答案 5 :(得分:0)

  

我在一本书中读过,字符数组应以\0结尾...

仅当您要将字符数组解释为字符串时才需要。在C语言中,字符串实际上是一维字符数组,以空字符\0结尾。

在您的第一个示例中,char数组a只是字符数组。您很幸运strlenprintf给出了预期的输出。 strlen函数返回终止的空字符之前的字符数。在这种情况下,数组a之后的内存必须为0。因此,您将从strlen获得预期的输出。出于同样的原因,printf也可以按预期方式工作,因为它写入的每个字节一直到并且不包括第一个空终止符。

在第二个示例中,您将一个整数指针传递给strlen

printf("%d\n",strlen(a));

编译器必须在其上发出警告消息,因为strlen的参数类型为const char *,并且您正在传递它int *

此外,在printf中,您将参数作为整数指针。 %s格式说明符期望使用char指针。在这种情况下,行为是不确定的。