数组是相同数据类型的有序集合,例如:int arr[10] = {1, 2, 3, 44, 55};
数组的特点:
- 同一个数组的所有成员都是相同的数据类型
- 各个元素的内存地址连续 (%p)
一维数组
数组地址
- 数组名为地址,是数组首元素的地址 (arr &arr[0])
- 数组首元素地址 (arr[0])
大小
int arr[5] = {1, 2, 3, 4, 5};
sizeof(arr) // 数组大小
sizeof(arr[0]) // 数组每个元素的大小
sizeof(arr)/sizeof(arr[0]) // 数组元素的个数
数组初始化
// 1.全部元素初始化
int arr[5] = {1, 2, 3, 4, 5};
// 2.部分元素初始化,剩余未初始化的元素默认0值
int arr[5] = {1, 2, 3};
// 3.初始化一个全为0的数组
int arr[5] = {0};
// 4.不指定数组大小,编译器会自动求元素个数
int arr[] = {1, 2, 3, 4, 5};
int arr[] = {0}; // 只有一个元素,值为0
// 5.部分元素初始化,其余未被初始化的元素,默认是随机数
int arr[10];
arr[0] = 1;
arr[1] = 2;
示例
- 数组逆序:三杯水变量交换法
#define _CRT_SECURE_NO_WARNINGS #include #include int main(void) { int arr[6] = {1, 2, 3, 4, 5, 6}; // 倒序为 {6, 5, 4, 3, 2, 1} int num = sizeof(arr) / sizeof(arr[0]); // 此数组的元素个数 int temp = 0; for (size_t i = 0; i<num/2; i++) // size_t == int { // 三杯水法 temp = arr[i]; arr[i] = arr[num - 1 - i]; arr[num - 1 - i] = temp; } for (size_t k=0; k<num; k++) { printf("%d ", arr[k]); } putchar('\n'); system("pause"); return 0; }
- 冒泡排序
#define _CRT_SECURE_NO_WARNINGS #include #include int main(void) { int arr[6] = { 1, 2, 6, 3, 5, 4 }; int num = sizeof(arr) / sizeof(arr[0]); // 元素个数 int temp = 0; for (size_t k = 0; k < num; k++) // 打印原本的数组 { printf("%d ", arr[k]); } putchar('\n'); for (size_t i = 0; i<num; i++) // 分成的行数 { for (size_t j = 0; j arr[j+1]) { temp = arr[j]; arr[j] = arr[j+1]; arr[j + 1] = temp; } } } for (size_t k = 0; k < num; k++) // 打印排序后的数组 { printf("%d ", arr[k]); } putchar('\n'); system("pause"); return 0; } //1 2 6 3 5 4 //1 2 3 4 5 6
二维数组
格式
// int arr[行数][列数] = {};
int arr[2][6] = {
{1, 2, 3, 4, 5, 6},
{3, 4, 5, 6, 7, 8}
}
打印二维数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void)
{
int arr[2][3] = {
{1, 2, 3},
{5, 6, 7}
};
int row = sizeof(arr)/sizeof(arr[0]); // 行数
int col = sizeof(arr[0]) / sizeof(arr[0][0]); // 列数
printf("行数是 %d,列数是 %d\n", row, col);
for (size_t i = 0; i < row; i++)
{
for(size_t j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
putchar('\n');
}
// 打印数组地址
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0][0]);
system("pause");
return 0;
}
//行数是 2,列数是 3
//1 2 3
//5 6 7
//008FFDC8
//008FFDC8
//008FFDC8
地址合一
- 数组的首地址 数组首元素地址 数组的首行地址
arr == &arr[0] == &arr[0][0]
大小
sizeof(arr) // 数组大小
sizeof(arr[0]) // 一行大小,二维数组的一行就是一个一维数组
sizeof(arr[0][0]) // 一个元素的大小
sizeof(arr)/sizeof(arr[0]) // 行数
sizeof(arr[0])/sizeof(arr[0][0]) // 列数
二维数组初始化
// 1.常规初始化
int arr[2][3] = {{1, 2, 4}, {3, 4, 6}};
// 2.不完全初始化
int arr[2][4] = {{1, 2}, {3, 4, 6}}; // 未被初始化的数值为0
int arr[2][4] = {0}; // 初始化一个初值全为0的二维数组
int arr[2][3] = {1, 2, 4, 5, 6}; // 系统自动分配行列 (少见)
// 3.不完全指定行列初始化
int arr[][] = {1, 2, 4, 5, 6}; // error! 二维数组必须指定列值
int arr[][3] = {1, 2, 4, 5, 6}; // 可以不指定行值
示例:求 3 名学生 2 门功课的总成绩
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void)
{
// 学生成绩如下
// 姓名 语文 数学
// a 60 90
// b 70 80
// c 90 80
int score[3][2] = {
{60, 90},
{70, 80},
{90, 80}
};
int n_ren = sizeof(score) / sizeof(score[0]); // 人数
int n_ke = sizeof(score[0]) / sizeof(score[0][0]); // 课程数
//printf("人数:%d\t,课程数:%d\n", n_ren, n_ke);
// 每个人的总成绩
for (size_t i = 0; i < n_ren; i++)
{
int sum1 = 0; // 每个人的总成绩
for (size_t j = 0; j < n_ke; j++)
{
sum1 += score[i][j];
}
//putchar('\n');
printf("每个人的总成绩分别为:%d\n", sum1);
}
// 每门功课的总成绩
for (size_t j = 0; j < n_ke; j++)
{
int sum2 = 0; // 每门功课的总成绩
for (size_t i = 0; i < n_ren; i++)
{
sum2 += score[i][j];
}
//putchar('\n');
printf("每门功课的总成绩分别为:%d\n", sum2);
}
system("pause");
return 0;
}
多维数组
以三维数组举例。四维数组、五维数组 … N 维数组,与三维数组语法相似。
格式
// int arr[层][行][列] = {};
int arr[2][3][4] = {
{{1, 2, 3, 4},
{9, 1, 3, 4},
{3, 4, 5, 6}
},
{{2, 5, 8, 9},
{0, 4, 3, 4},
{2, 4, 7, 1}
}
}
打印三维数组
- 略
字符数组和字符串
字符数组和字符串的区别
- 结尾字符不同,字符串的最后一个字符一般是 ‘\0’
-
示例
- 打印字符串和字符数组
#define _CRT_SECURE_NO_WARNINGS #include #include #include int main(void) { char str1[6] = { 'h', 'e', 'l', 'l', 'o', '\0' }; // 字符串 char str2[6] = "abcde"; // 字符串 char str3[5] = { 'h', 'e', 'l', 'l', 'o' }; // 字符数组 printf("str1[6] %s\n", str1); printf("str2[6] %s\n", str2); printf("str3[6] %s\n", str3); // 将数组按字符串打印,会出现乱码 // 正确打印数组的方法 for (size_t i = 0; i < 5; i++) { printf("%c ", str3[i]); } putchar('\n'); system("pause"); return 0; } //str1[6] hello //str2[6] abcde //str3[6] hello烫烫烫烫烫蘟bcde
- 键盘输入字符串,存至 str[] 中,统计每个字母出现的次数
#define _CRT_SECURE_NO_WARNINGS #include #include #include int main(void) { //char str[6] = "hello"; // 输入的字符串 char str[6]; char letter[26] = { 0 }; // 借助26个字母 //char a = 97; scanf("%s", str); // 键盘输出一个6位字符串 //printf("%s", str); for (size_t i = 0; i < 6; i++) { int index = str[i] - 'a'; // 求在 letter 中的下标 //printf("%d", index); letter[index]++; // i 从 0 开始,会有一个 letter[-97] 警告,可以忽略 } for (size_t j = 0; j 0) { printf("%c 出现了 %d 次\n", 'a' + j, letter[j]); } } system("pause"); return 0; }
- 打印字符串和字符数组
常用函数
- scanf 函数
#define _CRT_SECURE_NO_WARNINGS #include #include #include int main(void) { char str[5]; //scanf("%s", str); // 遇到空格和 \n 终止 scanf("%[^\n]", str); // 借助正则表达式获取带有空格的字符串 printf("%s\n", str); system("pause"); return 0; }
- 函数描述
- 获取字符串
- 注意事项
- 用于存储字符串的空间必须足够大,防止溢出
- 获取字符串 %s,遇到空格和换行 \n 终止
- 借助正则表达式获取带有空格的字符串
scanf("%[^\n]", str); // ^:表示除 xxx 之外
- 函数描述
- gets/fgets 函数
- 函数描述
- get string 的缩写,获取一个字符串,返回字符串的首地址
- 函数原型
char *gets(char *s) char *fgets(char *s, int size, FILE *stream)
- 参数
- s
- 字符串首地址
- size
- 空间大小
- stream
- 文件描述符 (可以是标准输入 stdin)
- s
- 返回值
- 成功,返回读入的字符串;失败,返回 NULL
- 参数
- gets(str) 与 scanf(“%s”, str) 的区别
- gets(str) 允许输入的字符串含有空格
- scanf(“%s”, str) 不允许含有空格
- gets 和 fgets 的区别
- gets 不安全,fgets 安全
- gets 必须遇到换行符或读到文件结尾才终止接收,所以和 scanf 一样,都是不安全的
- fgets 会预留 \0 的存储空间
- 如果空间足够,会读 \n
- 如果空间不够,不会读 \n,且在 \0 之前
- gets 不安全,fgets 安全
- 示例
#define _CRT_SECURE_NO_WARNINGS #include #include int main(void) { char str[5]; printf("%s\n", gets(str)); // 输入的字符串可以包含空格 printf("%s", fgets(str, 5, stdin)); // 安全,输入的字符串不包含空格 system("pause"); return 0; }
- 函数描述
- puts/fputs 函数
将一个字符串输出到屏幕,输出字符串后会自动添加 \n。
- 函数描述
- 字符串输出
- 输出字符串后,不自动添加 \n
- 函数原型
int puts(const char *s) int fputs(const char *str, FILE * stream)
- 参数
- s/str
- 字符串首地址
- stream
- 文件描述符 (可以是标准输入 stdin)
- s/str
- 返回值
- 成功,返回 0;失败,返回 -1
- 参数
- 示例:puts() 和 fputs() 的对比
#define _CRT_SECURE_NO_WARNINGS #include #include #include int main(void) { //char str[5]; //printf("%s\n", gets(str)); // 不安全,输入的字符串可以包含空格 //printf("%s", fgets(str, 5, stdin)); // 安全,输入的字符串不包含空格 char str2[] = "hello world"; puts(str2); // 自动添加了 \n fputs(str2, stdout); // 没有自动添加 \n system("pause"); return 0; }
- 函数描述
- strlen 函数
获取字符串的有效长度,这个长度不包含 \0,碰到 \0 结束。
- 函数描述
- 获取字符串长度
- 函数原型
size_t strlen(const char *s)
- 参数
- s
- 字符串首地址
- s
- 返回值
- 字符串 s 的长度,size_t 可以看成 unsigned int
- 参数
- 示例:对比 sizeof() 和 strlen()
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
<h1>include</h1>
<h1>include</h1>
<h1>include</h1>
<h1>include</h1>
<h1>include</h1>
//#include
int main(void)
{
//char str[5];<pre><code>//printf("%s\n", gets(str)); // 不安全,输入的字符串可以包含空格
//printf("%s", fgets(str, 5, stdin)); // 安全,输入的字符串不包含空格char str2[] = "hello world";
//puts(str2); // 自动添加了 \n
//fputs(str2, stdout); // 没有自动添加 \nprintf("sizeof() = %d\n", sizeof(str2)); // 结果为 12,表示总长度
printf("strlen() = %d\n", strlen(str2)); // 结果为 11,表示有效长度system("pause");
return 0;
</code></pre><p>}
“`
- 函数描述
示例:字符串追加
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char str1[6] = "hello";
char str2[6] = "world";
char str3[100] = { 0 };
char temp;
int i = 0;
for (i = 0; i < 6 - 1; i++)
{
str3[i] = str1[i];
}
printf("%s\n", str3); // -> i = 5
int j = 0;
while (str2[j]) // (str2[j] != '\0')
{
str3[i+j] = str2[j];
j++;
} // -> j = 5
//str3[i + j] = '\0'; // 因为在定义 str3[100] 时指定了 0,所以不需要手动添加 \0
printf("%s\n", str3);
system("pause");
return 0;
}