引入
上次讲到字符和字符串,二者可以打印到控制台,但是这样就让打印的内容固定了,那尝试改变一下——第一:单个的字符可以通过像int 、float那样定义变量,通过改变变量来改变打印内容;第二多个字符的字符串需要一个新的东西来存储——字符数组,数组是一种数据结构,用来存储同类型数据,分为一维数组及多维数组,今天先讲一维数组,如:
可以看到数组的索引从0开始,而不是1;并且每一个位置都只存放一个字符,那最后边索引为5的位置放的是什么东西?存放的是“\0”——空字符,表示字符串的结束,但是存放整型或其他数据类型的数组并不需要,这是因为很多时候数据大小在读取(输入)的时候是静态的,也就是可以明确指定数据的长度大小,比如:一个存放固定位数的电话号码数组,我们可以直接指定数组的元素大小为11(元素用来指代存放的东西)
至于动态的数据存放后面再讲——动态内存分配。
单个字符的变量定义及打印
类似于int、float、double,字符用char类型(character的前四个字母)定义变量
char a='H';
请注意char类型的变量只能存储单个的字符而不能是字符串,这是因为char本身的内存大小就只有一个字节,不信?看下面:
#include<stdio.h>
int main()
{char a = 'H';size_t size = sizeof (a);//size_t表示无符号整数的数据类型,用来存储sizeof操作符返回的值,字节大小必然是非负的所以是“无符号”printf("%zd\n", size);return 0;
}
所以很显然不可能单单用char类型的变量就能实现所有文本信息的储存。
数组的定义、初始化、打印
字符数组
字符数组在定义时同样需要指明数据的类型为char,接着,数组的完整定义是:首先我们需要确定数据的类型,就像变量的定义那样;其次我们需要给要存储的数据起一个名字——数组名,用来指代该数组,也为了区分不同的数组;接着我们需要用到一个新的操作符[]
,叫“下标引用操作符”,这里的下标就是我们上边所说的索引,操作符里面放下标,像这样[0]
表示第一个数组元素,然后在操作符前面加上数组名,就可以用来完全表示第一个数组元素:假设为arr[0]
,那么这时候arr就是数组名,一整个表示第一个数组元素,(注数组英文为:array)。那么该怎么存储我们的文本信息?这就类似于我们数据变量的初始化,数组也可以进行初始化:
初始化的格式:数据类型(type) 数组名[数组元素大小]=
int main()
{char arr[] = "HELLO" ;printf("%s", arr);//打印的时候用数组名指代一整个数组return 0;
}
上面的初始化就是直接指定数组内容,也就是给了一个字符串字面量,它是固定的,所以也会根据空字符来确定字符串的数组元素大小,无需在定义之初指定数组数组元素大小。
除了这种初始化办法,还有一种就是可以逐个确定数组里面的内容,比如像下面这样:
char arr[] = { 'H','E','L','L','O',‘\0’};
printf("%s", arr);
这时候{}
用来包裹全部的字符,‘’
用来包裹单个字符,用%s
可以实现一整个字符数组的输出。需要注意的就是需要自己手动在数组后面加上空字符以表示该字符数组的结束。正是因为有空字符所以字符数组可以这样打印,但整形数组(或其他数组)没有空字符来表示数组的结束来确定数组元素的大小,所以无法用下面的错误示范来输出一整个数组:
int arr[] = { 1,2,3,4,5,6 };
printf("%d", arr);
会输出异常
整形数组
整型数组的打印靠的是一个一个遍历数组的元素,如何做到?当然是可以采用我们学过的for循环来逐个打印!“遍历”说的就是可以不断自增或者自减,靠变量i来实现:
for(int i=0;i<(数组元素大小);i++)
{printf("%d ",arr[i]);
}
如果我们在定义并且初始化数组的时候加上数组元素大小,那就可以这样写:
int arr[11] = { 0,6,6,0,1,2,3,4,5,6,7 };
for (int i = 0; i < 11;i++)//这样将会遍历数组下标从0到10的元素
{printf("%d ", arr[i]);
}
数组元素的大小其实可以“算”出来。类似于用sizeof算出单个变量(或表达式)的字节大小,我们可以算出数组的总字节大小然后除以每一个数组元素的字节大小,不就是所求的数组的元素个数了吗?
int size=sizeof(arr)/sizeof(arr[0]);
sizeof后面不仅可以接变量和表达式,还可以接数组名以及单个的数组元素。
int arr[11] = { 0,6,6,0,1,2,3,4,5,6,7 };
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size ;i++)
{printf("%d ", arr[i]);//i遍历了每一个数组元素
}
同样我们也可以来用for循环遍历输出字符数组
char arr[] = { 'H','E','L','L','O','\0' };
int size = sizeof(arr) / sizeof(arr[0]);
for (int i=0; i < size; i++)
{printf("%c", arr[i]);//注意这里是%c
}
为什么‘\0’
不会打印,是因为该符号是不可打印符号,通过查看该字符数组的大小——6(char的每一个字节是1),我们得知最后的空字符是有被算进去的。
或者我们可以通过查看每一个元素的ASCII码值来证明下面字符串字面量(没有显示空字符)空字符的存在:
char arr[] = "ABCD";
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size; i++)
{printf("%d ", arr[i]);//注意这里是%d,可以主动转换成ASCII码值
}
其实字符串的长度计算可以用一个专门的函数来实现:strlen函数。但是要包含头文件string.h那么我们就可以把程序写成:
#include<stdio.h>
#include<string.h>
char arr[] = "HELLO";
int size = strlen(arr);
for (int i = 0; i < size; i++)
{printf("%c", arr[i]);//注意这里是%d,可以主动转换成ASCII码值
}
sizeof和strlen的区别
但strlen计算的是字符串的长度并不会把最后的空字符算进去,所以我们可以通过下面的程序看看二者的区别:
char arr[] = "HELLO";
int size1 = strlen(arr);
printf("%d\n", size1);
int size2 = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", size2);
那这种区别会有影响吗?并没有,即使sizeof比strlen多了一个,因为遍历到最后一个元素——空字符时,由于空字符是不可打印字符所以在打印时没有区别。