为什么使用太长的初始化程序初始化的字符串会表现得像这样

时间:2016-12-09 06:55:25

标签: c string pointers

#include<stdio.h>

void main()
{
    int i;
    char str[4] = "f4dfkjfj";
    char str2[3] = "987";
    char str3[2] = {'j','j','\0'};

    //printf("%c\n",str[1]);
    //printf("%c\n",1[str]);

    puts(str);
    puts(str2);
    puts(str3);           
}

输出观察:

  • 打印str2打印str2str的内容。
  • 打印str3打印str3str2str

为什么在打印非nul结束字符串时出现这种行为,它与先前定义的字符串连接,直到puts()函数遇到“\ 0”字符(此函数打印直到遇到nul)?

(注意:我故意用太长的初始化字符串初始化它们)

3 个答案:

答案 0 :(得分:5)

  

(注意:我故意用太长的初始化字符串初始化它们)

然后你也应该注意副作用。

包含所有数组的问题,它们不是以空值终止的,因此它们 字符串

引用C11,章节§7.1.1,术语定义,(强调我的

  

string 是一个连续的字符序列由第一个null终止并包含   字符即可。 [...]

将它们与字符串处理函数(如puts())一起使用会调用 undefined behavior ,因为搜索null-terminator的函数会超出范围(即,在允许的内存区域之外)并导致无效的内存访问

再次引用标准,第7.21.7.9章,

  

puts函数将s 指向的字符串写入stdout指向的流,   并在输出中附加换行符。终止空字符不是   写入。

期望的参数是 string ,代码中没有任何参数。

即FWIW,对于托管环境,main()的推荐签名至少为int main(void)

答案 1 :(得分:3)

初​​始化

  char str[4] = "f4dfkjfj";

以及

  char str2[3] = "987";

  char str3[2] = {'j','j','\0'};

不正确,因为表达式char str[4]为数据分配4个字节,但数据 - "f4dfkjfj"需要9个字节 - 可见字符为8个字节,'\0'为1个字节。

<强>更新

让我们考虑以下示例

#include<stdio.h>

void main()
{
    int i;
    char str[4] = "f4dfkjfj";
    char str2[3] = "987";
    printf("Address of str2 = %p and size is %d bytes\n", str2, sizeof(str2));
    printf("Addres     |  Data in memory\n");
    char * ptr;
    for (ptr = str2 - 2; ptr <= str2 + 5; ptr++)
    {
        printf("%p   | %c\n", ptr, *ptr);
    }
}

在Windows 7下的Visual Studio 2013中,我看到以下内容:

enter image description here

但如果我将char str2[3] = "987";更改为char str2[4] = "987";,结果将为

enter image description here

puts(str2)尝试char str2[4] = "987";,您会看到差异。

注意:每次内存地址(在本地变量的堆栈中)都可以(可以)不同,但是分配的内存(更改或不更改)周围的数据更重要。

答案 2 :(得分:2)

这些行是约束违规。编译器应该给出一条错误消息,并且程序的行为是完全未定义的:

char str[4] = "f4dfkjfj";
char str3[2] = {'j','j','\0'};

违反的约束是阵列的初始化程序太多。 (C11 6.7.9 / 2)

但是char str2[3] = "987";是正确的,有一种特殊情况,当从字符串文字初始化数组时,如果数组中没有空间,则允许忽略空终止符。 (C11 6.7.9 / 14)

继续将str2传递给期望以null结尾的字符串的函数会导致未定义的行为。