学期:2024-2025-1 学号:20241303 《计算机基础与程序设计》第十二周学习总结
作业信息
这个作业属于哪个课程 | <班级的链接>(如2024-2025-1-计算机基础与程序设计) |
---|---|
这个作业要求在哪里 | <作业要求的链接>(如2024-2025-1计算机基础与程序设计第十二周作业) |
这个作业的目标 | <写上具体方面> 加入云班课,参考本周学习资源 自学教材《C语言程序设计》第11章并完成云班课测试 |
作业正文 | ... 本博客链接 |
教材学习内容总结
《C语言程序设计》第11章:指针与数组
一、指针与数组的关系
在C语言中,数组名在很多情况下可以看作是一个指针。具体来说,数组名是指向数组第一个元素的指针(常量指针,不能对数组名本身进行赋值操作来改变它所指向的位置)。
例如,有一个整型数组int arr[5] = {1, 2, 3, 4, 5};
,数组名arr
就相当于一个指向arr[0]
的指针。
-
通过指针访问数组元素
- 可以定义一个指针变量来指向数组,然后通过指针来访问数组元素。例如:
int *p; p = arr; // 让指针p指向数组arr的第一个元素
- 此时,
*p
就表示数组的第一个元素arr[0]
,*(p + 1)
就表示arr[1]
,以此类推。这是因为在C语言中,数组元素在内存中是连续存储的,当指针p
指向数组的第一个元素时,p + 1
就指向数组的下一个元素(这里的加1实际上是根据指针所指向的数据类型的字节大小来移动的。对于int
类型,一般情况下一个int
占4个字节,所以p+1
会移动4个字节,指向下一个int
元素)。
- 可以定义一个指针变量来指向数组,然后通过指针来访问数组元素。例如:
-
指针运算与数组下标运算的等价性
- 对于数组
arr
,访问数组元素arr[i]
和通过指针p
访问*(p + i)
是等价的。这是因为数组下标的计算实际上也是基于指针的偏移。 - 例如,在
for
循环中遍历数组:- 使用数组下标:
for(int i = 0; i < 5; i++){ printf("%d ", arr[i]); }
- 使用指针:
for(int i = 0; i < 5; i++){ printf("%d ", *(p + i)); }
- 使用数组下标:
- 对于数组
二、数组作为函数参数时与指针的关系
当数组作为函数参数传递时,实际上传递的是数组的首地址,也就是一个指针。
- 函数形参中的数组和指针
- 在函数定义中,以下两种形式是等价的:
- 形式一:
void func(int arr[], int n);
- 形式二:
void func(int *arr, int n);
- 形式一:
- 这两种形式都表示函数
func
接受一个整型数组(或指向整型的指针)和数组的大小n
作为参数。
- 在函数定义中,以下两种形式是等价的:
- 在函数内部操作数组元素
- 当在函数内部通过形参来操作数组元素时,就如同操作指针一样。例如:
void func(int *arr, int n){ for(int i = 0; i < n; i++){ printf("%d ", arr[i]); // 等价于*(arr + i) } }
- 当在函数内部通过形参来操作数组元素时,就如同操作指针一样。例如:
三、二维数组与指针
- 二维数组的存储方式
- 二维数组在内存中也是按行顺序存储的。例如,有一个二维数组
int matrix[3][4];
,它的存储顺序是先存储第一行的4个元素,然后存储第二行的4个元素,最后存储第三行的4个元素。
- 二维数组在内存中也是按行顺序存储的。例如,有一个二维数组
- 指向二维数组的指针
- 可以定义一个指针来指向二维数组的行。例如:
- 定义一个指针
int (*p)[4];
,这个指针p
可以指向一个包含4个int
元素的数组(也就是二维数组的一行)。 - 可以让
p = matrix;
,此时p
就指向matrix
的第一行。*(p + 1)
就指向matrix
的第二行。
- 定义一个指针
- 要访问二维数组中的元素,可以使用
(*(p + i))[j]
或者p[i][j]
的形式(这里i
表示行索引,j
表示列索引)。这种方式和直接使用二维数组名matrix[i][j]
在功能上是类似的,但通过指针的方式可以更加灵活地操作数组,比如在动态内存分配二维数组时会经常用到。
- 可以定义一个指针来指向二维数组的行。例如:
教材学习中的问题和解决过程
- 指针越界访问
- 问题描述:
- 当指针指向数组元素时,如果不小心让指针超出了数组的范围,就会导致越界访问。这是因为C语言不会自动检查指针是否超出了数组边界。例如,对于数组
int arr[5]
,定义一个指针int *p = arr;
,如果在访问数组元素时使用*(p + 5)
或p[5]
,就会发生越界访问。
- 当指针指向数组元素时,如果不小心让指针超出了数组的范围,就会导致越界访问。这是因为C语言不会自动检查指针是否超出了数组边界。例如,对于数组
- 后果:
- 越界访问可能会读取或修改不属于该数组的内存区域的数据。这可能导致程序出现错误的结果,比如读取到一些随机值或者修改了其他变量的值。更严重的是,可能会导致程序崩溃,因为有可能会访问到操作系统保护的内存区域或者已经被其他程序占用的内存。
- 解决方法:
- 在使用指针访问数组元素时,要始终确保指针的偏移量在数组的有效范围内。可以通过检查指针的偏移量或者使用数组的长度来避免越界。例如,在一个循环中访问数组元素时,可以使用
for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
来确保只访问数组内的元素,其中sizeof(arr)/sizeof(arr[0])
可以计算出数组arr
的元素个数。
- 在使用指针访问数组元素时,要始终确保指针的偏移量在数组的有效范围内。可以通过检查指针的偏移量或者使用数组的长度来避免越界。例如,在一个循环中访问数组元素时,可以使用
- 问题描述:
- 数组名作为指针常量被错误赋值
- 问题描述:
- 数组名本身是一个指针常量,它指向数组的第一个元素,不能被重新赋值。例如,对于数组
int arr[5];
,如果尝试arr = NULL;
或者arr = another_array;
是错误的。
- 数组名本身是一个指针常量,它指向数组的第一个元素,不能被重新赋值。例如,对于数组
- 后果:
- 编译器会报错,因为这种赋值操作违反了数组名的常量属性。这样的代码无法通过编译。
- 解决方法:
- 如果想要使用一个指针来操作数组,可以定义一个普通的指针变量,然后将数组名赋值给这个指针变量。例如,
int *p = arr;
,这样就可以通过指针p
来操作数组,而不会出现对数组名进行非法赋值的情况。
- 如果想要使用一个指针来操作数组,可以定义一个普通的指针变量,然后将数组名赋值给这个指针变量。例如,
- 问题描述:
- 指针类型不匹配导致的错误访问
- 问题描述:
- 当指针的类型与它所指向的数据类型不匹配时,会导致错误的内存访问。例如,有一个
char
类型的数组char str[10];
,如果定义一个int *p = (int *)str;
,然后通过p
来访问内存,就会出现问题。
- 当指针的类型与它所指向的数据类型不匹配时,会导致错误的内存访问。例如,有一个
- 后果:
- 由于指针类型的错误,在访问内存时,会按照错误的类型来解析内存中的数据。在上述例子中,当通过
p
访问内存时,会按照int
类型(通常占4个字节)来读取内存,而不是按照char
类型(占1个字节),这会导致读取到的数据是错误的,而且还可能会访问到不属于str
数组的内存区域。
- 由于指针类型的错误,在访问内存时,会按照错误的类型来解析内存中的数据。在上述例子中,当通过
- 解决方法:
- 确保指针的类型与它所指向的数据类型一致。在上述例子中,应该定义
char *p = str;
来正确地访问char
类型的数组。
- 确保指针的类型与它所指向的数据类型一致。在上述例子中,应该定义
- 问题描述:
- 二维数组指针操作的复杂性导致的错误
- 问题描述:
- 在处理二维数组和指针时,容易出现混淆。例如,对于二维数组
int matrix[3][4];
,如果错误地定义指针或者错误地使用指针来访问数组元素,就会出现问题。如定义一个普通的int *p = matrix;
(这里的类型不匹配,因为matrix
是一个指向包含4个int
元素的数组的指针类型),然后尝试使用p
来访问二维数组元素就会出错。
- 在处理二维数组和指针时,容易出现混淆。例如,对于二维数组
- 后果:
- 可能会导致错误的内存访问,程序可能会输出错误的结果或者崩溃。例如,按照错误的方式访问二维数组元素可能会读取到其他行或者列的数据,或者访问到非法的内存地址。
- 解决方法:
- 正确理解二维数组与指针的关系。对于二维数组
int matrix[3][4]
,应该定义int (*p)[4]=matrix;
来正确地指向二维数组的行。在访问元素时,可以使用(*(p + i))[j]
或者p[i][j]
的形式来确保正确地访问二维数组中的元素。
- 正确理解二维数组与指针的关系。对于二维数组
- 问题描述:
基于AI的学习
代码调试中的问题和解决过程
问题:将一个数的每一位数字倒序后再输出这个数字
方法:
#include <stdio.h>
int main() {
int num;
int reversed = 0;
printf("请输入一个整数: ");
scanf("%d", &num);
while (num!= 0) {
reversed = reversed * 10 + num % 10; // 取 num 的最后一位数字并添加到 reversed 的末尾
num /= 10; // 去掉 num 的最后一位数字
}
printf("倒序后的数字是: %d\n", reversed);
return 0;
}
其他(感悟、思考等,可选)
需要多看课本。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第十二周 | 2100/3000 | 1/2 | 20/20 | |
第十三周 | 300/500 | 2/4 | 18/38 | |
第十四周 | 500/1000 | 3/7 | 22/60 | |
第十五周 | 300/1300 | 2/9 | 30/90 |