C 更改数组中的元素值不能正确反映

C 更改数组中的元素值不能正确反映,c,string,initialization,C,String,Initialization,我需要为我的计算机科学项目理解这一点,但我不确定发生了什么,代码是 #include <stdio.h> void main () { char x[6] = "12345\0"; char y[6] = "67890\0"; y[7]='A'; printf("X: %s\n",x); printf("Y: %s\n",y); } #包括 空干管(){ 字符x[6]=“12345\0”; 字符y[6]=“67890\0”; y[7]='A'

我需要为我的计算机科学项目理解这一点,但我不确定发生了什么,代码是

#include <stdio.h>
void main () {
    char x[6] = "12345\0";
    char y[6] = "67890\0";
    y[7]='A';
    printf("X: %s\n",x);
    printf("Y: %s\n",y);
}
#包括
空干管(){
字符x[6]=“12345\0”;
字符y[6]=“67890\0”;
y[7]='A';
printf(“X:%s\n”,X);
printf(“Y:%s\n”,Y);
}
输出为:

X:1A345
Y:67890


现在,当我清楚地指定
y
时,我不知道为什么
A
x
数组的第二个元素中

记住

  • 字符串文字由定义以null结尾
  • C数组基于
    0
    。索引
  • 澄清一下,

    • 当您尝试将字符串文字(如
      “12345\0”
      放入一个包含6个
      char
      元素的数组中时,编译器将尝试在字符串文字的元素后添加一个额外的null,这将成为一种尝试,以访问过去分配的内存区域,而该内存区域反过来调用。如果
      char x[6]=“12345\0”
      ,则不需要将
      \0
      作为字符串文本的一部分。另外,在为初始化提供字符串文本时,最好将元素的分配(换句话说,数组的大小)留给编译器。你可以用

      char x[ ] = "12345";
      
    • 然后,拥有一个维度为
      x
      的数组,对该数组的有效访问权限是从索引
      [0]
      [x-1]
      。访问已分配内存之外的内容也是UB。例如,上面的数组
      x
      ,可以(应该)在如下范围内安全访问

      len = strlen(x);      //get the length of the string
      for (int i = 0; i < len; i++)
      {
           x[i] = i*i;  //access the array
      
      }
      
      len=strlen(x)//获取字符串的长度
      对于(int i=0;i

    也就是说,请注意
    main()
    的建议签名是
    int main(void)
    当您在C中分配长度为n的数组时,实际上是在内存中分配了长度为n+1的数组,因为编译器为空终止符创建了空间。您不需要添加空终止符。这实际上导致了未定义的行为


    尝试删除空终止符。让我知道会发生什么

    您指定有两个数组,每个数组的大小为6个字节;这意味着它们将有编号为0到5的元素(因为C使用基于零的数组偏移量,而不是像其他一些语言那样基于一个偏移量)

    由于您试图访问
    y[7]
    ,因此您正在访问一个不属于数组的元素。C不进行边界检查,因此会进入未定义的行为。在您使用的编译器、编译器选项、操作系统、处理器体系结构等的特定组合中,
    x
    y
    之间没有空格,并且
    x
    y
    后面;因此,当您访问数组
    y
    末尾后面两处的元素时,您将访问数组
    x
    占用的内存。更改其中一个元素(操作系统/编译器(选项)/处理器),结果可能会大不相同。但这仍然不是你所期望的


    还请注意,
    \0
    是多余的,这将导致编译器有效地尝试将
    “12345\0\0”
    分配给数组,数组为七个字节(因此溢出)。它可能会发出警告,但不是必须发出警告。

    这里有一个大问题:

    char y[6] = "67890\0";
    y[7]='A';
    
    y
    是一个具有
    6个
    元素的数组,从
    0
    索引(即
    0
    1
    5
    )。这意味着
    y[7]
    是一个无效的表达式,给它赋值是未定义的行为

    char x[6] = "12345\0";
    char y[6] = "67890\0";
    y[7]='A';
    
    您在
    y
    数组的边界之外写入,由于
    x
    y
    数组在内存中的放置方式,您重写了
    x
    的第二个元素

    使用不同的操作系统,编译器或编译器标志可以在内存中产生不同的
    x
    y
    变量位置,代码将在其他地方写入
    'a'
    。甚至可以在
    只读
    内存区域中写入,在这种情况下,操作系统会因为页面错误异常而终止您的程序

    这就是它被称为未定义行为的原因

    char x[6] = "12345\0";
    char y[6] = "67890\0";
    y[7]='A';
    
    “C/C++”中的数组索引以零开头。这意味着您只能访问x,y的[0-5]索引

    访问y[7]会导致未定义的行为;在这种情况下,堆栈很可能向下增长并覆盖x的第二个元素(即x[1])


    相关读取:

    我对此不是100%确定,但从我所看到的情况来看,您已将数据写入数组边界之外,并将其写入相邻块中的内存

    char x[6] = "12345\0";
    char y[6] = "67890\0";
    
    混淆可能来自于,如果在x之后声明y,那么内存肯定应该如下所示:

    x[0]x[1]x[2]x[3]x[4]x[5]y[0]y[1]y[2]y[3]y[4]y[5]

    这可以归结为一种叫做大端点对小端点的东西

    在大端存储中,最高有效字节存储在最小地址中。在little endian中,它存储在最大的地址中

    许多计算机使用iLittle-endian系统(例如,许多intel硬件使用iLittle-endian系统),这可能意味着您的阵列实际上是这样存储在内存中的:

    y[0]y[1]y[2]y[3]y[4]y[5]x[0]x[1]x[2]x[3]x[4]x[5]


    如果是这种情况,那么调用y[7]实际上将对应于设置x[1],即x数组的第二个元素。导致数据覆盖并产生以下结果:X:1A345 Y:67890

    从阵列中删除
    \0
    ,因为它们已经包含在内,为什么要访问
    Y[7]
    ?它调用未定义的行为。这就是我们必须解释的,为什么A出现在另一个数组中,这是我在项目中得到的代码,谢谢:)解释:代码调用未定义的行为。任何东西