跳过正文
P5 数组
  1. S/
  2. C/C++/
  3. C/
  4. C语言程序设计/

P5 数组

Li
作者
Li
往前走,别回头!
目录
C 语言程序设计笔记 - 这篇文章属于一个选集。

知识点
#

6.1 一维数组
#

  • @ 一维数组图
    • ![[白板/P12 一维数组.canvas|P12 一维数组]]

6.1.1 一维数组的一般形式
#

[!tip] 一维数组定义

  • @ 一维数组: 有序数据的集合,包含若干个同类型元素,通过数组名加下标引用
  • 一般形式: 类型说明 数组名[常量表达式];
int a[10];  // 定义数组a,有10个元素

定义要点
#

  • ! 数组名后面是中括号 [],不能写成圆括号 ()
  • ! [10] 中的10表示有10个元素,下标从 0 开始,即 a[0]~a[9]不包括 a[10]
  • ! 非法引用 a[10] 时系统不提示错误,但会产生巨大程序隐患(覆盖其他变量内存)
int a[10];
a[10] = 8;  // 非法!超出下标范围,系统不报错但会留下巨大隐患
  • ! 常量表达式不能包含变量,C 语言不允许对数组大小做动态定义
int i = 8;
int a[i];  // 非法!变量不能用于定义数组大小

6.1.2 一维数组元素的引用
#

[!tip] 数组元素引用

  • 只能引用数组中的元素,不能引用整个数组
  • 引用形式: 数组名[下标]
int i, a[10];
for(i = 0; i < 10; i++)   // 给数组赋值
    a[i] = i;
for(i = 9; i >= 0; i--)   // 逆序输出
    printf("a[%d]=%d\n", i, a[i]);

6.1.3 一维数组的初始化
#

初始化方式示例说明
不给初值int a[10];元素值为随机值
全部赋初值int a[10] = {9,8,10,20,9,8,7,6,5,4};每个元素都有值
部分赋初值int a[10] = {9,8,10,20};未赋值的元素自动为 0
省略数组长度int a[] = {1,2,3,4,5};等价于 int a[5]
  • ! 如果只给部分元素赋初值,其余元素自动赋为 0
  • ! 只有在全部赋初值时才能省略数组长度
  • ! 若数组长度与初值个数不同,长度数字不能省略
int a[10] = {1,2,3,4,5};  // 只初始化了5个元素,后5个为0

6.2 二维数组
#

  • @ 二维数组图
    • ![[白板/P13 二维数组.canvas|P13 二维数组]]

6.2.1 二维数组的一般形式
#

[!tip] 二维数组定义

  • 一般形式: 类型说明 数组名[常量表达式][常量表达式];
  • 理解方式: 按行和列理解,也可以看成"元素为一维数组"的一维数组
float a[3][4];  // 3行4列的数组,共12个元素
// 不能写成 a[3,4],这是错误的写法

二维数组的理解
#

  • a[3][4] 可以把 a 看成一维数组,有3个元素 a[0]a[1]a[2]
  • 每个元素又是一个包含4个元素的一维数组

元素引用范围
#

  • ! 第一维下标范围: 02,第二维下标范围: 03
  • ! a[3][4] 在定义时表示维数,在引用时表示下标值,完全是两个意思

内存存放顺序
#

  • ! 二维数组在内存中按行存放:先存第一行,再存第二行
  • ! 多维数组:第一维下标变化最慢,最右边维度的下标变化最快

6.2.2 二维数组元素的引用
#

int a[3][4];
int i, j;
// 用循环嵌套给二维数组赋值
for(i = 0; i < 3; i++)
    for(j = 0; j < 4; j++)
        a[i][j] = i * j;
// 用循环嵌套输出
for(i = 0; i < 3; i++)
    for(j = 0; j < 4; j++)
        printf("a[%d][%d]=%d\n", i, j, a[i][j]);

6.2.3 二维数组的初始化
#

初始化方式示例说明
按行赋值int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};清晰明了
所有数据在一个大括号int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};不够清晰
部分元素赋值int a[3][4] = {{1},{3,4}};未赋值的为 0
省略第一维长度int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};第二维不能省
  • ! 全部赋初值时可省略第一维长度,但第二维长度不能省略
  • ! 部分赋初值时也可省略第一维长度,但应分行赋初值
int a[][4] = {{0,0,3},{0},{0,10}};

6.3 字符数组
#

  • @ 字符数组图
    • ![[白板/P14 字符数组.canvas|P14 字符数组]]

6.3.1 字符数组的定义
#

[!tip] 字符数组

  • @ 字符数组: 用来存放字符数据的数组,一个元素存放一个字符
  • 定义方式与整型数组类似,类型为 char
char c[10];
c[0] = 'I'; c[1] = ' '; c[2] = 'a'; c[3] = 'm';
c[4] = ' '; c[5] = 'h'; c[6] = 'a'; c[7] = 'p';
c[8] = 'p'; c[9] = 'y';

6.3.2 字符数组的初始化
#

初始化方式示例说明
逐个字符赋值char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'};好理解
省略数组长度char c[] = {'I',' ','a','m',' ','h','a','p','p','y'};等价于 char c[10]
初值个数大于数组长度char c[8] = {'I',...共10个...};编译报错
初值个数小于数组长度char c[12] = {'I',' ','a','m',' ','h','a','p','p','y'};多余元素可能为 '\0'

6.3.3 字符串和字符串结束标记
#

[!tip] 字符串结束标记

  • @ '\0': 字符串结束标志,ASCII 码为 0,用来标记字符串的结束
  • ! 重点中的重点,面试常考

用字符串常量初始化字符数组
#

// 逐个字符初始化:数组长度为10
char c[] = {'I',' ','a','m',' ','h','a','p','p','y'};

// 用字符串常量初始化:数组长度为11!系统自动在末尾加 '\0'
char c[] = "I am happy";  // 等价于 char c[11]
  • ! 用字符串常量初始化时,系统自动在末尾增加 '\0',所以数组长度比可见字符数多1
  • ! 用逐个字符初始化时,系统不会自动加 '\0'

两种初始化的区别
#

char c[] = {'I',' ','a','m',' ','h','a','p','p','y'};  // 长度10,没有'\0'
char c[] = "I am happy";                                 // 长度11,有'\0'
// 这两种写法不等价!

// 但下面这种写法等价:
char c[] = {'I',' ','a','m',' ','h','a','p','p','y','\0'};  // 长度11
char c[] = "I am happy";                                      // 长度11

字符数组大小必须够放 '\0'
#

char c[10] = "I am happy";   // 错误!需要11个位置(10个字符+'\0')
char c[11] = "I am happy";   // 正确,刚好能放下
char c[100] = "I am happy";  // 正确,更可以
  • ! 没有 '\0' 会导致 printf 输出时出现垃圾信息(一直输出直到偶然遇到 '\0'
  • ! 建议使用字符串常量方式初始化:char c[100] = "I am happy";

6.3.4 字符数组的输入/输出
#

输出字符串
#

  • %s 格式符输出整个字符串
  • printf 遇到 '\0' 就停止输出
  • ! 输出的字符串中不包含 '\0'
  • ! 数组长度大于字符串实际长度时,也只输出到 '\0' 结束
  • ! 包含多个 '\0' 时,遇到第一个 '\0' 就停止
char c[] = "China";
printf("%s\n", c);  // China

输入字符串
#

  • scanf%s 格式符输入
  • ! 输入的字符串长度必须短于数组长度,要给 '\0' 留位置
  • ! scanf 遇到空格就认为字符串结束
char c[6];
scanf("%s", c);  // 最多输入5个字符,给'\0'留位置
printf("%s\n", c);
  • ! scanf 中字符数组名前不需要加 &,因为数组名代表首地址
// 输入多个字符串,以空格分隔
char str1[10], str2[10], str3[10];
scanf("%s %s %s", str1, str2, str3);
// 输入: how are you
// str1="how", str2="are", str3="you"

// 但如果只用一个变量接收
char str[100];
scanf("%s", str);
// 输入: how are you
// str只得到"how",后面的被舍弃

6.3.5 字符串处理函数
#

[!tip] 字符串处理函数

  • 使用这些函数需要包含头文件 #include <string.h>

puts(字符数组) — 输出字符串
#

char str[100] = "Are you ok?";
puts(str);  // Are you ok?
// 等价于 printf("%s\n", str);

strcat(字符数组1, 字符数组2) — 连接字符串
#

char str1[10] = "one";
char str2[15] = "two";
strcat(str1, str2);
printf("%s\n", str1);  // onetwo
  • ! 字符数组1必须足够大,能容纳连接后的新字符串
  • ! 连接时将字符串1末尾的 '\0' 删除,连接字符串2的内容
  • ! 连接后只在新字符串末尾保留一个 '\0'
  • ! 连接后 str2 内容不发生变化

strcpy(字符数组1, 字符串2) — 复制字符串
#

char str1[10] = "one1234";
char str2[15] = "two";
strcpy(str1, str2);
// str1 变为 "two"
  • ! 字符数组1必须足够大,能容纳被复制的字符串(包括 '\0'
  • ! 字符数组1必须是数组名,字符串2可以是数组名或字符串常量
  • ! 复制时连同 '\0' 一起复制
  • ! 不能用赋值语句将字符串直接赋给字符数组
str1 = "China";   // 非法!
str1 = str2;      // 非法!
strcpy(str1, "China");  // 正确
  • ! 区分"定义时初始化"和"赋值"
char str1[10] = "one1234";  // 定义时初始化,合法
str1 = "one1234";            // 赋值,非法!

strcmp(字符串1, 字符串2) — 比较字符串
#

比较结果返回值
字符串1 == 字符串20
字符串1 > 字符串2正整数
字符串1 < 字符串2负整数
  • ! 比较规则:自左至右逐个字符按 ASCII 码值比较,直到出现不同字符或遇到 '\0'
  • ! 不能用 == 比较两个字符串,那比较的是地址
// 错误写法!比较的是地址
if(str1 == str2)

// 正确写法:比较字符串内容
if(strcmp(str1, str2) == 0)

strlen(字符数组) — 获取字符串长度
#

char str1[120] = "opel";
int len = strlen(str1);  // 4,不包括末尾的'\0'
  • ! 返回字符串的实际长度不包括 '\0'
  • ! 面试常考:strlensizeof 的区别
函数/运算符strlen(str)sizeof(str)
计算内容字符串实际长度变量所占内存大小
是否包含 '\0'不包含包含
与内容关系有关无关
与定义大小关系无关有关
char str1[120] = "opel";
int stlen = strlen(str1);   // 4,与str1中内容有关
int sostr = sizeof(str1);   // 120,与str1定义时的大小有关
Reply by Email
C 语言程序设计笔记 - 这篇文章属于一个选集。

相关文章