目录
数组
1. 概念:
具有一定顺序的若干变量的集合
2. 定义格式:
存储类型 数据类型 数组名[元素的个数];
int arr[5];
数组名:代表数组首元素的首地址,arr地址常量,不能为左值,不能被赋值
3. 访问元素:
数组名[下标]; 下标是从零开始
访问第一个元素:arr[0];
访问第n个元素:arr[n-1];
注意数组越界问题
4. 特点
1) 数据类型相同
2) 内存连续
5. 注意
1. 数组的数据类型就是数组元素的数据类型
2. 数组名要符合标识符的命名规则
3. 在同一函数中,数组名不要与变量名相同
int arr[5];
int arr; // 错误,在同一函数中,这样不被允许
4. 下标从0开始,到 n-1 结束
6. 分类
一维数组、二维数组
一维数组:只有一个下标的数组
1. 格式:
存储类型 数据类型 数组名[元素个数];
2. 访问元素:
数组名[下标]; 下标从0开始
3. 数组名:数组首元素的首地址
4. 初始化:
4.1. 全部初始化:
定义数组的同时全部初始化
int arr[5] = {1, 2, 3, 4, 5};
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[0]);
printf("%d\n", arr[1]);
return 0;
}
4.2. 部分初始化
int arr[5] = {1, 2}; // 1 2 0 0 0
未初始化的元素值为0
4.3. 未初始化:
随机值,需要单个元素赋值
int arr[5]; // 随机值
进行单个元素赋值:
arr[0] = 1;
arr[1] = 2;
5. 定义空数组:
1) 可以全部初始化:
int arr[5] = {0 , 0 , 0, 0, 0};
2) 可以部分初始化:
int arr[5] = {0};
3) 这样也可以:
int arr[5] = {};
注意:函数里面开辟的数组值为随机值,全局里面开辟的数组值为零
6. 引用:
1. 先定义后引用
2. 每次只能引用一个数组元素arr[i],如果想要引用所有元素可以使用循环遍历
int arr[5] = {1, 2, 3, 4, 5};
int i;
for (i = 0; i < 5; i++)
{
scanf(); / printf();
}
3. 引用时防止数组越界,有时编译器不会报错
4. 打印数组元素的地址用 %p 格式
printf("%p\n", arr[i]); arr[i] 是变量,不能以%p的格式打印,需要加取地址符 &
printf("%p\n", &arr[i]);
7. 内存分配
一一对应的关系
8. 数组遍历:
把数组下标作为循环变量,用 for 循环遍历
int arr[5] = {};
int i, j;
for (i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}
for (j = 0; j < 5; j++)
{
printf("%d\n", arr[j]);
}
9. 数组大小
int arr[5]; // 20
double d_arr[5]; // 40
char str[32]; // 32
计算数组的大小:
1. 数据元素个数 * 数据类型的大小
2. sizeof(数组名)
计算元素个数:
1. 直接观察
2. sizeof(数组名)/sizeof(数据类型)
练习:计算斐波那契数列前15项并逆序输出
1 1 2 3 5 8 13 21 .....
int arr[15] = {1, 1};
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[15] = {1, 1};
int i, j; // 循环变量
for (i = 2; i < 15; i++)
{
arr[i] = arr[i-1] + arr[i-2];
}
for (j = 14; j >= 0; j--)
{
printf("%d\n", arr[j]);
}
return 0;
}
清零函数
1. bzero
#include <strings.h>
void bzero(void *s, size_t n);
功能:内存空间设置为零
参数:s:要清空的空间的首地址
n:字节大小
返回值:无
#include <stdio.h>
#include <strings.h>
int main(int argc, char const *argv[])
{
int arr[15] = {1, 1};
int i, j; // 循环变量
for (i = 2; i < 15; i++)
{
arr[i] = arr[i-1] + arr[i-2];
}
bzero(arr, sizeof(arr));
for (i = 0; i < 15; i++)
{
// arr[i] = 0;
printf("%d\n", arr[i]);
}
return 0;
}
2. memset(可扩展)
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将内存空间设置为0
参数:s:要请空的空间的首地址
c:要设置的值,设置为0
n:字节大小
返回值:要清空的空间的首地址
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int arr[15] = {1, 1};
int i, j; // 循环变量
for (i = 2; i < 15; i++)
{
arr[i] = arr[i-1] + arr[i-2];
}
memset(arr, 0, sizeof(arr));
for (i = 0; i < 15; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
字符数组
字符数组可以存放字符串
1.概念
元素的数据类型为字符型的数组
2.形式
char str[]={'a','b','c'}; //sizeof(str)==3
char str[]={"hello"}; //sizeof(str)==6
char str[]="hello"; //sizeof(str)==6
char str[32]="hello"; //sizeof(str)==32
注意:字符串赋值时常常会省略元素的个数,需要注意数组越界问题
3.输入输出
输入
char str[32] = {};
1) scanf("%c", &str[i])
scanf("%s", str);
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[32] = {};
// for (int i = 0; i < 5; i++)
// {
// scanf("%c", &str[i]);
// }
scanf("%s", str);
printf("%s\n", str);
return 0;
}
输入的字符串不能含有空格,因为 scanf 输入字符串遇到空格或 \n都会认为字符串输入结束,空格后面的内容就不再存放到数组当中
如果需要输入空格可以使用下面的格式:
1)scanf(“%[^\n]”,str);
2)gets
char *gets(char *s);
// 功能:从终端获取字符串
// 参数:s:目标字符数组的首地址
// 返回值:目标字符数组的首地址
char str[32] = {};
gets(str);
printf("%s\n", str);
// 报了个警告
// gets比较危险,不推荐使用,因为不会数组越界检查的,但是是可以使用的
输出
1.printf("%s\n", str);
2.for(int i = 0; i < 32; i++)
{
printf("%c", str[i]);
}
printf("\n");
3.puts
int puts(const char *s)
// 功能:向终端输出字符串
// 参数:s:要输出字符数组的首地址
// 返回值:输出字符的个数
例题:判断对错
char str[10] = {};
str[10] = "hello"; // 错误,数组越界
str = "hello"; // 错误,str是地址常量,不能为左值
strcpy(str, "hello");
练习:将一串字符串进行倒置char buf[32]=”hello”;//olleh
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[] = "hello ";
// 将数组的元素个数拿掉,计算字符串的实际长度,但不符合题干
// szieof(buf)-1;
// 中间变量
char t;
// i: 循环变量
// n: 字符串的实际长度
int i, n = sizeof(buf)-1;
// 通过循环的方式计算字符串的实际长度
// for (n = 0; buf[n] != '\0'; n++);
// 交换
for (i = 0; i < n/2; i++)
{
t = buf[i];
buf[i] = buf[n-1-i];
buf[n-1-i] = t;
}
printf("%s\n", buf);
return 0;
}
计算字符串的实际长度
- for循环遍历数组,直到\0为止
for(n = 0; buf[n] != '\0', n++);
- strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串的实际长度(不包括\0)
参数:s:要计算的字符串的首地址
返回值:字符串的实际长度
char buf[32]="hello";
int num =strlen(buf);
printf("%d\n", num);// 5
sizeof(buf);// 32
char str[] = "hello"
int num = strlen(buf);
printf("%d\n", num); // num = 5
sizeof(str); // 6
sizeof和 strlen的区别
- sizeof是关键字,strlen是函数
- sizeof是计算数据所占空间大小,strlen是计算字符串的实际长度
- sizeof计算包括\0,strlen计算不包括\0,计算字符串长度时(元素个数省略情况下),sizeof比strlen大1
练习:实现字符串大小转换
冒泡排序
两两比较,第i个和 i+1个比较
int arr[5] = {5, 4,3, 2, 1};
第一轮:
45321
43521
43251
43215
第二轮:
34215
32415
32145
第三轮:
23145
21345
第四轮:
12345
n个数:比较n-1轮,每轮的交换次数从 n-1开始依次递减
算法实现:
#include <stdio.h>
#define N 10
int main(int argc, char const *argv[])
{
int arr[N];
for (int i = 0; i < N; i++)
{
scanf("%d", arr[i]);
}
for (int i = 0; i < N - 1; i++) // 轮数
{
for (int j = 0; j < N - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < 5; i++)
{
printf("%d", arr[i]);
}
printf("\n");
练习:将10个数进行降序排序
选择排序:
n个数:先找出最小值的下标暂存,选择出最小值与第一个数进行交换
排序过程:
1) 首先通过 n-1 次比较,从 n 个数中找出最小的,将它与第一个数交换 第一趟选择排序,结果最小的数被安置在第一个元素位置上
2) 再次通过 n-2 次比较,从剩余的n-1个数中下标对应的最小的值,将它与第二个数交换 第二趟选择排序
3) 重复上述过程,共经过 n-1 趟排序后,排序结束
算法实现:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[5] = {5, 4, 3, 2, 1}, n = 5;
for (int i = 0; i < n-1; i++)
{
// 把第一个值当作最小值,进行比较,后面直接拿k去进行比较
int k = i;
for (int j = 0; j < n; j++)
{
if(arr[j] < arr[k])
{
k = j;
}
}
if(i != k)
{
// 交换
arr[k] 和 arr[i] 交换;
}
}
return 0;
}
二维数组
1.格式
存储类型 数据类型 数组名[行数][列数]
int arr[2][3];
2.访问元素
数组名[行下标][列下标];(下标从0开始)
arr[0][0]:第一行第一列的元素
arr[1][2]:第二行第二列的元素
行下标、列下标都不能越界
行数可以省略,但是列数不能省略
int arr[][3];
3.数组元素个数:
行数*列数
sizeof(数组名) / sizeof(数据类型)
4.二维数组的大小:
sizeof(数组名)
数据类型大小 * 元素个数
5.数组名
arr:第一行的首地址
arr+1:第二行的首地址
6.初始化
1) 全部初始化:
int arr[2][3] = {1, 2, 3, 4, 5, 6};
printf("%d %d %d\n", arr[0][0], arr[0][1], arr[0][2]);// 1 2 3
printf("%d %d %d\n", arr[1][0], arr[1][1], arr[1][2]);// 4 5 6
2) 部分初始化:未初始化的部分元素值为0
int arr[2][3] = {1, 2, 3, 4}; //顺序赋值
printf("%d %d %d\n", arr[0][0], arr[0][1], arr[0][2]); // 1 2 3
printf("%d %d %d\n", arr[1][0], arr[1][1], arr[1][2]); // 4 0 0
intarr[2][3] = {{1, 2}, {4, 5}};// 按行赋值1 2 0 4 5 0
3) 未初始化:随机值,需要单独的赋值
int arr[2][3];
A
C
7.内存分配
内存分配
// a 和 a[0] 并不完全相同,级别不同,后面会讲,数值上是一样的,级别不一样
// a 是行地址
// a[0] 是列地址
//[]降级
// a 表示 第一行的首地址
// a+1 表示 第二行的首地址
// a[0] 表示第一行第一列的地址
// a[1] 表示第二行第一列的地址
二维数组
二维数组遍历
for循环嵌套,外层行数,内层是列数
int arr[m][n] = {};
for(i = 0; i < m; i++)
{
for(j = 0; j < n; j++)
{
// 可以输入也可以输出
scanf() / printf()
}
}
例:
int arr[2][3] ={};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", &arr[i][j]);
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d", arr[i][j]);
}
}