首页 > 编程笔记 > C语言笔记 阅读:18

C语言字符数组的用法(非常详细)

在众多的数组中,有一个特殊的数组,即字符数组,我们需要对其进行额外的讲解。

在探究字符数组之前,我们来回顾与字符串相关的知识点,看个例子:
#include <stdio.h>
int main()
{
    printf("sizeof HelloWorld = %d\n", sizeof("HelloWorld"));
    return 0;
}
使用 sizeof 操作符测量字符串 "HelloWorld" 占用了 11 字节,执行结果为:

sizeof HelloWorld = 11

内存中的字符串常量由每个字符的 ASCII 码按照顺序排列构成,每个字符仅占 1 字节,并且末尾会附上一个数值 0,指示字符串结尾,如下图所示:


图 1 字符串"HelloWorld"内部存储

字符 '0' 对应的 ASCII 码为十进制 48。为了不与字符 '0' 冲突,将标记字符串结尾的数值 0,使用转义序列 '\0' 表示。

字符数组存储字符串

由于字符串满足数组的类型相同且按顺序排列的特点,元素为 char 的数组可以用于存储字符串。

1) 初始化字符数组

我们可以声明一个 char 类型的数组,并将其初始化为 "HelloWorld",代码如下:
char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
在上述代码中,由于数组大小为 20,而初始化列表中仅有 10 个元素,数组中剩余的 10 个元素会被自动初始化为 0,即自动添加了字符串结尾标识符 '\0'。

此外,我们还可以使用更简便的字符数组初始化方式,即将初始化列表直接写成一个字符串常量,代码如下:
char str[20] = "HelloWorld";
在上述代码中,字符串常量的末尾会被自动添加 '\0' 作为字符串结尾标识符,因此上述代码等价于:
char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};

2) 省略数组大小

有时候,我们希望一个数组被初始化为某个字符串,但是又不想数清楚到底有多少个字符。此时,我们可以在数组声明时省略数组大小,此时数组的大小就是初始化列表中元素的个数。

代码如下:
char str1[] = "HelloWorld";
char str2[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};
上述两种写法是等价的,数组的大小为 11,即初始化列表中的元素个数。

3) 输出字符数组

接下来,我们可以使用 printf() 函数将字符数组中存储的字符串输出到控制台中,由于 printf() 函数的第一个参数可以接收一串字符串,因此我们可以直接将数组作为 printf() 的第一个参数,如下所示:
printf("HelloWorld");
printf(str);           //使用字符数组
在 C语言中,字符数组和字符串常量是存储字符串的两种方式。字符串常量是一种特殊类型的字符数组。当我们在 C语言中使用 printf() 函数时,可以直接将字符数组作为参数传递给该函数,因为 printf() 函数会将字符数组中的字符逐个输出到控制台中,直到遇到空字符。

此外,转换规范 %s 也可以作为字符数组的占位符,代码如下:
printf("%s", str);
下面是一个将字符数组输出到控制台中的示例:
#include <stdio.h>
int main()
{
   char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
   printf(str);
   printf("\n");
   printf("%s", str);
   printf("\n");
   return 0;
}
运行结果为:

HelloWorld
HelloWorld

字符串结尾标记'\0'

在使用字符串常量时,系统会自动为我们在字符串末尾添加 '\0' 标记字符串结束。但是在使用字符数组时,有些情况不能保证字符串末尾有 '\0',需要格外注意。

1) 初始化列表长度小于数组长度

如果使用初始化列表初始化字符数组,并且初始化列表长度小于数组长度,则数组前面的元素将被初始化为字符串,后面的元素将被填充为 0。在这种情况下,字符数组中的字符串正常结尾,因为系统在初始化期间已经为字符串添加了结尾标记 '\0'。

例如:
char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
上面的代码将数组前 10 个元素初始化为字符串 "HelloWorld",后面的 10 个元素被填充为 0。

2) 初始化列表长度等于数组长度

如果使用初始化列表初始化字符数组,并且初始化列表长度等于数组长度,则数组中的元素都被初始化为字符串。但由于初始化列表已经占用了数组中的所有空间,因此没有空间可以保存结尾标记 '\0'。

在这种情况下,字符数组中的字符串无法结尾,这可能会导致数组越界访问。例如:
char str[10] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
上面的代码将数组中的所有 10 个元素都初始化为字符串 "HelloWorld"。但由于缺少结尾标记 '\0',在使用 printf() 等函数输出该字符数组时,程序将继续输出数组外的元素,直到遇到一个 '\0' 才停止。这可能导致数组被越界访问。

例如,下面的代码将输出字符数组 str 中的所有元素,以及其后面的不确定值。
printf("%s", str);
运行结果为:

HelloWorld烫烫烫烫烫烫烫烫烫

因此,在使用初始化列表初始化字符数组时,必须确保数组中有足够的空间保存结尾标记 '\0'。

3) 初始化列表长度大于数组长度

如果使用初始化列表初始化字符数组,并且初始化列表长度大于数组长度,则将无法通过编译。这是因为初始化列表中的元素数量超过了数组的容量,无法被全部存储。

例如:
char str[5] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
上面的代码将会导致编译错误,因为初始化列表中有 10 个元素,而数组只有 5 个元素的容量。

4) 省略数组大小的情况

在 C语言中,我们有时会省略数组的大小并直接使用字符串常量来初始化数组。

例如,下面的代码:
char str[] = "HelloWorld";
这段代码省略了数组的大小,直接使用字符串常量来初始化数组。由于系统会自动为字符串常量结尾添加 '\0',因此字符串常量大小为 11。使用该字符串初始化数组,数组大小也为 11,并且最后一个字符为 '\0',即字符串正常结尾。

我们还可以使用初始化列表来初始化数组,例如:
char str[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};
在这段代码中,我们使用了初始化列表来初始化数组。该初始化列表包含了 11 个字符常量,其中最后一个字符常量为 '\0',表示字符串的结尾。使用该初始化列表初始化数组,数组大小为 11,并且最后一个字符为 '\0',即字符串正常结尾。

需要注意的是,如果我们使用以下代码初始化数组,则该数组中的字符将不会正常结尾。
char str[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
该初始化列表包含了 10 个字符常量,最后一个字符常量为 'd',因此使用该初始化列表初始化数组,数组大小为 10,并且最后一个字符为 'd'。这意味着该字符数组中的字符串无法正常结尾。

在实际编程中,我们应该避免出现这种情况,以确保程序的正确性和稳定性。

字符数组的大小及长度

在下面的代码中,我们声明一个长度为 20 的字符数组 str,并使用字符串常量"HelloWorld"对它进行初始化。这样,字符数组的前 10 个元素为 HelloWorld,第 11 个元素为 '\0'。
char str[20] = "HelloWorld";
为了测试 str 的大小,我们可以使用 sizeof 关键字,结果为 20。从结果可以看出,str 的大小为 20 字节,和我们声明的数组大小一致。因此,我们无法直接用 sizeof 关键字测量字符串的长度。

接下来,我们将提供两种方法来测量字符串的长度。

1) 使用循环测量字符串长度

一个字符串用 '\0' 标记结尾,只要知道在 '\0' 之前,有多少个字符,就能知道字符数组中的字符串的长度了。具体的代码如下:
#include <stdio.h>

int main()
{
    char str[20] = "HelloWorld";
    int len = 0;
    while(str[len] != '\0')  // 检查字符串结束符
    {
        len++;
    }
    printf("%d", len);
    return 0;
}
在程序中,我们声明了一个 len 变量,用于统计字符串长度。while 循环从第一个元素开始,检查元素是否为 '\0',如果不是,则将 len 加 1,直到元素为 '\0'。循环结束时,len 的值即为字符串的长度。运行结果为 10。

2) 使用strlen()测量字符串长度

我们还可以使用 strlen() 函数来计算字符串的长度。strlen() 是由 string(字符串)和 length(长度)两个单词组合而成。

strlen() 函数的使用方法如下:
下面展示了一个使用 strlen() 函数的例子:
#include <stdio.h>
#include <string.h>

int main()
{
    char str[20] = "HelloWorld";
    int len1;
    len1 = strlen(str);
    printf("len1 = %d\n", len1);

    int len2;
    len2 = strlen("HelloWorld");
    printf("len2 = %d\n", len2);

    printf("sizeof str %d\n", sizeof(str));
    printf("sizeof helloworld %d\n", sizeof("HelloWorld"));
    return 0;
}
在这个例子中,len1 是由 strlen() 函数测量的字符数组 str 内字符串的长度,len2 是由 strlen() 函数测量的字符串常量 "HelloWorld" 的长度。后续,用 sizeof 分别测量字符数组 str 和字符串常量 "HelloWorld" 占用的空间大小。

这个程序的运行结果为:

len1 = 10
len2 = 10
sizeof str 20
sizeof helloworld 11

因为 HelloWorld 有 10 个字符,所以字符串长度为 10,len1 和 len2 都是 10。字符数组有 20 个元素,所以它占用 20 字节的空间。字符串常量为字符串长度加上结尾标记,所以它占用 11 字节的空间。

总的来说,strlen(str) 测量从第一个元素开始直到元素值为 '\0' 的字符串的长度,而 sizeof(str) 测量数组本身占用的空间大小。

修改字符数组

字符串常量是不可变的,但字符数组却可以被修改。

下面展示了一个修改字符数组的例子:
#include <stdio.h>
#include <string.h>

int main()
{
    char str[20] = "abcde";

    // 修改前
    printf("%s\n", str);  // 使用%s格式符来打印字符串

    // 每个元素减 32
    for(int i = 0; i < strlen(str); i++)
    {
        str[i] = str[i] - 32;
    }

    // 修改后
    printf("%s\n", str);
    return 0;
}
在程序中,数组被初始化为 "abcde",然后我们将其中的小写字符转换为大写字符。根据 ASCII 码,将小写字符的 ASCII 码减去 32 即可得到对应的大写字符。运行结果为:

abcde
ABCDE

从键盘输入字符串到字符数组中

可以使用 scanf() 函数将从键盘中输入的一串字符串存储到字符数组中,其转换规范为 "%s"。例如:
char str[20];
scanf("%s", str);  //  将从键盘中输入的一串字符串存储到字符数组str中
下面程序使用 scanf() 函数将一串字符串存储到字符数组中,并将其中的小写字符转换为大写字符后再进行输出。
#include <stdio.h>
#include <string.h>

int main()
{
    char str[20];

    // 输入一串字符到 str 中
    scanf("%s", str);

    // 修改前
    printf("%s\n", str);

    // 每个元素减 32
    for(int i = 0; i < strlen(str); i++)
    {
        str[i] = str[i] - 32;
    }

    // 修改后
    printf("%s\n", str);
    return 0;
}
scanf() 函数会自动在输入的字符串后面加上 '\0',因此输入的字符串可以正常结束。运行结果为:

apple
apple
APPLE

相关文章