第十一章 字符串和字符串函数

GitHub地址,欢迎 star

11.1 表示字符串和字符串 I/O

字符串是以空字符(\0)结尾的 char 类型数组。

11.1.1 在程序中定义字符串

1、字符串字面量(字符串常量
用双引号括起来的内容称为字符串字面量(string literal),也叫作字符串常量(string constant)。双引号中的字符和编译器自动加入末尾的 \0 字符,都作为字符串储存在内存中。

字符串常量属于静态存储类别(static storage class),这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的生命期内存在,即使函数被调用多次。用双引号括起来的内容被视为指向该字符串储存位置的指针。这类似于把数组名作为该数组位置的指针。

2、字符串数组和初始化
注意最后的空字符。没有这个空字符,这就不是一个字符串,而是一个字符数组。

在指定数组大小时,要确保数组的元素个数至少比字符串长度多 1(为了容纳空字符)。所有未被使用的元素都被自动初始化为 0(这里的 0 指的是 char 形式的空字符,不是数字字符 0)。

3、数组和指针
数组形式(ar1[])在计算机的内存中分配为一个内含 29 也元素的数组(每个元素对应一个字符,还加上一个末尾的空字符 ‘\0’),每个元素被初始化为字符串字面量对应的字符。通常,字符串都作为可执行文件的一部分储存在数据段中。当把程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区(static memory)中。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。注意,此时字符串有两个副本。一个是在静态内存中的字符串字面量,另一个是储存在 ar1 数组中的字符串。

伺候,编译器便把数组名 ar1 识别为该数组首元素地址(&ar1[0])的别名。这里关键要理解,在数值形式中,ar1 是地址常量。不能更改 ar1,如果改变了 ar1,则意味着改变了数组的储存位置(即地址)。可以进行类似 ar1 + 1 这样的操作,标识数组的下一个元素。但是不允许进行 ++ar1 这样的操作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左值)不能用于常量。

指针形式(*pt1)也使得编译器为字符串在静态存储区预留 29 个元素的空间。另外,一旦开始执行程序,它会为指针变量 pt1 留出一个储存位置,并把字符串的地址储存在指针变量中。该变量最初执行该字符串的首字符,但是它的值可以改变。因此,可以使用递增运算符。

字符串字面量被视为 const 数据。由于 pt1 执行这个 const 数据,所以应该把 pt1 声明为指向 const 数据的指针。这意味着不能用 pt1 改变它所指向的数据,但是仍然可以改变 pt1 的值。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据,除非把数组生命为 const。

总之,初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。如下程序清单。

/** 字符串地址 */
#include <stdio.h>
#define MSG "I'm special"
int main()
{
    char ar[] = MSG;
    const char *pt = MSG;
    printf("address of \"I'm special\": %p \n","I'm special");
    printf("           address ar: %p\n",ar);
    printf("           address pt: %p\n",pt);
    printf("        address of MSG: %p\n",MSG);
    printf("address of \"I'm special\": %p \n","I'm special");
    return 0;
}

下面是在我们的系统中运行该程序后的输出:
address of “I’m special”: 00403178
address ar: 0060FEF0
address pt: 00403178
address of MSG: 00403178
address of “I’m special”: 00403178

该程序的输出说明了什么?第一,pt 和 MSG 的地址相同,而 ar 的地址不同,这与我们前面讨论的内容一致。第二,虽然字符串字面量 “I’m special” 在程序的两个 printf() 函数中出现了两次,但是编译器只使用了一个存储位置,而且与 MSG 的地址相同。编译器可以把多次使用的相同字面量储存在一处或多处。另一个编译器可能在不同的位置储存 3 个 “I’m special”。第三,静态数据使用的内存与 ar 使用的动态内存不同。不仅值不同,特定编译器甚至使用不同的位数表示两种内存。

4、数组和指针的区别
初始化字符数组来储存字符串和初始化指针来指向字符串有何区别?例如,假设有下面两个声明:
char heart[] = “I love Tillie!”;
const char *head = “I love Millie!”;

两者主要的区别是:数组名 heart 是常量,而指针名 head 是变量。那么,实际使用有什么区别?
只有指针表示法可以进行递增操作。

这类似于 x = 3; 和 3 = x; 的情况。赋值运算符的左侧必须是变量(或概括地说是可修改的左值),顺带一提,head = heart; 不会导致 head 指向的字符串消失,这样做只是改变了储存在 head 中的地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值