C语言 | 第十三章 | 二维数组 冒泡排序 字符串指针 断点调试

P 120 数组应用案例 2023/1/29

一、应用案例

案例一:创建一个char类型的26个元素的数组,分别 放置’A’-'Z‘。使用for循环访问所有元素并打印出来。提示:字符数据运算 ‘A’+1 -> ‘B’

#include<stdio.h>

void main(){

	/*
	创建一个char类型的26个元素的数组,分别 放置'A'-'Z‘。使用for循环访问所有元素
	并打印出来。提示:字符数据运算 'A'+1 -> 'B
	*/
	char arr[26];
	int i;
	for(i = 0; i < 26 ; i++){
		arr[i] = 'A' + i; // 第一个是0,A+0依然是0
	}
	for(i = 0; i < 26; i++){
		printf("arr[%d]=%c\n",arr,arr[i]);
	}
	getchar();
}

案例二:请求出一个数组的最大值,并得到对应的下标。

#include<stdio.h>
void main(){

	/*
	请求出一个数组的最大值,并得到对应的下标。
	分析:
	1.自定义数组大小5
	2.假定第一个元素max= arr[0]就是最大值,然后我们依次和数组后面的数进行比较,
	如果发现有比max更大的数,就相应的变化,遍历完整个数组,max就是最大数。
	*/
	int arr[] = {0,-1,89,99,4};
	int arrLen = sizeof(arr) / sizeof(int);
	int max = arr[0];
	int maxIndex = 0;
	int i;
	for(i = 1; i < arrLen; i++){    // 这里 i 的初始值为1;因为为0则是和自己比较没有意义
		// 发现有比max更大的数,就相应的变化
		if(arr[i] > max){
			max  = arr[i];
			maxIndex = i; // 对应下标就是i,所以把i 赋值即可
		}
	}
	printf("max = %d maxIndex = %d",max,maxIndex); // 99,3
	getchar();
}

P 121 字符串介绍和内存布局 2023/1/29

一、字符数组的介绍

基本介绍:用来存放字符数组称为字符数组, 看几个案例。

char a[10]; //一维字符数组, 长度为10
char b[5][10]; //二维字符数组, 后面我们详细介绍二维数组
char c[20]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; // 给部分数组元素赋值

总结:字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。

#include<stdio.h>
void main(){

	// c是一个一维字符数组,给部分元素赋值
	char c[20]={'t','o','m'};

	// 输出
	printf("%s",c);	  // 输出tom
	getchar();
}

二、字符串注意事项

  1. 在 C 语言中,字符串实际上是使用 null 字符 (‘\0’) [null字符就是\0,因为ASCII 0 表示空],终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。【以下为内存布局图】
  2. '\0’是ASCII码表中的第0个字符,用NUL表示,称为空字符。该字符既不能显示,也不是控制字符,输出该字符不会有任何效果,它在C语言中仅作为字符串的结束标志。
  3. 字符数组(字符串)在内存中的布局分析 [案例]。

image-20230129180936276

  1. 思考 char str[3] = {‘a’,‘b’,‘c’} 输出什么? 为什么?
	char c[7]={'t','o','m'}; 
// 正常输出tom

	char str[3] = {'a','b','c'};
// 输出a,b,c后,后面可能会出现乱码,一些垃圾值

	char str[] = {'a','b','c'};
// 不指定长度,输出结果可能同样会出现乱码

分析示意图:

image-20230129182746533

结论:如果在给某个字符数组赋值时,(1)赋给的元素的个数小于该数组的长度,则会自动在后面加 ‘\0’, 表示字符串结束,(2)赋给的元素的个数等于该数组的长度,则不会自动添加 ‘\0’。

P 122 字符指针和内存布局 2023/1/30

一、字符串的访问和遍历

小结:因为字符串的本质就是字符数组,因此可以按照数组的方式遍历和访问某个元素即可, 案例如下:

#include<stdio.h>
#include<string.h>
void main(){
	// 这种方式定义字符数组不会出现任何的乱码,会自动添加一个\0
	char greeting[] = "Hello";  
	int i;
	int len  = strlen(greeting);  // len = 5
	printf("\n greeting = %s",greeting);
	printf("\nlen=%d", len);
	printf("\n字符串第3个字符是=%c", greeting[2]); // 下标是从0开始的,第三个字符
	for(i = 0; i < len; i++) {
		printf("\n%c", greeting[i]);
	}
	getchar();
}

  • 内存布局

image-20230130115430132

二、字符串的表示形式

  • 用字符数组存放一个字符串

1 . char str[]=“hello tom”;

2 . char str2[] = {‘h’, ‘e’};

  • 用字符指针指向一个字符串

1 . 比如: char pStr=" hello";*

#include<stdio.h>
void main(){
	// 使用一个指针pStr,指向一个字符数组
	char* pStr = "Hello";

	printf("\n pStr指向的字符串= %s",pStr); // 输出Hello
	getchar();
}

2 . 总结:

  • C语言对字符串常量" hello"是按字符数组处理的,在内存中开辟了一个字符数组用来存放字符串常量,程序在定义字符串指针变量str时只是把字符串首地址(即存放字符串的字符数组的首地址)赋给pStr。
  • 内存布局图

image-20230130120200916

  • printf(“%s\n”,str); 可以输出 str 指向的字符串。

三、两种方法表示字符串的讨论

  1. 字符数组由若干个元素组成,每个元素放一个字符;而字符指针变量中存放的是地址(字符串/字符数组的首地址),绝不是将字符串放到字符指针变量中(是字符串首地址)。

  2. 对字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值:

char str[14];   // str 实际是一个常量

str=" hello tom"; //错误

str[0] = 'i'; //ok ,因为只是找到第一个元素在那里填了一个i而已

内存布局图:

image-20230130131706012

  1. 对字符指针变量,采用下面方法赋值, 是可以的:
#include<stdio.h>
void main(){
	char* a="yes";
	// 赋值前的地址:
	printf("\na本身的地址是%p,指向的地址是%p",&a,a);

	a=" hello tom";   // 定义一个字符指针变量时,可以多次赋值
	// 赋值后的地址:
	printf("\na本身的地址是%p,指向的地址是%p",&a,a);
	printf("\n%s",a);
	getchar();
}

image-20230130133405513

内存布局图:

image-20230130132914387

  1. 如果定义了一个字符数组,那么它有确定的内存地址(即字符数组名是一个常量);而定义一个字符指针变量时,它并未指向某个确定的字符数据,并且可以多次赋值 [代码+图解]。

P 123 字符数组注意事项和细节 2023/1/31

一、字符串相关函数回顾

  • 常用字符串函数一览
序号函数目的
1strcpy(s1, s2);复制字符串 s2 到字符串 s1。
2strcat(s1, s2);连接字符串 s2 到字符串 s1 的末尾。
3strlen(s1);返回字符串 s1 的长度。
4strcmp(s1, s2);如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
5strchr(s1, ch);返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
6strstr(s1, s2);返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
#include<stdio.h>
#include<string.h>

void main(){

	char str1[12] = "Hello";
	char str2[12] = "World";
	char str3[12];
	int len ; 
	/* 复制 str1 到 str3 */
	strcpy(str3, str1);   // str3内容 "Hello"
	printf("strcpy( str3, str1) : %s\n", str3 );

	/* 连接 str1 和 str2 */
	strcat( str1, str2);
	printf("strcat( str1, str2): %s\n", str1 );  // "Hello world"

	/* 连接后,str1 的总长度 */
	len = strlen(str1);   // 因为进行了连接,所以总长度应该是10,10个字符
	printf("strlen(str1) : %d\n", len );

	if(strcmp(str1, str2)<0){
		printf("小于0");
	}else{
		printf("大于0");
	}
	getchar();
}

二、字符数组使用注意事项和细节

  1. 程序中往往依靠检测 ‘\0’ 的位置来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。因此,字符串长度不会统计 ‘\0’, 字符数组长度会统计 [案例]。

  2. 在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度,否则,在输出字符数组时可能出现未知字符。

  3. 系统对字符串常量也自动加一个’\0’作为结束符。例如"C Program”共有9个字符,但在内存中占10个字节,最后一个字节’\0’是系统自动加上的。(通过sizeof()函数可验证)。

  4. 定义字符数组时,如果 给的字符个数 比 数组的长度小,则系统会默认将剩余的元素空间,全部设置为 ‘\0’, 比如 char str[6] = “ab” , str内存布局就是[a] [b] [ \0 ] [\0] [\0] [\0]。

  5. 字符数组定义和初始化的方式比较多,比如:

#include<stdio.h>

void main(){

	char str1[ ]={"I am happy"}; // 默认后面加 '\0'
	char str2[ ]="I am happy"; // 省略{}号 ,默认后面加 '\0'
	char str3[ ]={'I',' ','a','m',' ','h','a','p','p','y'}; // 字符数组后面不会加 '\0', 可能有乱码
	char str4[5]={'C','h','i','n','a'}; //字符数组后面不会加 '\0', 可能有乱码
	char * pStr = "hello";		 // OK, 使用一个指针pStr,指向一个字符数组


	printf("\n str1 = %s",str1);
	printf("\n str1 = %s",str2);
	printf("\n str1 = %s",str3);
	printf("\n str1 = %s",str4);
	getchar();
}

P 124 冒泡排序分析和实现 2023/1/31

一、排序算法

介绍:排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。

排序的分类:

  1. 内部排序:指将需要处理的所有数据都加载到**内部存储器(内存)**中进行排序。

  2. 外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。

二、冒泡排序

基本介绍:冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

结论:因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。

  • 冒泡排序应用实例

案例:我们举一个具体的案例来说明冒泡法。我们将五个无序的数:{3, 9, -1, 10, -2}使用冒泡排序法将其排成一个从小到大的有序数列。

分析过程:

// 	冒泡排序的过程(从小到大) ===> 分析过程(思路) ===》 代码

原始数组{3, 9, -1, 10, -2}1轮排序:
(1)  3,9-1,10-2
(2) 3,-1,9,10,-2
(3) 3,-1,9,10,-2
(4)3,-1,9,-2,10  // 第1大的数就移动到最后2轮排序:
(1) -1,3,9,-2,10
(2)-1,3,9,-2,10
(3)-1,3,-2,9,10 //第2大的数就移动适当位置3轮排序:
(1)-1,3,-2,9,10
(2)-1,-2,3,9,10 第3大的数就移动适当位置4轮排序:
(1) -2,-1,3,9,10//第4大的数就移动适当位置
排序结束
// 是不需要进行第五轮的比较,因为四个数都已经找到合适的位置了

将上述的分析过程然后写成代码:

#include<stdio.h>

void main(){

	int arr[] = {3,9,-1,10,-2};

	// 因为每次排序几乎一样,因此,我们可以使用for循环处理
	int j;
	int t;  // 临时变量
	// 第一轮排序:
	for(j = 0; j < 4; j++){

		// 如果前面的数大于后面的数就,交换
		if(arr[j] > arr[j+1]){
			t = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = t;
		}
	}

	// 输出看看第一轮排序的的情况 
	for(j = 0 ; j < 5 ; j++){
		printf("%d ",arr[j]);
	}
	printf("\n");

	// 第二轮排序:
	for(j = 0; j < 3; j++){  // 第二轮比较3次就可以了,因为前一轮最大数已经固定了

		// 如果前面的数大于后面的数就,交换
		if(arr[j] > arr[j+1]){
			t = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = t;
		}
	}

	for(j = 0 ; j < 5 ; j++){
		printf("%d ",arr[j]);
	}
	printf("\n");

	// 第三轮排序:
	for(j = 0; j < 2; j++){  // 第二轮比较2次就可以了,因为前两轮已经固定两个数

		// 如果前面的数大于后面的数就,交换
		if(arr[j] > arr[j+1]){
			t = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = t;
		}
	}

	for(j = 0 ; j < 5 ; j++){
		printf("%d ",arr[j]);
	}
	printf("\n");

	// 第四轮排序:
	for(j = 0; j < 1; j++){  // 第二轮比较2次就可以了

		// 如果前面的数大于后面的数就,交换
		if(arr[j] > arr[j+1]){
			t = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = t;
		}
	}

	for(j = 0 ; j < 5 ; j++){
		printf("%d ",arr[j]);
	}
	printf("\n");
	getchar();
} 

代码优化:

// 因为每次排序几乎一样,因此,我们可以使用for循环处理
#include<stdio.h>

void main(){
	int arr[] = {3,9,-1,10,-2,-11,989};
	
	// 因为每次排序几乎一样,因此,我们可以使用for循环处理
	int j;
	int i;
	int t;  // 临时变量
	int arrLen = sizeof(arr) / sizeof(int);  // 数组的大小,通过计算得到,这样就很方便,可以随意改变数组大小

	for(i = 0 ; i < arrLen-1 ; i++){  // 这里可以写活,刚好等于数组的大小减1
	// 第一层循环就是总的轮数
		for(j = 0; j < arrLen-1-i; j++){
		// 第二层循环就是执行交换,进行轮中的排序
			// 如果前面的数大于后面的数就,交换
			if(arr[j] > arr[j+1]){   // 从大到小,把大于改成小于就可以了
				t = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = t;
			}
		}
		// 获取每一轮循环后的结果
		for(j = 0 ; j < arrLen ; j++){
			printf("%d ",arr[j]);
		}
		printf("\n"); // 每一轮循环输出结果后换行
	}
	getchar();
}

函数优化处理:

#include<stdio.h>

// 冒泡排序的函数:
void bubbleSort(int arr[],int arrLen){
	// 因为每次排序几乎一样,因此,我们可以使用for循环处理
	int j;
	int i;
	int t;  // 临时变量

	for(i = 0 ; i < arrLen-1 ; i++){  // 这里可以写活,刚好等于数组的大小减1
		// 第一层循环就是总的轮数
		for(j = 0; j < arrLen-1-i; j++){
			// 第二层循环就是执行交换,进行轮中的排序
			// 如果前面的数大于后面的数就,交换
			if(arr[j] > arr[j+1]){   // 从大到小,把大于改成小于就可以了
				t = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = t;
			}
		}
		// 获取每一轮循环后的结果
		for(j = 0 ; j < arrLen ; j++){
			printf("%d ",arr[j]);
		}
		printf("\n"); // 每一轮循环输出结果后换行
	}
}

void main(){
	int j;
	int arr[] = {3,9,-1,10,-2,-11,};
	int arrLen = sizeof(arr) / sizeof(int);  // 数组的大小,通过计算得到,这样就很方便,可以随意改变数组大小
	bubbleSort(arr,arrLen);  // 数组默认是地址传递(指针)
	getchar();
}

总结: 以上案例的优化过程总结了前面所学,进行了一个综合。

P 125 顺序查找和二分查找 2023/2/1

一、基本介绍

在C中,我们常用的查找有两种

1) 顺序查找

2) 二分查找(默写)【比较重要】

  • 案例演示:

二、顺序查找

案例一:有一个数列:{23, 1, 34,89, 101},猜数游戏:从键盘中任意输入一个数,判断数列中是否包含该数,

【顺序查找】要求: 如果找到了,就提示找到,并给出下标值, 找不到提示 没有。

#include<stdio.h>

int seqSearch(int arr[], int arrLen, int val){

	int i;
	for(i = 0; i < arrLen ; i++){
		if(val == arr[i]){
			return i;
		}
	}
	// 如果在for循环中,没有执行到return,说明没有找到
	return -1; // 可以返回一个-1,因为下标是没有-1的
}

void main(){
	/*
	有一个数列:{23, 1, 34,89, 101},
	猜数游戏:从键盘中任意输入一个数,判断数列中是否包含该数,
	【顺序查找】要求: 如果找到了,就提示找到,并给出下标值, 找不到提示 没有。
	*/
	// 分析思路:
	//1. 按照数组进行数组进行遍历,如果相等则找到。
	int arr[] = {23,1,34,89,101};
	int arrLen = sizeof(arr) / sizeof(int);
	int index = seqSearch(arr,arrLen,-101); // 将形参传入进去,返回值用index进行接收
	if(index != -1){
		printf("找到了下标为%d",index);
	}else{
		printf("没有找到");
	}
	getchar();
}

三、二分查找

案例二:请对一个有序数组进行【二分查找】 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"
二分查找的前提是,该数组是一个有序数组。

#include<stdio.h>

// 二分查找:
int binarySearch(int arr[], int leftIndex, int rightIndex, int findVal){

	// 先找到中间这个数midVal
	int midIndex = (leftIndex + rightIndex) / 2;  // 比如数组大小为5,左下标(0+5)/2=2.5取整为2
	int midVal = arr[midIndex];   // 中间值

	//如果leftIndexf > rightIndex ,说明这个数组都比较过,但是没有找到。
	// 因为:leftIndex是在逐渐向右边移动,rightIndex是逐渐向左边移动,所以如果左边大于右边,
	// 就说明已经将整个数组范围全部查找完但是没有找到。
	if(leftIndex > rightIndex){
		return -1;  // !!!
	}

	// 如果midVal > finVal说明 应该在midVal的左边去查找
	if(midVal > findVal){
		binarySearch(arr,leftIndex,midIndex-1,findVal); // !!! 函数递归,递归相当于缩小范围进行查找
		// 如果midVal < finaVal说明 应该在midVal的右边去查找
	}else if(midVal < findVal){
		binarySearch(arr,midIndex+1,rightIndex,findVal); // 在右边查找,右边索引不变,左边索引加一
	}else{
		return midIndex;  // 返回该数的下标
	}

}
void main(){
	/*
	请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ,输入一个数
	看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"
	。二分查找的前提是,该数组是一个有序数组
	*/
	int arr[]= {1,8, 10, 89, 1000, 1234};
	int arrLen = sizeof(arr) / sizeof(int);
	int index = binarySearch(arr, 0 ,arrLen-1,-1234);  // 这里长度-1,因为下标从0开始,得到长度是6,但第六个下标为5
	if(index != -1){
		printf("index = %d",index);
	}else{
		printf("没有找到");
	}
	getchar();

	// 思路分析:
	// 比如我们要查找的数是findVal
	// 1.先找到 数组中间这个数midVal,和finVal比较
	// 2.如果midVal > finVal说明 应该在midVal的左边去查找
	// 3.如果midVal < finVal说明 应该在midVal的右边去查找
	// 4. 如果midVal == findVal ,说明找到
	// 5. 如果leftIndexf > rightIndex ,说明这个数组都比较过,但是没有找到
}

P 126 二维数组的基本使用 2023/2/1

一、基本介绍

多维数组我们介绍二维数组。

  • 应用场景

比如我们开发一个五子棋游戏,棋盘就是需要二维数组来表示。如图:

image-20230201161024628

二、入门案例

**案例一:**请用二维数组输出如下图形。

image-20230201162349511

#include<stdio.h>
void main () {

	// a[4][6]  :表示的是一个4行6列的二维数组
    // (1)先定义在初始化:
	int a[4][6]; // 没有初始化,则是分配的内存垃圾值(不确定的值)
	int i, j;

	//全部初始化 0
	for(i = 0; i < 4; i++) {      // 先遍历行
		for(j = 0; j < 6; j++) {  //  在遍历列
			a[i][j] = 0;
		}
	}
	a[1][2] = 1;
	a[2][1] = 2;
	a[2][3] = 3;

	// 输出二维数组
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 6; j++) {
			//printf("a[%d][%d]=%d ", i, j, a[i][j]);
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}

相关地址输出:

	//看看二维数组的内存布局
	printf("\n二维数组a的首地址=%p", a);
	printf("\n二维数组a[0]的地址=%p", a[0]);
	printf("\n二维数组a[0][0]的地址=%p", &a[0][0]);
	printf("\n二维数组a[0][1]的地址=%p", &a[0][1]);
	//将二维数组的各个元素得地址输出
	printf("\n");
	for(i = 0; i < 4; i++) {
		printf("a[%d]的地址=%p ", i, a[i]);   // 输出a[0-4]的地址
		for(j=0; j < 6; j++) {
			printf("a[%d][%d]的地址=%p ", i, j , &a[i][j]); // 输出全部地址
		}
// 地址是连续分部的,虽然看着换行了,第二行的第一个元素地址是上一行最后加4
		printf("\n");	
	}
	getchar();
}

内存分析图:

image-20230201170914734

总结:

  1. 语法: 类型 数组名[大小] [大小],比如: int a [2] [3];
  1. 二维数组在内存的存在形式,各个元素的地址是连续分布的,即在前一个元素基础上+4(int类型)。

三、直接初始化

  • 定义 类型 数组名[大小] [大小] = {{值1,值2…},{值1,值2…},{值1,值2…}};
    或者 类型 数组名[大小] [大小] = { 值1,值2,值3,值4,值5,值6 …};

image-20230201171744020

P 127 二维数组的应用案例 2023/2/2

一、应用案例

案例一:请使用灵活的方式遍历如下数组 :

int map[3][3] = {{0,0,1},{1,1,1},{1,1,3}};

#include<stdio.h>

void main(){

   int  map[3][3] = {{0,0,1},{1,1,1},{1,1,3}};
   // 遍历
   // 先得到行
   // 1. sizeof(map) 得到这个map数组的大小 9 * 4 = 36
   // 2. sizeof(map[0]) 得到map中,第一行有多大 3 * 4 = 12
   int rows = sizeof(map) / sizeof(map[0]);  // 3个元素
   //printf("rows = %d",rows);

   // 得到列
   int cols = sizeof(map[0]) / sizeof(int);  // 一行的大小是12,int占4字节,除以4可以得到列。 sizeof([0][0])
   int i;
   int j;
   for(i = 0; i < rows; i++){
   	for(j = 0; j < cols; j++){
   		printf("%d ",map[i][j]);
   	}
   	printf("\n");
   }
   getchar();
}

案例二:int arr[3][2]={{4,6},{1,4},{-2,8}};遍历该二维数组,并得到和?

#include<stdio.h>

void main(){
	int arr[3][2]={{4,6},{1,4},{-2,8}};
	int i,j;
	int sum = 0;
	int rows = sizeof(arr) / sizeof(arr[0]);
	int column = sizeof(arr[0]) / sizeof(int);
	for(i = 0; i < rows ; i++){
		for(j = 0; j < column ; j++){
			printf("%d ",arr[i][j]);
		sum += arr[i][j];  // 定义一个sum,每次累加
		}
		printf("\n");
	}
	printf("sum=%d",sum);  // 21 
	getchar();
}

案例三:定义二维数组,用于保存三个班,每个班五名同学成绩,并求出每个班级平均分、以及所有班级平均分【 数据要求从控制台输入 】

#include<stdio.h>

void main(){
	/*
	定义二维数组,用于保存三个班,每个班五名同学成绩,并求出每个班级平均分、
	以及所有班级平均分【 数据要求从控制台输入 】
	*/

	// 分析:
	// 1 .创建一个scores[3][5]
	// 2. 遍历,给赋值
	// 3. 再次遍历统计总分和平均分
	// 4. 最后输出

	double score[3][5]; //
	int rows = sizeof(score) / sizeof(score[0]),cols = sizeof(score[0]) / sizeof(double), i, j; // 
	//classTotalScore 各个班级总成绩, totalScore 所有学生成绩
	double totalScore = 0.0, classTotalScore = 0.0;  // 定义所有班级总成绩、个各班的成绩
	for (i = 0; i < rows; i++ ) {
		for (j = 0; j < cols ; j++ ) {
			score[i][j] = 0.0;  // 初始化
		}
	}
	//遍历给每个学生输入成绩
	for (i = 0; i < rows; i++ ) {
		for (j = 0; j < cols ; j++ ) {
			printf("请输入第 %d 个班的 第 %d 个 学生的成绩", i + 1, j + 1); // 下标从0开始,但是没有第0个班,所以+1
			scanf("%lf", &score[i][j]);
		}
	}

	// 显示成绩情况
	for(i = 0; i < rows ; i++){
		for(j = 0 ; j < cols; j++){
			printf("%.2f  ",score[i][j]);
		}
		printf("\n");
	}

	// 统计各个班的总成绩,和所有学生的总成绩
	for(i = 0; i < rows; i++){
		classTotalScore = 0.0; // 每次清0,这样才能统计每个班的总分
		for(j = 0; j < cols; j++){
			classTotalScore += score[i][j];
		}
		printf("\n 第%d个班的平均成绩时%.2f",i+1,classTotalScore/cols);
		totalScore += classTotalScore;   //  将该班级的总分,累积到totalScore
	}
	printf("\n 所有学生的总成绩是 %.2f ,平均成绩%.2f",totalScore,totalScore/(rows * cols)); 
																						// 平均成绩=总成绩/人数(rows * cols)
	getchar();
	getchar();
} 

image-20230202124408624

P 128 二维数组的注意事项和细节 2023/2/2

一、注意事项

  1. 可以只对部分元素赋值,未赋值的元素自动取“零”值【案例】。
#include<stdio.h>

int main(){
	int a[4][5] = {{1},{2},{3},{1}};  // 每行后面是没有赋值的
					//{1}等价于 {1,0,0,0,0} 
	int i,j;
	for(i = 0; i < 4; i++){
		for(j = 0; j < 5; j++){
			printf("%d ",a[i][j]);
		}
		printf("\n");
	}
	getchar();
}

image-20230202170107381

  1. 如果对全部元素赋值,那么第一维的长度可以不给出(二维数组可以省略行)。比如:
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 
可以写为: 
int a[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};  // 是等价于前面的写法
  1. 二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。
// 二维数组a[3][4]可看成三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2]。
// 这三个一维数组都有 4 个元素,如,一维数组 a[0] 的元素为 a[0][0]、a[0][1]、a[0][2]、a[0][3]

image-20230202171022644

P 129 断点调试介绍和快捷键 2023/2/3

一、需求引入

引入:在开发中,程序员发现一个非常诡异的错误,怎么看源代码都发现不了这个错误,这时老程序员就会温馨提示,可以使用断点调试,一步一步的看源码执行的过程,从而发现错误所在。

基本介绍:断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。然后程序可以进行分析从而找到这个Bug。

二、断点调试的快捷键

F5开始调试、执行到下一个断点
F11逐句执行代码,会进入到函数体
F10逐过程执行(遇到函数,不会进入到函数体)
Shift+F11跳出(跳出某个函数,跳出前会将该函数执行完)
Shiit+F5终止调试

P 130 断点调试应用案例(1)2023/2/3

一、应用案例

案例一:看一下变量的变化情况等:(逐过程执行代码……)

#include<stdio.h>
void main() {
	int sum = 0;
	int i = 0;
	for(i = 0; i < 10; i++) {
		sum += i;
		printf("\n i=%d", i);
		printf("\n sum=%d", sum);
	}
	printf("退出for循环了~~");
}

image-20230203125254302

image-20230203125503125

image-20230203125659108

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗星星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值