【编程语言】C语言数组与字符串(包括:字符串与字符数组)

在程序设计中,为了方便处理,通常把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在C语言中,数组属于构造数据结构。一个数组可以分解成多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按照数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。

本文主要介绍一维数组、二维数组和字符数组,其余的数组将会在以后的文章中介绍到。


数组概述

数组就是用来存储和处理一组相同类型的数据的。

数组在C语言中有着特殊的地位,它有很多的特性,比如它的存储是连续的、数组的名称是数组的地址等。

一维数组

一维数组是使用同一个数组名存储一组数据类型相同的数据,用索引或下标区别数组中的不同元素。

  • 一维数组的定义方式为:
类型说明符 数组名[常量表达式]

在定义数组时,应该注意以下几点:

  1. 数组使用的方括号[],不是小括号;
  2. 常量表达式表示数据元素的个数,必须是一个正的整数值,不能含有变量,但是可以是符号常量或者常量表达式;
  3. 数组定义后,其长度不可更改。
  • 一维数组的数据元素的一般形式为:
数组名[下标]

在引用数组元素时,应该注意以下几点:

  1. 一位数组元素的下标从0开始,最大下标为n-1;
  2. 在C语言中只能逐个地使用数组元素,而不能一次引用整个数组。
  • 一维数组的初始化:除了用赋值语句对数组元素逐个赋值外,还可采用初始化赋值和动态赋值的方法。

初始化赋值的一般形式为:

类型说明符 数组名[常量表达式] = {初始值表};

int a[5]={1,2,3,4,5};

在初始化赋值时,应该注意以下几点:

  1. 数组定义时初始化可以整体赋初值,但是除了定义初始化之外,不能整体赋值!
  2. 当初始值表给出全部元素值时,可以不给出数组元素的个数;
  3. 可以只给部分数组中的部分元素赋初始值(没有赋值的位将补0)。也就是说可以这样:
int a[]={1,2,3,4,5};
int b[5]={1,2,3};

二维数组

  • 二维数组定义的一般形式为:
类型说明符 数组名[常量表达式1][常量表达式2];

二维数组被定义后,编译系统将为该数组在内存中分配一片连续的存储空间,按行的顺序连续存储数组的各个元素。即先顺序存储第一行元素,再存储第二行元素……直到存储最后一行元素。数组名代表着数组的起始地址。

  • 二维数组元素引用的基本语法格式为:
数组名[下标][下标]
  • 初始化赋值的一般形式为:
类型说明符 数组名[常量表达式1][常量表达式2] = {初始值表};

int a[3][4] = { { 1,2,3,4 },{ 5,6,7,8 },{ 9,0,1,2 } };

由于二维数组在内存中是按照线性顺序存储的,所以内存括号可以省去,不会产生影响。也就是说可以这样:

int a[3][4] = { 1,2,3,4,5,6,7,8,9,0,1,2 };

在初始化赋值时,应该注意以下几点:

  1. 当初始值表给出全部元素值时,可以不给出二维数组的常量表达式1(但常量表达式2必须给);
  2. 可以只给部分数组中的部分元素赋初始值(没有赋值的位将补0)。也就是说可以这样:
int a[][4] = { { 1,2,3,4 },{ 5,6,7,8 },{ 9,0,1,2 } };
int a[][4] = { 1,2,3,4,5,6,7,8,9,0,1,2 };
int b[3][4] = { { 1 },{ 5 },{ 9 } };

在这里,省去3是可以的,但是4不能省去的。编译器会根据所附的数值的个数以及数组的列数,自动计算出数组的行数。

数组是一种构造类型的数据。二维数组可以看作是由一维数组的嵌套而构成的。设一维数组的每个元素都是一个数组,就组成了二维数组。当然前提是各元素的类型必须相同。根据这样的分析,一个二维数组也可以分解成多个一维数组。C语言允许这样的分解。如二维数组a[3][4],可以分解成3个一维数组,a[0]、a[1]、a[2]。

数组与链表的区别:

  • 从逻辑结构上来看,数组必须实现定义固定的长度,不能适应数据动态增减的情况。当数据增加时,可能会造成空间的不够;当数据减少时,可能造成空间浪费。而链表动态地进行存储分配,可以适应数据动态增减的情况,方便插入和删除;
  • 从内存储存上来看,数组从栈中分配空间,方便快捷,但自由度小。而链表从堆中分配空间,自由度大,但申请管理比较麻烦。


字符数组与字符串

字符数组的说明与初始化

用于存放字符的数组称为字符数组,它是数组的一种特殊类型,字符数组的每个元素存放一个字符。

  • 字符数组的定义的一般形式为:
char 数组名[常量表达式];
char 数组名[常量表达式1][常量表达式2];
  • 字符数组的初始化:除了数组的基本初始化方式外,字符数组还可以使用字符串常量初始化。
char a[5]={'a','b','c','d','e'};
char a[]={'a','b','c','d','e'};

把一个字符串存入一个数组时,也会将结束符'\0'存入数组,并以此作为该字符串是否结束的标志。有了'\0'标志后,就不必再用字符数组的长度来判断字符串的长度了。

用字符串方式赋值比用字符逐个赋值要多占1个字节,用于存放字符串结束标志'\0'。以下三种初始化的效果是一样的:

char b[]={"How are you"};
char b[]="How are you";
char b[]={'H','o','w',' ','a','r','e',' ','y','o','u','\0'};

同时,需要注意:字符数组可以使用在定义初始化的时候用字符串初始化,但是不能使用赋值语句整体赋值:

char c[11] = "How are you";            /* 正确 */
char c[11];
c = "How are you";            /* 错误 */

为了证实'\0'的存在,例如:

#include<stdio.h>

int main()
{
	int i;
	char c[] = "How are you!";

	for (i = 0;; i++) {
		if (c[i] == '\0')
			break;
	}
	printf("%d\n", i);

	return 0;
}

最终结果输出12,代表字符数组下标为12的字符是'\0',这与我们的理论是一致的。

字符串和字符数组

C语言中没有专门的字符串变量,如果要将一个字符串存放到某一个变量中,必须使用字符数组,即用一个字符数组来存放一个字符串,数组中每个元素存放一个字符。

  • 字符串:必须以'\0'结尾;
  • 字符数组:可以包含多个'\0',也可以不包含'\0'。

字符串的处理函数

在采用字符串方式后,字符数组的输入/输出变得更加简单方便。除了可以使用字符串对字符数组赋初值的方法外,还可以用printf()函数和scanf_s()函数一次性输入/输出一个字符数组的所有字符,而不必利用循环单个操作。

C语言提供了大量的字符串处理函数,大致可以分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索几类。

在使用输入/输出的字符串函数之前,需要包含头文件stdio.h;使用其他字符串函数之前,需要包含头文件string.h;

由于没有字符串变量这个说法,所以对任何字符串的操作都是将它放在字符数组里。

  • 字符串输入函数gets()

从键盘上输入一个字符串(包括空格),赋予从字符数组起始的存储单元,直到读入一个回车符为止。回车符读入后,不作为字符串的内容,系统将自动用'\0'替换,作为字符串结束的标志。

char c[20];
gets(c);
  • 字符串输出函数puts()

将字符数组起始地址开始的第一个字符串(以'\0'结束的字符序列)输出到显示器,并将字符串结束标志'\0'自动转换成'\n'。

char c[]="Hello!";
puts(c);

区分printf()与puts()、scanf_s()与gets()函数的区别:

  1. puts()函数在遇到'\0'时,会自动转换成'\n'。而printf()函数则不输出'\0',同时也不输出'\n';
  2. gets()函数接受字符串,读入换行符才停止。而scanf_s()函数则读入到空格或者换行符就停止。
  • 字符串长度函数strlen()

测试字符数组起始地址开始的字符串(以'\0'结束的字符序列)有效长度,此长度不包括'\0'在内。

char c[]="Hello";
printf("%d",strlen(c));
  • 字符串连接函数strcat()
strcat(字符数组1,字符数组2);

将字符数组2连接到字符数组1之后,函数值为字符数组1的起始地址。连接字符数组1和字符数组2的尾部都有一个'\0',连接时将字符数组1后的'\0'自动取消,字符数组2后的'\0'一并连接到字符数组1后。

这里还有一个注意点:

#include<stdio.h>

int main()
{
	char c1[20] = "Hello ";        /* 这里必须要指定足够的长度,否则会报错! */
	char c2[] = "World!";
	printf("%s", strcat(c1, c2));

	return 0;
}

字符数组1必须要足够长度,以便在其有效字符后能够容纳字符数组2的字符串。

  • 字符串复制函数strcpy()
strcpy(字符数组1,字符数组2);

将字符数组2复制到字符数组1中去,函数值为字符数组1的起始地址。字符数组1的长度能够容纳字符数组2的长度;将字符数组2的字符串结束标志'\0'一起复制到字符数组1中去了;同时,strcpy()函数能够将字符数组2前面若干个字符复制到字符数组1中,例如:

strcpy(字符数组1,字符数组2,长度);
  • 字符串比较函数strcmp()
strcmp(字符数组1,字符数组2);

将两个字符数组自左向右对应的字符逐个进行比较(按照ASCII码值大小比较),直到出现不同的字符或者遇到'\0'字符为止,函数值为一个整型。具体返回的情况如下表所示:

strcmp函数返回值
字符串大小情况 返回值
字符串1等于字符串2 0
字符串1大于字符串2 1
字符串1小于字符串2 -1


关于数组的小例子

1、杨辉三角形:

#include<stdio.h>

int main()
{
	int a[10][10],i,j;
	for (i = 0; i < 10; i++) {
		a[i][0] = 1;
		a[i][i] = 1;
	}
	for (i = 2; i < 10; i++) {
		for (j = 1; j < i; j++) {
			a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
		}
	}
	for (i = 0; i < 10; i++) {
		for (j = 0; j < i+1; j++) {
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}

	return 0;
}

这段程序的运行结果为:

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
请按任意键继续. . .

2、找出下面程序中的错误:

#include<stdio.h>

int main()
{
	unsigned char a[256], i;
	for (i = 0; i < 256; i++) {
		a[i] = i;
	}

	return 0;
}
unsigned char的取值范围为0-255,当i最大为255的时候,下次循环i++,i就变成了0,造成无限循环。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80180031