《C Prime Plus》-11.1学习笔记

126 阅读5分钟

重新认识字符串

一开始学习编程只是单纯的把字符串当成普通的一连串字符,而后来在网上看碎片化的视频得知字符串是很重要的一种数据类型,然后学到书中这个章节,才知道字符串是比较复杂的东西QAQ。

字符串的定义

字符串是一连串字符和结尾的空字符\0组成的,如果没有结尾的空字符\0,那么仅仅只是一个字符数组

字符串字面量与字符串常量

双引号内的内容被称为字符串字面量,字面量这个概念非常重要,在前一章《数组》就有介绍,平时我们用变量名来储存值,而字面量也可看做一种变量名(实际上是常量),他存储的值就是他字面上的值,比如整数5的值就是5(废话),5这个字符就是整数5的字面量。

字符串字面量本质上是指向该字符串内存地址的指针。这点尤为重要!(与数组名本质上也是指针类似)

编译器会自动为双引号内的内容结尾处加上空字符\0

字符串常量属于静态内存类别(内存分为动态内存和静态内存,动态内存在程序运行中可以修改里面的值,而静态内存在编译时就产生了,程序运行时静态内存里的值不允许被改变)。字符串常量只会被存储一次,存在于整个程序的生命周期中。

字符串声明和初始化

定义一个字符串时,必须让编译器知道这个字符串需要多少空间,如:

char m1[40]="Hello World!";

方括号内的整数就是这个字符串的所占内存长度,并且一定要比字符串内容里的长度要大至少1,因为结尾空字符\0也占一个长度(虽然没显示出来)。

但是,每次都指定字符串空间大小很麻烦,对于不需要被修改的字符串,指定内存空间这件事可以交给编译器去做,省略方括号内的整数,编译器就会自动为其分配与内容长度刚刚好的内存空间。

const char m1[]="Hello World!";

但如果声明一个后续会修改的字符串,就必须先指定最大长度,给修改留下操作空间。

重点: 字符串的声明可以与数组一样,用两种表示法。一种是指针表示法:

const char *pi = "Hello World!";

一种是数组表示法:

const char ar[]="Hello World!";

对于指针表示法,字符串字面量被视为const数据,需要用指向const数据类型的指针来指向它,并且字符串字面量是不能被修改的。

而数组表示法,实际上是先在静态内存区先分配了内存给这些字面量,然后又创建了一个新的内存空间,这个空间属于名为ar的数组,然后计算机把之前字面量的数据拷贝到这个数组得内存空间中,这也是两种表示法的本质区别。另一个区别是,只有指针表示法可以递增操作,而数组名在字符串这里是不能递增的。

对于非const,数组的元素是变量,但数组名永远是常量。

对于字符串字面量,如果要用指针表示法来创建,必须用const指针!

字符串数组

字符串也可以作为元素,来组成一个数组,也就是字符串数组也同时可以数组表示法和指针表示法:

const char *ar1[2]={"Hello World!","Ni Hao Shi Jie!"};

上面这里,ar1是一个存储着两个字符串指针的指针数组

const char ar2[2][40]={"Hello World!","Ni Hao Shi Jie!"};

ar2就是正常的二维数组了,由两个最大长度为40的字符串组成。

这里两个数组里元素都是字符串字面量,指针表示法里所存储的是单次存储在静态内存地址的字符串字面量;而ar2这样的二维数组,是先经历了在静态内存地址创建字符串字面量,然后把这些字面量拷贝到数组,也就是这些字符串被存储了两次。ar2也是一个矩形数组,规定了每个元素的最大长度,没有内容那些内存空间都预先填充了空字符\0

指针和字符串

指针对于字符串也是十分重要,以下是指针与字符串的一些基本操作

#include <stdio.h>

int main(void) {
    char *pi = "Hello Wolrd!";
    printf("%s\n", pi);  
    printf("%c\n", *pi); //解引用并以字符输出,打印首字母;
    printf("%p\n", pi);  //以地址输出,打印字面量的静态内存地址;
    puts(pi);
    return 0;
}

为什么printf("%s\n", pi);可以直接打印出整个字符串?不应该报错吗?或者至少也只是打印出首字母才对,因为pi存储的只是首个字符的地址而已。虽然此节书中并未做解答,但实际上是因为printf中%s的传参方式决定的,printf中%s是不断从指针所存储的地址开始传输值知道接收到的值是空字符\0

总结

字符串看着很简单,实际上很复杂啊。