深度剖析“字符串与数组、指针”的关系

您所在的位置:网站首页 数组和指针性质相同吗怎么设置 深度剖析“字符串与数组、指针”的关系

深度剖析“字符串与数组、指针”的关系

2024-07-13 05:15| 来源: 网络整理| 查看: 265

一、字符串的结构

字符串由字符组成,把一个个字符串起来就构成了字符串。

字符串的基本结构如下所示。

字符

‘H’

‘e’

‘l’

‘l’

‘o’

‘\0’

字符串

“Hello”

字符串的格式规范:

①使用时需加双引号。

②字符串最后一个字符为’\0’。它是字符串结束的标志,不在显示器上显示,但占用一个字符空间。

二、字符串的本质

在C语言中,字符串是以字符数组的形式存储的。所以,可以像处理普通数组一样处理字符串,只需要注意输入输出和字符串函数的使用。

三、字符串的定义

字符串变量的定义方法:

char s[6];

①s是变量名。

②字符串的长度为实际字符个数加1(因为要给空字符’\0’留个位置)。

四、字符串的赋值

1.数组形式:赋值

因为字符串本质上就是数组,因此它的赋值也具有数组的特点:只能在初始化时用等号。

(1)字符串的初始化

方法一:指定大小

char s[6]="Hello"; //指定字符串最大占用6个的char型内存单元

方法二:省略大小

char s[]="Hello"; //自动计算出最大占用6个的char型内存单元

(2)初始化后的赋值

和数组一样天生低人一等,给字符串变量赋值时,只有初始化时才可以使用“=”,之后均需使用strcpy()函数。

char s[10]; strcpy(s, "Hello"); //s指上一行定义的s[10]数组的首元素的内存地址。

2.指针形式:传址

字符串的本质是数组,数组的本质是占用连续内存单元的多个变量(每个内存单元大小相同,就像把一整板豆腐切成大小相同的小块,每一块可看成一个内存单元,每个内存单元存储着一个变量——数组元素)。内存是和指针密切相关的,因为指针保存的就是内存地址。因此,可以通过把字符串的首地址传给指针进行赋值,这样的指针称为“指向字符串的指针”。

举个例子:

#include int main() { char str[] = "Hello, World!"; // 定义一个字符数组(字符串) char *ptr = str; // 定义一个指向字符的指针,并让它指向str的首地址 // 使用指针打印字符串 printf("%s\n", ptr); // 使用数组名(数组名相当于指向首元素的指针)打印字符串 printf("%s\n", str); // 遍历字符串 while (*ptr != '\0') { // '\0' 是字符串的结束符 printf("%c", *ptr); ptr++; // 移动指针到下一个字符 } printf("\n"); return 0; }

前两行代码可进一步简化成:

char *ptr = "Hello, World!";

这种赋值形式看起来就像数组不存在一样,但本质上都是让ptr指向字符串"Hello, World!"首字符’H’的地址,只不过前者使了数组名这个“中介”。

但两种赋值方式却有一个实质性的关键不同,这一点一定要注意。

就是前者定义的字符串可以改变,而后者字符串会被视为字符串常量,不可以改变。

因为两种初始化方法代表着两种不同的字符串存储方式。不同点如下:

(1)可写性

char str[] = "Hello, World!":这是用数组的方式初始化,字符串存储在程序的可写数据段(如堆或栈)中,因此你可以修改str数组中的字符。

char* ptr = "Hello, World!":因为是直接将字符串的首地址赋给指针,这里的字符串会被当作字符串常量,存储在程序的只读数据段(也称为文本段或代码段)中,因此用这种方式初化始后字符串是不能改变的。如果强行修改会导致未定义行为。如下面的代码:

#include int main() { //字符串会被视为常量字符串 char *ptr="Hello, Dad!"; //试图改变字符串常量,运行会出错 *(ptr+8) = 'o'; //指针偏移式赋值 ptr[9] = 'g'; //数组式赋值 printf("通过指针访问字符串: %s\n", ptr); return 0; }

如果想用指针修改字符串,数组这个中介就派上用场了。改后代码如下:

#include int main() { //数组做中介 char str[] = "Hello, Dad!"; char *ptr=str; //改变字符时不会出错 *(ptr+8) = 'o'; //指针偏移式赋值 ptr[9] = 'g'; //数组式赋值 printf("通过指针访问字符串: %s\n", ptr); return 0; }

(2)生命周期

数组如在函数内部声明,它的生命周期就是该函数的执行期间。如在全局或静态上下文中声明,生命周期就是整个程序的执行期间;常量字符串的生命周期是整个程序的执行期间。

四、二维字符数组与字符指针数组

二维字符数组指元素为字符的二维数组。二维数字相当于由行列组成的矩阵,所以二维字符数组实质上是字符的二维矩阵。

二维字符数组常用于存储多个字符串,我们可以用最原始的二维数组初始化方式对二维字符数组赋值。

#include int main() { char str[3][10] = { {'h', 'u', 'm', 'a', 'n', '\0'}, {'d', 'o', 'g', '\0'}, {'c', 'a', 't', '\0'}, }; printf("%s %s %s\n", str[0], str[1], str[2]); return 0; }

这种赋值方式实在是太麻烦了,因而简化成这样:

#include int main() { char str[3][10] = {"human", "dog", "cat"}; printf("%s %s %s\n", str[0], str[1], str[2]); return 0; }

和字符串赋值一样,也可以通过将字符串的首地址赋给指针来进行赋值。不同的是,现在有多个字符串,因而相应地就要有多个指针,分别指向每个字符串的首地址。

我们可以像之前一样,通过数组这个中介间接指向每个字符串:

#include int main() { char str[3][10] = {"human", "dog", "cat"}; char* ptr[3]; ptr[0]=str[0]; ptr[1]=str[1]; ptr[2]=str[2]; printf("%s %s %s\n", ptr[0], ptr[1], ptr[2]); return 0; }

上面的ptr数组被叫做:字符指针数组(Character Pointer Array),它是一个一维数组,其元素是指向字符的指针。

也可以抛弃数组,直接指向字符串:

#include int main() { char* ptr[3]; ptr[0]="human"; ptr[1]="dog"; ptr[2]="cat"; printf("%s %s %s\n", ptr[0], ptr[1], ptr[2]); return 0; }

不过这种方式还是显得有些麻烦,不如将指针数组的定义与赋值集成为一条语句,于是又简化成这样:

#include int main() { char* ptr[3]={"human", "dog", "cat"}; printf("%s %s %s\n", ptr[0], ptr[1], ptr[2]); return 0; }

看起来上面的数组貌似存储的是3个字符串,但你要明白它们存储的其实只是3个指针。

再强调一遍,这个数组只是个一维数组,它是指针数组,即数组的元素都是指针。

同样地,有无数组做中介会影响字符串的可写性。如果通过数组中介赋值,可以改变字符串:

#include int main() { char str[3][10] = {"human", "dog", "cat"}; char* ptr[3]; ptr[0]=str[0]; ptr[1]=str[1]; ptr[2]=str[2]; //用数组做中介,可以改变字符串 ptr[1][1] = 'a'; ptr[1][2] = 'd'; ptr[2][0] = 'm'; ptr[2][2] = 'm'; printf("%s %s %s\n", ptr[0], ptr[1], ptr[2]); return 0; }

如果直接将指针指向字符串,没通过数组赋值,这些字符串同样会被视为字符串常量,不能改变。

#include int main() { char* ptr[3] = {"human", "dog", "cat"}; //指针直接指向字符串时,不可以改变字符串 ptr[1][1] = 'a'; ptr[1][2] = 'd'; ptr[2][0] = 'm'; ptr[2][2] = 'm'; printf("%s %s %s\n", ptr[0], ptr[1], ptr[2]); return 0; }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3