字符数组的长度不确定

时间:2019-07-02 03:20:01

标签: c arrays string

我一直在想两者之间有什么区别

    char[] = "hello world"

    char[20] = "hello world"

我试图写这段短代码:


    #include <stdio.h>
    #include <stdlib.h>


    int main(){
        int i;
        char str[20] = "hello world";
        for( i = 0; i<20; i++){
            if(str[i]=='\n')
                printf("\nExit character newline");
            else if(str[i]=='\0')
                printf("\nNull terminated..");
            else
                printf("\nCur: %c", str[i]);
        }
        return 0;

    }

输出:


Cur: h
Cur: e
Cur: l
Cur: l
Cur: o
Cur:
Cur: w
Cur: o
Cur: r
Cur: l
Cur: d
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..

另一方面,当我没有明确定义数组大小而只是使用

    char[] = "hello world"

它给了我这个输出:

Cur: h
Cur: e
Cur: l
Cur: l
Cur: o
Cur:
Cur: w
Cur: o
Cur: r
Cur: l
Cur: d
Null terminated..
Cur: 
Null terminated..
Null terminated..
Null terminated..
Cur: 
Cur:  
Cur: a
Null terminated..

我对上面的输出感到困惑。 char [] =“ hello world”是否仅以12个元素结尾,并且在最后一个元素中填充了空终止符?另外,如果我用%s打印char,我的假设会正确吗?

4 个答案:

答案 0 :(得分:3)

声明`char str [] =“ hello world”保留12个字符的空间,最后一个为零。与其他一些语言不同。但是,C实现通常不竭力捕获越界数组访问。通常,尝试读取超出字符串末尾的内容将访问紧随其后的所有存储内容,但除非有人使用一种可以控制对象放置的实现(例如,在翻译单元中仅包含一个对象,并且使用链接器规范强制将该翻译单元的数据直接放置在另一个翻译单元之前),超过字符串末尾的读取将不会产生可预测的结果。如果使用的是激进的优化编译器,它可能会决定可以省略任何仅在程序尝试访问数组末尾以外的数据时才有意义的代码。

答案 1 :(得分:2)

因此在第一个字符char [20]中,您留出了最多20个字符的空间,这就是为什么所有字符都在最后一个字符之后终止的原因。在第二个字符char []中,您没有预留额外的空间。因此,当您传递字符串的末尾时,似乎正在发生的事情就是您来自计算机其他部分的内存。这就是为什么在那里有随机字符的原因。

这是另一个堆栈覆盖流,它会更深入地介绍

How to declare strings in C

答案 2 :(得分:0)

在c中,您可以在数组范围之外进行读取和写入。这当然是未定义的行为。但是语言允许。

当您读取未分配给对象的内存时,您可能会得到不可预测的值,您可能还会遇到段错误

答案 3 :(得分:0)

我建议您尝试以下程序:

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

int main() {
    char str1[] = "hello world";
    char str2[20] = "goodbye world";
    printf("str1: size = %zd, len = %zd\n", sizeof(str1), strlen(str1));
    printf("str2: size = %zd, len = %zd\n", sizeof(str2), strlen(str2));
}

(如果您使用的旧编译器不接受%zd,则可以改用"size = %d, len = %d\n", (int)sizeof(str1), (int)strlen(str1)。)

编译器将为您提供看起来像这样的数组:

      +---+---+---+---+---+---+---+---+---+---+---+---+
str1: | h | e | l | l | o |   | w | o | r | l | d |\0 |
      +---+---+---+---+---+---+---+---+---+---+---+---+

      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
str2: | g | o | o | d | b | y | e |   | w | o | r | l | d |\0 |   |   |   |   |   |   |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

(实际上,尽管我没有明确显示所有内容,但可以保证在str2末尾的所有“空”单元格也将包含\0。)

通常,如果您尝试访问数组定义端之外的内存:(a)您不会找到任何有趣的东西,并且(b)这样做是非法的,尽管(c)C编译器通常会阻止您尝试。

如果您真的想看看发生了什么,请尝试运行以下循环:

for(int i = 0; i < 30; i++)
    printf("str1[%d] = '%c'\n", i, str1[i]);

您可能会看到字符串“再见世界”潜伏在str1的“结尾”内存中。如果不这样做,请尝试交换str1str2的顺序:

char str2[20] = "goodbye world";
char str1[] = "hello world";

但是,您当然在这里是“违反规则”,在任何情况下都可能看不到多余的“再见”字符串,否则您的程序将在尝试中崩溃。

还有一件事。我想回到您在评论中说过的话。你说:

  

我正在尝试找出并理解在hello世界中'd'之后的空终止符可能是什么。我期望str[]的其余部分将填充空终止符,与str[20]观察到的相同。

现在,实际上,str[20]的“其余部分都填充有空终止符”,这是因为,而 only 则是因为您为数组分配的字符数明显超过了所需。另一方面,当您说str[] = "..."时,会得到一个数组,其中精确地包含 所需的字符(包括一个终止于\0的字符)。当您声明str[] = "..."时,说“其余的都充满了...”甚至是没有意义的,因为没有“其余的”可以填补。