为什么字符串长度加上C的容量加一?

时间:2017-04-15 09:34:18

标签: c string indexing

您的字符串长度应该是您希望它能够容纳的最大字符数。足够逻辑:字符串以NULL字符终止。

这是大多数新手得到的一般建议。然而,随着我在编程方面的发展,现在似乎并不是那么正确。

任何类型的数组(intchar)的索引从0开始。因此,大多数数组的最大索引值比其数值小1。它与字符串相同,但由于它在末尾有一个额外的字符,它会增加1。因此,字符串长度与其中的字符数相同。

要查看我是否正确,请参阅以下代码段:

char str[9];
scanf("%s", str);
printf("%d", strlen(str));

使这个成为一个完整的程序,然后运行它。输入123456789,保证9个字符的长文本,然后查看结果。它可以保存字符串,当然,字符串长度为9

我甚至目睹了许多专家程序员说字符串大小应该加上其容量。这个建议在很大程度上是一个神话,或者我在某个地方出错了?

修改

我想说我想创建一个可以容纳Arr个元素的整数数组x。由于索引值从Arr开始而不是x,因此0的最后一个元素的索引值将比1小一个。所以,它的长度为x-1

那你怎么声明呢?我这样做:int Arr[x-1];。我不认为这有任何问题。

现在,如果Arrchar类型数组(即字符串),则Arr的长度将比其int对应的长度多一个,因为它具有最后一个额外的NULL字符。这将最终为:(x-1)+1=x

Code to demonstrate this

那么为什么这次声明必须是char Arr[x+1]而不仅仅是char Arr[x]

7 个答案:

答案 0 :(得分:4)

根据C标准相对于转换说明符s的描述(7.21.6.2 fscanf函数)

  

s匹配一系列非空白字符.279)如果没有l长度   修改器存在,相应的参数应为指针   字符数组的初始元素足以接受   序列和一个终止空字符,将被添加   自动

因此,如果要输入字符序列123456789,则会尝试编写以下字符“

{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '\0' }`
声明为

的数组中的

char str[9];

如图所示,序列包含10个字符,而数组只能容纳9个字符。因此,数组之外的内存将被覆盖,因此程序具有未定义的行为。

在与C ++相反的C中,您可以通过以下方式初始化字符数组

char str[3] = "Bye";

在这种情况下,终止零将不会用作数组的初始值设定项。也就是说,数组不包含字符串,只包含字符

{ 'B', 'y', 'e' }

但是,您可能不会将标准C函数strlen应用于此数组,因为该函数会对字符进行计数,直到遇到终止零并且数组没有这样的字符。

您应该区分sizeof运算符返回的值和标准C函数strlen返回的值。

例如,如果您有这样的声明

char str[10] = "Hello";

然后sizeof运算符sizeof( str )返回10,即数组有10个大小等于1的元素(sizeof( char)总是等于1)。

但是,如果您将应用标准C函数strlen,则返回的值将等于5,因为该函数会在终止零之前计算所有字符。

您可以编写例如

str[8] = 'A';

然而,如果ypu应用函数strlen,您将再次获得值5,因为在元素str[8]之前,值为'A',存在终止零。

答案 1 :(得分:4)

你对索引是正确的。但是:

char str[9];

以这种方式声明字符串时,数字9数组长度。减去NULL,只能有8个字符,而不是9.数组的长度是数组中元素的数量,而不是您认为的最大索引值。你会混淆这些条款。

为什么你的程序可以解释许多其他答案甚至评论。

答案 2 :(得分:2)

  

任何类型的数组(无论是int还是char)的索引都从0开始。

是的,那是真的。

  

因此,所有数组大小都比它们的数值小一个。

没有。用于索引的第一个值仅影响索引,而不是大小。例如,1大小的数组只有一个索引0.它是最大索引值,比索引大小小一个,而不是相反。

在声明char str[9];中,值9是数组大小,而不是最大索引值。

您的示例似乎起作用的原因是,未定义的行为不会 导致崩溃或错误消息。

答案 3 :(得分:1)

你是对的,数组索引从0开始,但是一个char str [9]的长度为9,所以最高的索引是8.你的例子似乎有效,但它很容易造成错误。您也可以在代码中键入1234567890,它将输出10,因为程序无法知道数组的长度。

当您定义该char数组时,您在堆栈上为它创建一个9字节的空间,但是当您将它传递给scanf时,char []会转换为char *指向数组中第一个元素的指针。因此,scanf无法知道数组的长度,并将输入写入内存,从str指向的位置开始。它将\ 0字符写在为数组保留的空间之外!但是当它再次传递给strlen时,它无法看到数组的大小并继续扫描内存中的\ 0,它在10个字节后找到,所以它假设长度为10。

像@Ajay一样,Brahmakshatriya在他的回答中表明这可能导致错误,因为字符串外部的空间可以用于另一个变量,例如另一个字符串,然后可以将不同的数据写入\ 0的字节。

答案 4 :(得分:0)

看到这个 - > Ideone

int main(void) {
    char a[16];
    char b[16];
    scanf("%s",a);
    b[0]='a';
    b[1]='\0';
    printf("%s %d %p %p", a, strlen(a), a, b);
    return 0;  
}

这几乎是您展示的代码的副本。对于16长度的给定输入(数组大小也为16),打印的长度为17。

现在我们已经确定你所说的不正确,我们将看看为什么它为你打印9而不是我发布的例子。

您创建了大小为9的数组(分配了9个字节)。 然后,您将9个字节的数据存储到其中,并通过在第十个字节上写入的'\0'终止它。由于任何(幸运的)重要的是没有使用该空间,因此数据适合。

然后当你打电话给strlen时,它给了你9个。

现在我创建了一个16字节的数组,然后跟着另一个放在它后面的数组。现在当它读取16个字节并用'\0'终止它时,它写入了b。

我写信给b再次覆盖了它。 由'\0'撰写的scanf已经消失。

然后strlen计算长度溢出到b并在看到'\0' b[1]时停止。

所有这些都是未定义的行为

答案 5 :(得分:0)

  

...因此,字符串长度与其中的字符数相同。

如果我们将终止null视为字符,则此语句是正确的。但是,保存字符串所需的存储空间比其中的字符数多一个。 (强调'字符串'是因为作为数据类型的字符串需要额外的终止空值,这需要存储。)

答案 6 :(得分:-1)

试图证明我的观点:

代码

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

int main()
{
    char str[23];
    scanf("%s", str);
    printf("String length = %d\n", strlen(str));
    printf("String element  ---  Index number");
    int index=0;

    while (str[i]!='\0')
    {
        printf("\n%c  ---  %d", str[i], index);
        i++;
    }

    printf("\nNULL  ===  %d", index);

    return 0;
}

样本输入

graphing

样本输出

String length = 8
String element  ---  Index number
g  ---  0
r  ---  1
a  ---  2
p  ---  3
h  ---  4
i  ---  5
n  ---  6
g  ---  7
NULL  ===  8