数组
1. 数组概述
数组概念
数组 是一种能够包含多个 同类型数据 的复合结构。
可以理解为一个连续的存储空间,被切分为了多个存储单元
数组定义
数组的定义需要三个要素:
- 数组元素类型
- 数组名称
- 数组的大小
int a[10];
double m[20];
注:
- 数组方括号内的只能是 字面量 或 枚举常量,不能是变量(已赋值的也不行) ;
如:int a[10]; #define N 10 int m[N*5]; enum{X=20}; int n[X]; //以上是合法的数组定义 //以下是不合法的数组定义 int i=10; double x[i]; //注:该种定义方法在C99标准中被允许
- 在常规编程中,数组的大小不宜太大(博主最高使用过长度为10000的数组),过大的数组可能会引起报错。
如真的需要使用一个很大的存储空间,请参考后一篇文章: 指针
数组使用
基本操作:元素访问
int a[10];
a[0]=9;
......
值得一提的是,变量不能出现在数组定义的方括号中,但可以出现在使用数组时的方括号中。如:
常用操作:批量赋值
int a[10], i;
int a[0]=0;
for(i=1;i<9;i++){
a[i]=a[0];
}
这种用法是可行的。
注:
利用 for 循环对数组内的元素进行统一赋值是十分常见的用法,但这一过程中常常会出现 越界访问 问题。
数组初始化
三种方法:
- 直接初始化
注:直接初始化不能使用除了常量之外的任何表达式。int a[3]={1, 2, 3};
- 部分初始化
这种初始化方法会将未提到的元素自动赋值0。int a[3]={1};
- 直接写数组
这种初始化方法会在编译过程中自动给出数组的大小。int a[]={1, 2, 3, 12, 23, 32};
数组作为函数参数
问题在于,C语言的参数传递是值传递,因此无法简单的在函数中使用数组。
解决方案:将数组在内存中存储的地址传递给函数。
int function(int array[]){
......
}
int main(){
int a[3]={1, 2, 3};
function(a);
......
return 0;
}
数组的基本使用至此以叙述完毕,接下来会阐述一些常用的数组及其用法。
2. 一维数值型数组的应用
排序问题
排序 是十分常见的可以用数组解决的问题之一,在本篇博文中,仅仅展示出一个基本的方法—— 冒泡排序 。
冒泡排序 的基本思路是运用嵌套for循环对数组进行操作,每一轮循环都将最大的数挪到数组后方相应位置。
这里给出参考代码:
void bubblesort(int array[], int n){
//array[]是需要排序的数组,n是数组的大小
int i1, i2;
for(i1=0;i1<n-1;i1++){
for(i2=0;i2<n-i1-1;i2++){
if(array[i2]>=array[i2+1]){
int m; //m是个临时变量
m=array[i2];
array[i2]=array[i2+1];
array[i2+1]=m;
}
//如果前一个元素大于(等于)后一个元素,则将两个元素交换
}
}
//排序完成,如需输出:
for(i1=0;i1<n;i1++){
printf("%d ", array[i1]);
}
}
需要标注的是,关于排序还有不少改进方法,如 选择排序 , 插入排序 等,在后面的进阶训练篇章中会有提及。
查找问题
查找 即在数组中寻找某个元素
线性查找
线性查找的思路是最简单的,即从头到尾全过一遍,找到相应元素便返回。
int search(int array[], int n, int goal){
//array[]为要进行查找的数组,n为数组大小,goal为要查找的元素
int i, count=0;
for(i=0;i<n;i++){
if(array[i]==goal){
printf("%d ", i);
count++;
}
}
return count;
//数组中每个等于goal的元素下标均输出,并返回出现次数(count)
}
很明显,这种查找方法在数组已经排好序的情况下是效率较低的。这样就衍生出了另一种查找方法—— 折半查找 。
同样的,在后续进阶文章中会提及。
插入,删除问题
插入,删除的操作方法相比于查找就多了一步,因此此处不再详细阐述,给出思路。
插入:
找到插入位置->将后面的元素全部往后挪一位->将要插入的元素放进去
删除:
找到要删除的元素->将后面的元素往前挪一位
3. 字符数组 / 字符串
字符串常量
顾名思义, 字符串常量 即由一连串字符组成的常量。
在C语言中规定: \0 是字符串结尾的标志。
即:如果定义了一个内容为”C programming”的字符串,那它在存储空间内的存储方式为:”C programming\0”
字符数组
字符数组 可以用来存储字符串。
定义
与其他数组定义方式相同:
char array[20];
注:定义字符数组时需要考虑”\0”占据的一个大小。
初始化
初始化字符数组有多种方法。
逐个字符赋值:
char task[20]={'C', 'p', 'r', 'o', 'g', 'r', 'a', 'm'}; //其中未声明初始值的元素会自动赋值为 '\0'
直接定义字符串常量:
char str1[20]="C programming"; char str2[]="That's a string"; //未定义初始大小的字符串常量会自动设定其大小为链长+1(用于存储 \0)
需要注意的是,这种方法只能用于初始化,不能用于赋值。
输入/输出
C语言中的scanf(visual studio中为scanf_s)以及printf函数为字符串设定了单独的输入输出格式:
char str1[20];
scanf_s("%s", str1);
printf("%s", str1);
但很遗憾,由于scanf的限制,这样的输入不适用于列中带有空格的字符串。因为scanf遇到空格会中止。
因此,以下部分引入新的标准库<string.h>
<string.h>概述
在C语言中,涉及到字符串,这个标准库几乎是逃不过去的,因为其中涉及到了许多很好用的操作字符串的函数。
字符串输入函数 gets
char str[20];
gets(str);
该函数遇到空格不会终止,当遇到回车时会中止输入,并自动在最后多赋值一个 \0
字符串输出函数 puts
char str[]="C programming";
puts(str);
该函数会输出括号中的字符串,并在最后自动加上回车。
字符串长度函数 strlen
char str[]="I'm editing a blog";
printf("%d", strlen(str));
该函数会自动计算括号内字符串的实际长度。(不含 \0)
字符串复制函数 strcpy
char str1[20], str2[20];
strcpy(str1, "blog");
strcpy(str2, str1);
该函数可以将后面字符串的内容复制到前面的字符串中。
需要注意的是前面的字符串需要够大来容纳后面的字符串。
字符串比较函数 strcmp
char str1[]="Beijing";
char str2[]="Beijing";
char str3[]="Shanghai";
printf("%d", strcmp(str1, str2)); //输出0
printf("%d", strcmp(str1, str3)); //输出一个非0的值
该函数会比较前后两字符串的值,相同则输出0,不相同则输出非0;
字符串连接函数 strcat
char str[30]="Programming", str2[10]="c language";
strcat(str, str2); //将str2的内容接到str后面
该函数可以将后面字符串的内容接到前面的字符串后面。
一点补充
字符串的操作方法十分繁杂,包括最简单的gets,puts函数的各种变体也有很多,在这里不过多赘述,在后面的文章中还会相应提及。
4. 二维数组
定义
int a[5][5];
上述方式表示定义一个5*5的二维数组。
赋值/使用
二维数组的赋值常用两个for循环嵌套进行
int a[5][5], i1, i2;
for(i1=0;i1<5;i1++){
for(i2=0;i2<5;i2++){
scanf_s("%d", &a[i1][i2]);
}
}
若二维数组作为函数参数使用,则需要使用如下格式:
int function(int a[][5]){
......
}
//二维数组前面的一维长度不需要给出,但后面的二维长度必须给出
int main(){
int a[5][5];
}
综上,关于数组的一些基本概念已经梳理完毕了。
下一章的指针,相对而言更加复杂,理解难度也更高,但归根结底与数组的原理极其相似,熟练应用后指针反而更加灵活。 (虽然我现在还不能熟练应用)
这篇博文就到这里~~