字符数组及其定义和初始化,C语言字符数组详解

字符串的存储方式有字符数组和字符指针,我们先来看看字符数组。

因为字符串是由多个字符组成的序列,所以要想存储一个字符串,可以先把它拆成一个个字符,然后分别对这些字符进行存储,即通过字符数组存储。字符数组是一个数组,且是存储字符的数组,该数组中一个元素存放字符串的一个字符。

字符数组的定义

因为字符数组首先是一个数组,所以前面讲的数组内容通通都适用。其次它是存放字符的数组,即数组的类型是 char 型。比如:
char name[10];
表示定义了 10 字节的连续内存空间。

1) 如果字符串的长度大于 10,那么就存在语法错误。这里需要注意的是,这里指的“字符串的长度”包括最后的 '\0'。也就是说,虽然系统会自动在字符串的结尾加 '\0',但它不会自动为 '\0' 开辟内存空间。所以在定义数组长度的时候一定要考虑 '\0'。

2) 如果字符串的长度小于数组的长度,则只将字符串中的字符赋给数组中前面的元素,剩下的内存空间系统会自动用 \0' 填充。

字符数组的初始化

字符数组的初始化与数组的初始化一样,要么定义时初始化,要么定义后初始化。下面写一个程序来说明这个问题:
# include <stdio.h>
int main(void)
{
    char a[10];
    a[0] = 'i'; a[1] = ' '; a[2] = 'l'; a[3] = 'o'; a[4] = 'v'; 
//空格字符的单引号内一定要敲空格
    a[5] = 'e'; a[6] = ' '; a[7] = 'y'; a[8] = 'o'; a[9] = 'u'; 
//空格字符的单引号内一定要敲空格
    a[10] = '\0'; 
    char b[10];
    b[0] = 'i'; b[1] = ' '; b[2] = 'm'; b[3] = 'i'; b[4] = 's'; 
//空格字符的单引号内一定要敲空格
    b[5] = 's'; b[6] = ' '; b[7] = 'y'; b[8] = 'o'; b[9] = 'u'; 
//空格字符的单引号内一定要敲空格
    char c[] = "i believe you";
    char d[] = {'i', ' ', 'l', 'i', 'k', 'e', ' ', 'y', 'o', 'u','\0'}; 
//空格字符的单引号内一定要敲空格
    char e[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'}; 
//空格字符的单引号内一定要敲空格
    char f[] = "上课睡觉觉, 下课打闹闹, 考试死翘翘";
    char g[10] = "";
    printf("a = %s\n", a);  //输出字符串用%s, 输出参数必须写数组名
    printf("b = %s\n", b);
    printf("c = %s\n", c);
    printf("d = %s\n", d);
    printf("e = %s\n", e);
    printf("f = %s\n", f);
    printf("g = %s\n", g);
    return 0;
}
输出结果是:
a = i love you
b = i miss you烫i love you
c = i believe you
d = i like you
e = Hello World蘨 like you
f = 上课睡觉觉, 下课打闹闹, 考试死翘翘
g =

首先要说明的是,这个程序只有在 .cpp 文件中才能运行,在 .c 文件中会有很多错误。因为我们在前面讲过,C89 标准规定变量的定义只能在程序的开头,或者说定义变量的前面不能有其他非声明或非定义的语句。而 .cpp 文件是编写 C++ 程序的,C++ 向下完全兼容 C,而且它对变量定义的位置没有特殊要求,只要在使用位置之前即可。

数组 a 是先定义后初始化。一方面与以前讲的数值型数组一样,先定义后初始化必须一个一个地进行赋值,不能整体赋值;另一方面与以前讲的数值型数组又不一样,对于字符串,先定义后初始化也可以整体赋值,但是要调用 strcpy 函数,这点稍后再讲。

总之上面这个程序中给数组 a 一个一个进行初始化的方式很麻烦。而且这样写需要注意:前面讲过系统会在字符串的最后自动添加结束标志符 '\0',但是当一个一个赋值时,系统不会自动添加 '\0',必须手动添加。如果忘记添加,虽然语法上没有错误,但是程序将无法达到我们想要的功能。数组 b 就是这样的例子。

此外,空格字符必须要在单引号内“敲”一个空格,不能什么都不“敲”,什么都不“敲”就是语法错误。也不能多“敲”,因为一个单引号内只能放一个字符,“敲”多个空格就是多个字符了。

数组b就是最后没有手动添加 '\0' 的例子。程序是希望数组 b 输出“i miss you”,但输出结果是“i miss you烫i love you”。原因就是系统没有在最后添加'\0'。

虽然程序中对数组 b 的长度进行了限制,即长度为 10,但是由于内存单元是连续的,对于字符串,系统只要没有遇到 '\0',就会认为该字符串还没有结束,就会一直往后找,直到遇到 '\0' 为止。被找过的内存单元都会输出,从而超出定义的 10 字节。

数组 c 是定义时初始化。定义时初始化可以整体赋值。整体赋值有一个明显的优点——方便。定义时初始化可以不用指定数组的长度,而先定义后初始化则必须要指定数组的长度,如数组 a 和数组 b。不用指定数组长度有一个好处:不用人为确定需要多少字节的内存空间,系统会根据初始化的内容自动分配数量正好的内存空间。而且对于数组 c 的写法系统会自动在最后添加结束标志符 '\0',不需要人为添加。

数组 d 也是定义时初始化,但它既属于整体赋值,也属于一个一个赋值。说它是整体赋值是因为不用写 d[0]、d[1]…而说它是一个一个赋值是因为它把整个句子分成了一个一个的字符。还是数组 c 的写法比较方便,而且对于数组d的写法系统也不会自动在最后添加结束标志符 '\0',必须人为添加。如果忘记添加就会出现与数组 b 同样的错误。从数组 e 的输出结果可以看出这一点。

数组 f 是存储汉字,汉字不能像数组 a 或数组 d 那样分开一个一个赋值。因为一个汉字占 2 字节,若分开赋值,由于一个单引号内只能放一个字符,即一字节,所以将占 2 字节的汉字放进去当然就出错了。因此如果用字符数组存储汉字的话必须整体赋值,即要么定义时初始化,要么调用 strcpy 函数。

数组 g 初始化为一对双引号,表示该字符数组中 10 个元素的内容都为 '\0'。下面写一个程序验证一下:
# include <stdio.h>
int main(void)
{
    char str[3] = "";
    str[2] = 'a';
    printf("str = %s\n", str);
    return 0;
}
输出结果是:
str =

程序中定义了一个长度为 3 的字符数组,然后给第三个元素赋值为 'a',然后将整个字符数组输出。但是输出结果什么都没有,原因就是其直接初始化为一对双引号,此时字符数组中所有元素都是 '\0'。所以虽然第三个元素为 'a',但因为第一个元素为 '\0',而 '\0' 是字符串的结束标志符,所以无法输出。

需要注意的是,使用此种初始化方式时一定要指定数组的长度,否则默认数组长度为 1。

总结,字符数组与前面讲的数值数组有一个很大的区别,即字符数组可以通过“%s”一次性全部输出,而数值数组只能逐个输出每个元素。

推荐文章
SELinux Targeted、MLS和Minimum策略

对于SELinux来说,所选择的策略类型直接决定了使用哪种策略规则来执行主体(进程)可以访问的目标(文件或目录资源)。不仅如此,策略类型还决定需要哪些特定的安全上下文属性。通过策略类型,读者可以更精确

matlab中.*和*的区别是什么?

matlab中.*和*的区别1、"*"即矩阵乘法,两个矩阵必须满足左边矩阵的列数等于右边矩阵的行数A(m,k)*B(k,n)=C(m,n)即矩阵的行列式相乘2、".*"即对应元素相乘,两个矩阵必须满足

string、stringbuffer和stringbuilder的区别是什么?

String、StringBuffer和StringBuilder的区别:StringString类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对

快速格式化和普通格式化的区别是什么?

在我们格式化U盘或者硬盘时,会有一个快速格式化选项,如果勾选了快速格式化将会大大缩短格式化所需的时间,那么快速格式化和普通格式化有什么区别呢?快速格式化就是将磁盘上的文件都标记成已被删除,新的文件可以

学IT需要什么基础?

学IT需要什么基础?1、喜欢IT有句话说的好:只有喜欢,才能用心;只有用心,才能做好!IT是一个行业,有很多知识和技能,发展方向也很多。那么给自己制定一个目标,然后为了自己的目标努力。2、动手能力强在

@Autowired注解与@Resource注解的区别是什么?

@Autowired注解与@Resource注解的区别Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解。如:@Resource、@PostConstru

GDB查看命令

关于GDB调试工具,主要是讲解它的字符界面的使用,也就是使用命令调试程序。GDB中调试的命令非常的多,具体可以通过help命令查看。 查看命令的种类 查看各个种类的命令可以进入到gdb的命令行模式

Java抽象类和接口的区别

前面《Java接口》一节中提到接口是一种特殊的抽象类,接口和抽象类的渊源颇深,有很大的相似之处,所以在选择使用谁的问题上很容易迷糊。本节我们先整理一下Java中抽象类和接口的特点,再分析它们具有的相同

1)产品销售系统 2)网站多用户系统 3)产品互动系统 图1:天猫评价系统 4)推广营销系统

Python操作MongoDB数据库(简明版)

除了通过启动mongo进程进如Shell环境访问数据库外,MongoDB还提供了其他基于编程语言的访问数据库方法。MongoDB官方提供了Java和Python语言的驱动包,利用这些驱动包可使用多种编

.rp是什么文件?

扩展名为“.rp”的文件是通过AxureRP一个线框图和原型设计工具创建的项目文件。它是一个包含的图,其可以包括文本域,图像,按钮和其他视觉元素的数据文件。它是用来执行前创建初步设计和样机。通常以Ax

Python爬虫入门教程:超级简单的Python爬虫教程

这是一篇详细介绍Python爬虫入门的教程,从实战出发,适合初学者。读者只需在阅读过程紧跟文章思路,理清相应的实现代码,30分钟即可学会编写简单的Python爬虫。 这篇Python爬虫教程主要讲解

主成分分析法(PCA)原理和步骤(超级详细)

主成分分析(PrincipalComponentAnalysis,PCA)是一种多变量统计方法,它是最常用的降维方法之一,通过正交变换将一组可能存在相关性的变量数据转换为一组线性不相关的变量,转换后的

Go语言内存管理简述

内存管理是非常重要的一个话题。关于编程语言是否应该支持垃圾回收就有个搞笑的争论,一派人认为,内存管理太重要了,而手动管理麻烦且容易出错,所以我们应该交给机器去管理。另一派人则认为,内存管理太重要了!所

c语言null什么意思?

在C语言中,NULL和0的值都是一样的,但是为了目的和用途及容易识别的原因,NULL用于指针和对象,0用于数值。NULL就是系统定义特殊的0,把你初始化的指针指向它,可以防止“野指针”的恶果。C语言中

Hibernate buildSessionFactory方法:构建SessionFactory

buildSessionFactory方法用于构建一个唯一的SessionFactory,即Session示例的工厂,这个工厂将被应用程序的所有线程共享。 语法: buildSessionFact

开源等于免费吗?用事实来说话

首先,开源软件和免费软件是两个概念: 开源软件是指公开源代码的软件。开源软件在发行的时候会附上软件的源代码,并授权允许用户更改、传播或者二次开发。 免费软件就是免费提供给用户使用的软件,但是在免

什么叫编程?什么人能学编程?

什么叫编程?编程是编定程序的中文简称,就是让计算机代为解决某个问题,对某个计算体系规定一定的运算方式,使计算体系按照该计算方式运行,并最终得到相应结果的过程。为了使计算机能够理解人的意图,人类就必须将

Android移动端部署TensorFlow mobile

在下面的案例中将学习如何为移动环境安装TensorFlow。设定的环境是macOS,以Android系统为例,其他配置将在随后的案例中进行描述。 使用AndroidStudio,采用Google的A

Servlet配置虚拟路径映射

在web.xml文件中,一个元素用于映射一个Servlet的对外访问路径,该路径也称为虚拟路径。例如,在《第一个Servlet程序》教程中,TestServlet01所映射的虚拟路径为“/TestSe