滴水三期:day21.1-字符数组与字符串

一、字符数组与字符串

  • 字符数组:

    char arr[10] = {'A','B','C','D','E','F'};   //编译器默认在结尾添加0x00
    char arr[3] = {'A','B','C'}; //但是如果没有留出'\0'的空间,编译器就不会自动添加
    char buffer[100] = ""; //定义了一个空字符串,字符串中每个字节默认初始为0x00
    char buffer[] = ""; //这个数组长度只有1,且默认初始为0x00
    

    注意:定义字符数组来表示字符串时,一定要在结尾给'\0',我们可以手动添加,也可以不添加让编译器自己自动帮我们在结尾处添加'\0',但是一定要注意如果让编译器添加的话,要把字符数组的宽度的长度设置好,一定要预留空间给'\0'

    image-20230225000548014image-20230225000743674 image-20230225000413239

  • 字符串:(也是字符数组的一种,特殊的赋值方式)

    char names[] = "ABCDE";  //可以省略数组大小,但是此时的数组大小应该6,因为编译器会自动在末尾加0x00
    char* p = "ABCDE";    //这个是将常量区中存储ABCDE字符串的首地址赋给p,此时p的长度是4字节,常量区的字符						   串虽然后面补0,但是不能算到字符串的长度中,所以字符串的长度应为5字节
    

    注意:编译器在后面添一个 00 做为字符串的结束标记;如果用数组定义的字符串则存在内存中,但指针定义的字符串存在常量区

  • 输出字符数组或者字符串:

    char arr1[6] = {'A','B','C','D','E','\0'};
    char arr2[6] = {'A','B','C','D','E',0};  //注意区分这里的0不是'0'
    char arr3[6] = {'A','B','C','D','E'};   //只要比5大,数组的长度随便
    char names[] = "ABCDE";
    printf("%s\n",arr1);
    printf("%s\n",arr2);
    printf("%s",names);
    char word[8];
    scanf("%7s", word);//最多读7个字符
    

    上面的结果都是一样的,都打印ABCDE

  • %s的用法:打印一个字符串,直到内存为 0 为止。这个0相当于16进制的0,平时我们用则使用0或者'\0'来表示

  • 字符串数组

    char a[][10] = {"hello","abcdefg"};
    char *a[] = {"hello","abcdefg"};
    

    image-20230213160923987

二、常量区

  • 内存分布区还记得吗?当中有一个常量区,可读不可写

    image-20211214154110985
  • 什么数据存在常量区:字符串等

三、字符串的反汇编(指针)

  • 存储在常量区的数据无法修改

    char* x = "china";   //x中存的就是存储在常量区的china字符串的首地址,x指针型变量直接指向常量区中的存储				         china字符串的首地址
    char y[] = "china";  //这里也是常量区中的china字符串,但是与指针不同的是,这里会将字符串值复制一份到给y				         字符数组变量分配的内存中(栈)
    void Func(){
    	y[1] = 'A';    //可以修改
        //*(x + 1) = 'A';  无法修改
    }
    

    x本身是可以再修改的:如x = "abc";,但是x指向的字符串"china"是无法修改的!即x指向常量区的"china"字符串,现在想用x去改变常量区的字符串,也是不可以的。具体原因看下面反汇编!

  • 反汇编角度分析char* x = "china";char y[] = "china";的区别

    1. 先来分析一下char* x = "china":因为char*类型宽度为4字节,VC编译器的执行一个函数时默认提升的堆栈空间为0x40字节,现在由于定义了一个char*类型的局部变量,所以会提升堆栈空间为0x40 + 0x4 = 0x44;而后我们发现反汇编中的操作就是将已经存储在常量区(内存中的某个区域)的字符串"china"的首地址0x422E80存入了给char*类型变量x分配的内存中(main函数的缓冲区),即**char*类型的变量x存储的是"china"字符串在常量区中的首地址**

      #include "stdafx.h"
      int main(int argc,char* argv[]){
      	char* x = "china";
      	//char y[] = "china";
          return 0;
      }
      

      D8D40B687DA1E14F316C214FD15FA91C 屏幕截图 2021-12-14 163404

      所以可以发现使用指针类型变量x操作字符串其实就是修改常量区中的字符串,那么由于常量区中的数据可读不可写,所以编译器不允许使用*(x + 1) = 'A'

    2. 再在分析char y[] = "china":这是在main函数中定义了一个char类型的字符数组变量y,接着给字符数组赋值"china"。但是在反汇编中可以发现,这里由于没有使用指针了,那么这个操作就是把已经存储在常量区的字符串"china"复制一份,将数据存到main中为字符数组变量y分配的8字节内存中(因为局部变量遵循本机尺寸)。

      #include "stdafx.h"
      void main(int argc,char* argv[]){
      	//char* x = "china";
      	char y[] = "china";
      	y[1] = 'A';
      }
      

      DD0C532CC6F2A12AFB08FD7B8EFE305B

      这里之所以复制了两次是因为char y[] = "china";字符数组变量y的长度应为6,因为末尾编译器会补0x00表示字符串结束,那么由于一个寄存器宽度为4字节,不够一次性就将常量区中的"china"常量复制到main给y变量分配的内存(缓冲区)中,所以会分两次,一次复制0x42201C开始往后的4字节(dword)数据,二次复制0x422020开始往后的2字节(word)数据,因为刚说过了要补0x00。最后我们可以发现,y[1] = 'A'的操作不是对常量区中的"china"字符串的操作,而是对复制过来的值操作,所以就可以修改!

      屏幕截图 2021-12-14 171756
  • 所以:如果想对字符串做写操作,就定义成:char arr[] = "abc";。如果只做读操作,定义成前面的或者char* arr = "abc";都是可以的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值