C语言:数组
这是很基础的教程,我只是写给自己看,作为一个学习笔记记录一下,如果正在阅读的你觉得简单,请不要批评,可以关掉选择离开
如何学好一门编程语言
- 掌握基础知识,为将来进一步学习打下良好的基础。
- 上机实践,通过大量的例题学习怎么设计算法,培养解题思路。
- 养成良好的编码习惯,注释一定要写,要不然保你一周后自己写的代码都不认识了
数组
数组由数据类型相同的一系列元素组成。声明数组需要指定 元素类型 和元素个数。
float candy[365]; /* 内含365个float类型元素的 数组 */ char code[12]; /* 内含12个char类型元素的 数组 */ int states[50]; /* 内含50个int类型元素的 数组 */
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素candy[0],最高的地址对应最后一个元素candy[365]。
数组初始化
数组初始化时 一般用 { } 将元素包括,用逗号分隔。元素数量 不能大于 数组声明时在方括号 [ ] 中指定的元素数目。
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
1、如果我们声明的时候 没有指明元素个数,那么数组的大小则为初始化时元素的个数。
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
2、数组要么在声明的时候全部赋值,要么只能单个赋值
int a[5]={1,1,1,1,1}; // 在声明的时候赋值 // 或者在声明后逐个赋值,有点像追加 int a[0]=1; int a[1]=1; int a[2]=1; int a[3]=1; int a[4]=1; int str[5]; str={1,2,3,4,5}; // 错误演示,声明后只能逐个赋值
3、未初始化的数组,编译器使用的值是内存相应位置上的现有值,数据是乱的。
float candy[5]; //-0.005860 //0.000000 //-0.005860 //0.000000 //-58450.625000
4、在使用数组前,最好先将数组初始化,内存置零。养成良好习惯,避免不必要的错误。
char str1[10]={0}; // 在数组声明时,初始化为0 // 或者 char str[10]; // 数组声明 memset(str,'\0', sizeof(str)); // 内存位置置零
5、当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。
float candy[4]={1.f,2.f}; // 只初始化2项 //1.000000 //2.000000 //0.000000 //0.000000
索引数组:对于数组 int candy[4]; ,索引值只能在0~3以内。不在索引边界虽然不会报错,但是会出现错误的值。
多维数组
多维数组声明的一般形式为:
type name[size1][size2]...[sizeN]; int threedim[5][4]; // 创建一个二维的数组,shape=(5,4)
因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识
初始化
// 逐行初始化 int a[3][4] = { {0, 1, 2, 3} , /* 初始化索引号为 0 的行 */ {4, 5, 6, 7} , /* 初始化索引号为 1 的行 */ {8, 9, 10, 11} /* 初始化索引号为 2 的行 */ }; // 连续初始化 int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
多维数组还可以局部赋值,未赋值的元素会自动取0值。
下标索引
int val = a[2][3]; // 获取数组中第 3 行第 4 个元素
使用嵌套循环来处理二维数组:
#include <stdio.h> int main () { /* 一个带有 5 行 2 列的数组 */ int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}}; int i, j; /* 输出数组中每个元素的值 */ for ( i = 0; i < 5; i++ ) { for ( j = 0; j < 2; j++ ) { printf("a[%d][%d] = %d\n", i,j, a[i][j] ); } } return 0; }
a[0][0] = 0 a[0][1] = 0 a[1][0] = 1 a[1][1] = 2 a[2][0] = 2 a[2][1] = 4 a[3][0] = 3 a[3][1] = 6 a[4][0] = 4 a[4][1] = 8
指针索引
int zippo[4][2];
对于二维数组,数组名 数组首元素的数组,其中 首元素包含两个值。
zippo 和&zippo[0]指向的地址一样,但zippo[0]本身是一个内含两个整数的数组。zippo+1指向的是下一行的地址,zippo[0]+1指向的是第0行的下一个元素。
int zippo[4][2] = {{2, 4}, {6, 8}, {1, 3}, {5, 7}}; printf(" zippo = %p, zippo + 1 = %p\n", zippo, zippo + 1); // zippo = 0x7fffd15143c0, zippo + 1 = 0x7fffd15143c8 printf("zippo[0] = %p, zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1); // zippo[0] = 0x7fffd15143c0, zippo[0] + 1 = 0x7fffd15143c4 printf(" *zippo = %p, *zippo + 1 = %p\n", *zippo, *zippo + 1); // *zippo = 0x7fffd15143c0, *zippo + 1 = 0x7fffd15143c4 printf("zippo[0][0] = %d\n", zippo[0][0]); // zippo[0][0] = 2 printf(" *zippo[0] = %d\n", *zippo[0]); // *zippo[0] = 2 printf(" **zippo = %d\n", **zippo); // **zippo = 2 printf(" zippo[2][1] = %d\n", zippo[2][1]); // zippo[2][1] = 3 printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo + 2) + 1)); // *(*(zippo+2) + 1) = 3
索引二维数组的值需要 使用两个间接运算符(*)或者使用两对方括号([])都能获得该值(还可以使用一个*和一对[],但是我们暂不讨论这么多情况)。所以上例中 zippo[2][1]等价的指针表示法是*(*(zippo+2) + 1)。
[]的优先级高于*
字符串
在C语言中,字符串实际上就是一维的字符数组,只不过字符串 结尾都以null字符 \0 终止。因此字符串的长度 总是比字符数组多1字节。
注意:字符数组和字符串并不是一回事,字符串必须以'\0'结尾,但字符数组是字符组成的数组。所以用printf("%s")的时候,只能打印'\0'结尾的字符数组。
字符数组初始化的方法:
// 字符数组 char site[7] = { 'R', 'U', 'N', 'O', 'O', 'B', '\0' }; // 字符串 char site[] = "RUNOOB"; // 更常用 char site[] = {"RUNOOB"}; char *str="IloveChina"; // 用字符指针指向一个字符串
注意:上述这种字符数组的整体赋值只能在字符数组初始化时使用,不能用于字符数组的赋值,字符数组的赋值只能对其元素一一赋值。
下面的赋值方法是错误的:
char str[20]; str="Iamhappy";
只能对字符数组的各个元素逐个赋值,或者strcpy (官方并不推荐):
char str[10]={'I','','a','m','','h','a','p','p','y'}; strcpy(str, "Iamhappy");
字符串名(数组名)既是指向数组第一个元素的地址,也代表整个数组的索引

字符串相关函数
- strcpy(s1, s2):复制字符串 s2 到字符串 s1。
- strcat(s1, s2):连接字符串 s2 到字符串 s1 的末尾。
- strlen(s1):计算字符串 s1 的长度。不会计算'\0'
- strcmp(s1, s2):如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
- strchr(s1, ch):返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
- strstr(s1, s2):返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
- put(字符数组名):把字符数组中的字符串输出到显示器。
- gets (字符数组名):从键盘上输入一个字符串。
#include<stdio.h> #include<string.h> int main() { char str1[14] = "runoob"; char str2[14] = "google"; char str3[14]; int len; /* 复制 str1 到 str3 */ strcpy(str3,str1); printf("字符串复制:%s\n", str3); // runoob /* 连接 str1 和 str2 */ strcat(str1, str2); printf("字符串拼接:%s\n", str1); // runoobgoogle /* 连接后,str1 的总长度 */ len = strlen(str1); printf("字符串的长度:%d\n", len); // 12 return 0; }
数组与函数
声明数组形参
int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形参,空括号代表ar是一个指针。
int sum(int *ar, int n); int sum(int *, int); int sum(int ar[], int n); int sum(int [], int);
当数组是二维数组时如下,
int sum( int (* pt)[4] ); int sum( int pt[][4]); int sum( int [][4]);
传递数组给函数
当数组作为函数形参时,不能省略参数名,我们可以采用以下三种方式:
方式1:形参是一个 指针
void myFunction(int *param) {...}
方式2:形参是一个已定义大小的数组:
void myFunction(int param[10]) {...}
方式3:形参是一个未定义大小的数组:
void myFunction(int param[]) {...}
实例:给函数传递数组
#include<stdio.h> // 函数声明时,参数名称并不重要,参数类型是必须的 double getAverage(int arr[], int size); // 主函数 int main(void) { int balance[5] = {1000, 2, 3, 17, 50}; // 数组初始化 double avg; // 变量声明 /* 传递一个指向数组的指针作为参数 */ avg = getAverage(balance, 5); // 函数调用 printf("平均值是:%f", avg); return 0; } // 函数定义 double getAverage(int arr[], int size) { // 变量声明 int i; double avg; double sum = 0; for (i = 0; i < size; ++i) { sum = sum + arr[i]; } avg = sum / size; return avg; }
平均值是: 214.400000
从函数返回数组
C 语言不允许从函数 返回一个完整的数组。只能 返回指向数组的指针。
int * myFunction() {...}
例子:生成 10 个随机数,并使用数组来返回它们
#include <stdio.h> #include <stdlib.h> #include <time.h> /* 定义 要生成和返回随机数的函数 */ int* getRandom(){ // C 不支持在函数外返回局部变量的地址, // 除非定义局部变量为 static 变量。 static int r[10]; int i; srand((unsigned)time(NULL)); // 设置随机数种子 for (i = 0; i < 10; ++i){ r[i] = rand(); printf("r[%d] = %d\n", i, r[i]); } // getRandom是一个返回指针的函数,那么r就是一个指针,指针就是地址,r就是地址 return r; } int main(){ int* p; // 声明一个指向整数的指针 int i; p = getRandom(); for (i = 0; i < 10; i++){ printf("*(p + %d) : %d\n", i, *(p + i)); } return 0; }
r[0] = 6742 r[1] = 3486 r[2] = 11181 r[3] = 9850 r[4] = 16663 r[5] = 29286 r[6] = 13426 r[7] = 8210 r[8] = 27862 r[9] = 10451 *(p + 0) : 6742 *(p + 1) : 3486 *(p + 2) : 11181 *(p + 3) : 9850 *(p + 4) : 16663 *(p + 5) : 29286 *(p + 6) : 13426 *(p + 7) : 8210 *(p + 8) : 27862 *(p + 9) : 10451
数组与指针
指针的值是它所指向对象的地址,数组名是一个指向数组第一个元素的指针,例:
flizny == &flizny[0]; // 数组名是该数组首元素的地址
flizny 和 &flizny[0] 都是数组首元素的地址(&是地址运算符)。
在指针前面使用 * 运算符可以得到该指针所指向对象的值。
dates + 2 == &dates[2] // 相同的地址 *(dates + 2) == dates[2] // 相同的值
指针加1,指针的值递增它所指向类型的大小(以字节为单位)
#include <stdio.h> #define SIZE 2 int main(void) { short dates[SIZE] = {1, 2}; // short 是2字节 short *p; short index; p = dates; // 把数组地址赋给指针 for (index = 0; index < SIZE; index++) printf("指针 + %d: %10p, %d\n", index, p + index, *(p + index)); // 指针加1,地址+2, 但是取的值是下一个 // 指针 + 0: 0x7fffe206c274, 1 // 指针 + 1: 0x7fffe206c276, 2 return 0; }
顺带一提,不要混淆 *(dates+2)和*dates+2。间接运算符(*)的优先级高于+,所以*dates+2相当于(*dates)+2:
*(dates + 2) // dates第3个元素的值 *dates + 2 // dates第1个元素的值加2
参考
浙公网安备 33010602011771号