一、学习内容
-
数组
-
什么是数组?
连续存储[内存连续] 多个 类型相同的 数据的容器
-
-
一维数组
-
概念
由一个下标定义的数组 -
语法格式
-
定义数组 数据类型 数组名[常量表达式];
-
分析
-
数据类型——int float double char 结构体 共用体 指针 空void……
-
数组名——符合命名规范
-
[]——数组的标志
-
常量表达式——数组的长度
-
-
注意
-
定义时,[]里面四不准——不为0,不为空,不为小数,不为变量(C99标准之前)
-
-
C99标准下
-
第一种情况
-
int n=10; int a[n]; //变长数组 编译不报错
-
-
第二种情况
-
int n=10; int a[n]={1,2,3}; //变长数组初始化 编译报错【variable-sized object may not be initialized】
-
-
-
-
初始化/赋初始值
-
数组初始化
-
全部初始化 int a[5] = {1 , 2 , 3 , 4 , 5};
-
部分初始化 int a[5] = {1 , 2}; //未被初始化的默认为0
-
特殊初始化 int a[] = {1 , 2 , 3 , 4 , 5 , 6 , 7}; //根据赋值元素的个数,确定长度 int len = sizeof(a)/sizeof(int);
-
错误初始化 int a[5]; a[5]={1 , 2 , 3 , 4 , 5}; float b[5]; float b[5];//数组不能整体赋值
-
单个元素赋值 int a[5]; a[0] = 1; //给第一个元素赋值为1 a[1] = 20; //给第二个元素赋值为20 其他没有赋值的元素是一个随机值
-
关于数组清0,全部赋值为0
-
第一种:int a[5] = {0}; //此处的0放在第一个房间,其他房间没有放,默认是0
-
第二种:库函数 memset
-
功能:给字符串清空 或者 给整数赋值为0或-1 头文件:#include <string.h>
-
函数原型:void *memset(void *s, int c, size_t n); int arr[5]; memset(arr , 0 , sizeof(arr)); memset(arr , -1 , sizeof(arr));
-
-
第三种:库函数 bzero
-
功能:归0 头文件:#include <strings.h>
-
函数原型:void bzero(void *s, size_t n); int barr[5]; bzero(barr , sizeof(barr));
-
-
-
-
-
数组的引用/使用
-
int arr[5]; 表示定义了一个长度为5 的整型数组,里面包含5个元素(整数)
-
数组的输出
-
方式1
-
printf("%d %d %d %d %d\n" , arr[0] , arr[1] , arr[2] , arr[3] , arr[4]);
-
-
方式2
-
使用循环
for(i=0; i<5; i++){
printf("%d ",arr[i]);
} //循环中 i 表示下标 arr[i]表示元素 -
注意定义时在C99标准之前[]不为变量引用数组元素是[]可以为常量、变量、表达式 arr[i] arr[1] arr[i+1]
-
-
-
-
-
二维数组
-
概念
-
两个下标定义的数组
-
-
语法格式
-
数据类型 数组名[常量表达式1][常量表达式2];
-
分析
-
常量表达式1 代表 第一维长度 【行数】
-
常量表达式2 代表 第二维长度 【列数】
-
-
-
初始化
-
全部初始化 int a[2][3] = {1 , 2 , 3 , 4 , 5 , 6}; int a[2][3] = {{1 , 2 , 3} , {4 , 5 , 6}};
-
部分初始化 int a[2][3] = {1 , 2 , 5 , 6}; //1 2 5 , 6 0 0 int a[2][3] = {{1} , {3 , 4}}; //1 0 0 , 3 4 0
-
特殊初始化【可以省略行不可省略列】 int a[ ][3] = {1 , 2 , 3 , 4 , 5 , 6}; 》》 int a[2][3]; int a[ ][3] = {1 , 2 , 3 , 4}; 》》 int a[2][3]; int a[ ][3] = {{1 , 2 } , {3} , {4,5 , 6}}; 》》 int a[3][3];
-
错误初始化 int a[2][ ]={1 , 2 ,3}; int a[2][3] = {{1 , 2} , {3 , 4} , {5 , 6}};
-
如何计算行数和列数 int a[ ][3] = {1 , 2 , 3 , 4}; int sum = sizeof(a); //计算总字节数 int count = sizeof(a)/sizeof(int); //计算总元素个数 int rows = sizeof(a) / sizeof(a[0]); //计算行数 int cols = sizeof(a[0]) / sizeof(a[0][0]); //计算列数
-
-
数组的引用
-
int a[2][3]; 定义2行3列的数组,存储6个整型数据。
-
此处有两个下标——行下标 列下标 行下标范围[0 , 1]列下标范围[0 , 2]
-
第一列 第二列 第三列
循环输入: for(i=0; i<行数; i++){ //外行
第一行:a[0][0] a[0][1] a[0][2]
第二行:a[1][0] a[1][1] a[1][2]
for(j=0; j<列数; j++){ //内列
printf("a[%d][%d]=%d " , i , j , a[i][j]);
}
printf("\n");
}
-
-
-
-
脑图
二、作业
-
编写一个程序。输出以下形式的前五行杨辉三角图案 (石峰)
思路提示:注意拼凑
代码解答:
#include <stdio.h>
#define ROWS 5 // 输出前五行
#define WIDTH 4 // 每个数字的宽度
int main() {
int triangle[ROWS][ROWS]; // 存储杨辉三角形的数组
// 初始化杨辉三角形的所有元素为0
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j <= i; j++) {
triangle[i][j] = 0;
}
}
// 构建杨辉三角形
for (int i = 0; i < ROWS; i++) {
triangle[i][0] = 1; // 每行的第一个元素为1
triangle[i][i] = 1; // 每行的最后一个元素为1
for (int j = 1; j < i; j++) {
triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
}
}
for (int i = 0; i < ROWS; i++){
for (int k = 0; k < (ROWS - i - 1); k++) {
printf(" ");
}
for (int j = 0; j <=i; j++){
printf("%4d",triangle[i][j]);
}
printf("\n");
}
return 0;
}
成果展现:
2.编写一个程序。请用 C、JAVA、C# 任一种语言,对数组 {3, 2, 10, 1, 23, 15, 82} 进行由小到大的排序。(安在软件)
思路提示:
int a[]={3, 2, 10, 1, 23, 15, 82};
int n=sizeof(a)/sizeof(int);
要求使用冒泡排序 和 简单选择排序实现
代码解答:
简单选择排序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int a[]={3,2,10,1,23,15,82};
//计算数组长度
int n=sizeof(a)/sizeof(int);
int i , j ,t;
//进行简单选择排序
for(int i=0;i<n-1;i++){
int min = i;
for(int j=i+1;j<n;j++){
if(a[j]<a[min]){
min = j;
}
}
if(i != min){
t=a[i],a[i] = a[min],a[min]=t;
}
}
printf("排序后的数组为:");
for(i=0;i<n;i++){
printf("%4d",a[i]);
}
printf("\n");
return 0;
}
冒泡排序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int a[]={3,2,10,1,23,15,82};
int len;
//计算数组长度
len = sizeof(a)/sizeof(int);
int i ,j,temp;
temp = 0;
//进行冒泡排序,从小到大
for(i=0;i<len-1;i++){
for(j=0;j<len-i-1;j++){
if(a[j]>a[j+1]){
temp=a[j],a[j]=a[j+1],a[j+1]=temp;
}
}
}
for(i=0;i<len;i++){
printf("%4d",a[i]);
}
return 0;
}
3.编写一个程序。假设你有一只刚出生的母兔,母兔第3年开始生小兔,以后每年生一只。如果生出的都是母兔,请代码实现:输入你拥有该母兔后的第几年,输出该年你拥有的兔子数。(北京信果科技)
思路提示:1 1 2 3 5 8 13 21
代码解答:
#include <stdio.h>
int main(int argc, const char *argv[]) {
int year;
printf("请输入你拥有母兔的第几年:\n");
scanf("%d", &year);
int n = 1; // 初始的母兔数量
int rabbits[100] = {0}; // 数组存储每年母兔数量
rabbits[0] = 1; // 第1年有1只兔子
// 从第2年开始计算兔子数量
for (int i = 1; i < year; i++) {
if (i >= 2) {
// 从第3年开始,每年新增的兔子为3年前出生的兔子数
rabbits[i] = rabbits[i - 1] + rabbits[i - 2];
} else {
// 第1到第2年,兔子数量保持不变
rabbits[i] = rabbits[i - 1];
}
}
// 输出第year年拥有的兔子总数
printf("第%d年你拥有%d只兔子\n", year, rabbits[year - 1]);
return 0;
}
4.编写一个程序。输入两个n行m列的矩阵A和B,输出他们的和A+B(小孩哥)
思路提示:定义两个二维数组 int a[10][10] , b[10][10];输入行数列数;然后对a数组元素和b数组元素对应求和【可以赋值给c数组】。最后输出求和后的结果
代码解答:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int a[10][10]={0};
int b[10][10]={0};
int c[10][10]={0};
int m,n;//列数和行数
int i,j;
printf("请输入矩阵的行数和列数\n");
scanf("%d %d",&m,&n);
printf("请输入a行列式内的值\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
scanf("%d",&a[i][j]);
}
}
printf("请输入b行列式内的值\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
scanf("%d",&b[i][j]);
}
}
printf("a行列式为:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%4d",a[i][j]);
}
printf("\n");
}
printf("b行列式为:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%4d",b[i][j]);
}
printf("\n");
}
for(i=0;i<m;i++){
for(j=0;j<n;j++){
c[i][j]=a[i][j]+b[i][j];
}
}
printf("a和b的行列式和为:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%4d",c[i][j]);
}
printf("\n");
}
return 0;
}
结果展示:
5.编写一个程序。一个长度为10的数组中重复数据,现要求相同数据只保留一份,其他的用0来代替,被替换的数据移到数组末尾。(数组内容自定) (富士安全,云尖软件开发)
int a[]={3, 2, 10, 1, 23, 15, 82,1,2,78};
提示思路:
第一步:重复数组用0代替
for(i=0; i<10; i++){
for(j=i+1; j<10; j++){
if(a[i]==a[j]){
a[j] = 0;
}
}
}
第二步:从大到小排序 输出
代码解答:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
int a[]={3,2,10,1,23,15,82,1,2,78};
int i,j;
int len = sizeof(a)/sizeof(int);
for(i=0;i<len;i++){
for(j=i+1;j<len;j++){
if(a[i] == a[j]){
a[j]=0;
}
}
}
for(i=0;i<len-1;i++){
int temp=0;
for(j=0;j<len-i-1;j++){
if(a[j]<a[j+1]){
temp = a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
for(i=0;i<len;i++){
printf("%4d",a[i]);
}
return 0;
}
成果展示:
6.【选做题】编写一个程序。给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值的那两个整数,并输出它们的数组下标。输入: nums = [3,2,4],target = 6 输出:[1,2]。(郎宁电力)
代码解答:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[]) {
int n; // 数组的大小
printf("请输入数组的长度: ");
scanf("%d", &n); // 输入数组长度
int nums[n]; // 动态定义数组大小
printf("请输入整数数组 nums[%d]:\n", n);
int i, j;
// 输入数组元素
for (i = 0; i < n; i++) {
scanf("%d", &nums[i]);
}
int target;
// 输入目标值
printf("请输入目标值:\n");
scanf("%d", &target);
// 查找和为目标值的两个数的下标
for (i = 0; i < n; i++) {
for (j = i + 1; j < n; j++) {
// 如果找到两个数的和等于 target,打印它们的下标
if (nums[i] + nums[j] == target) {
printf("找到的下标为: [%d, %d]\n", i, j);
return 0; // 找到后直接结束程序
}
}
}
return 0;
}
结果展示:
三、总结
1.学习内容概述:
二维数组和一维数组是本次学习的重点。
通过学习,掌握了数组的声明、初始化、内存分配及基本的遍历操作方法。理解了数组在C语言中的内存结构和常见的应用场景。
二维数组学习的重点在于如何声明、初始化和访问其元素,特别是在多维数组中如何理解和操作其不同维度的索引。
常用函数:
比如`memset`、`bzero`等函数在处理数组初始化时的用法。
2. 学习难点:
二维数组的内存结构
二维数组的内存是连续分配的,学习时需要注意二维数组和一维数组的转换方式,以及不同情况下如何正确地索引到指定元素。
数组长度的动态化
学习中提到的变量长度数组(VLA),比如`int n=10; int arr[n];`,这种数组长度是动态指定的,较为难理解。特别是在某些编译器中会存在不同的支持问题。
内存操作函数的使用
函数如`memset`和`bzero`在初始化数组时比较常见,但它们的参数和用法需要特别留意,否则容易造成未预期的行为。
3. 注意事项
数组的边界问题
在遍历数组时,特别要注意数组的边界,不要越界访问。这不仅会导致程序的逻辑错误,还可能会产生运行时的崩溃或不稳定性。
二维数组的行列索引顺序
在访问二维数组元素时,要分清楚`arr[i][j]`中`i`代表行,`j`代表列。访问时顺序不同可能导致结果与预期不符。
数组初始化问题
声明数组后,如果没有立即初始化,数组中的值是未定义的。初始化数组时,可以使用`{0}`等方式初始化为0或使用`memset`等函数进行内存清零。
4. 未来学习的重点
动态内存分配和释放
目前的学习主要集中在静态数组上,接下来可以学习使用`malloc`、`calloc`等动态分配内存的方式,处理动态数组的创建与释放问题。
数组与函数结合使用
理解如何将数组作为参数传递给函数,以及在函数中如何操作数组,将是未来学习的重点之一。
更复杂的数据结构
在掌握数组的基础上,未来可以扩展到更复杂的数据结构,如链表、队列、栈等,以便处理更复杂的数据操作。
算法与数组的结合
在学习基本数组操作后,可以继续学习数组排序、查找等算法的实现,如快速排序、二分查找等。