一、数组的访问越界
下面借助一个数组的实例来掌握数组元素的赋值、访问越界:
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int j = 20;
int i = 10;
a[5]=6; //导致 越界访问
a[6]=7; // 越界访问会造成 数据 异常
printf("i=%d\n",i); //最后导致 i的值发生改变
return 0;
}
通过设置“断点”,我们可以清晰的看到,当程序运行到“a[6]=7;”时,i值还未发生变化,但是当“a[6]=7”运行后,则i的值发生了变化,从10变成了7。
注意: 这是因为“越界访问”会导致数据发生异常
举个例子:
原本小明和小红是从小玩到大的好朋友(此时友谊是“朋友”),但是某一天,小明突然对小红说“做我女盆友吧”,并且小明还强行kiss了小红!
记住:
① 小明突然对小红说“做我女盆友吧”
② 小明强行kiss了小红
小明执行①后,小红会当作开玩笑,或者是口头上拒绝,两者之间还是朋友,友谊并未发生质变;
当小明执行②后,小红大概率会翻脸,并且发生一些冲突,此时友谊发生了本质上的变化。
由于上述操作已经超出的朋友间的界限,此越界行为可能导致 两者变成情侣,或者导致两者最终连朋友都做不成,也就是友谊从 “朋友” 变成了 “男女朋友 / 陌生人”。
注意:
编译器并不检查 程序对数组下标的引用是否在数组的合法范围内。
二、数组的传递
一维数组的传递,数组长度是无法传递给子函数的,在C语言中,函数调用的方式的值传递。
接下来举个例子:
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int i;
for(i=0;i<5;i++)
{
printf("%d\n",a[i]);
}
}
输出结果为:
#include<stdio.h>
void print(int a[5]) //void 函数不需要有返回值
{
int i;
for(i=0;i<sizeof(a)/sizeof(int);i++)
{
printf("%d\n",a[i]);
}
}
int main() //主函数
{
int a[5] = {1,2,3,4,5};
print(a);
return 0;
}
输出结果为:
(1)为什么数组的长度无法从主要函数传递到子函数呢?
接下来为大家稍微解释一下
此时a的字节长度为20
刚进入到 子函数中 a的字节长度为8,说明并未成功传递
综上所述:
因为“数组”在C语言中,它直接传递到“子函数”中,是无法将数组传递过去,即“没有数组传递”的功能,传递过去的时候相当于是弱化成“指针”,也就是说 子函数中 sizeof(a)中的a是指针,为8个字节。(由于此时并未到指针章节,所以现在先不介绍,大家了解一下就行)。
简化一下:
数组名传递到子函数后,子函数形参接收到的是数组的起始地址,因此不能够把数组的长度传递给子函数。
(2)如何实现 主函数中的数组的元素个数 传递到 子函数中?
如下所示:
#include<stdio.h>
void print(int a[],int length)
{
int i;
for(i=0;i<length;i++)
{
printf("%d\n",a[i]);
}
}
int main() //主函数
{
int a[5] = {1,2,3,4,5};
print(a,5);
return 0;
}
输出结果为:
通过自身多定义一个“变量”(也就是想传递的数组长度),通过该变量实现数组长度的传递。上述案例则是多定义了一个变量 length,通过它实现了数组长度从主函数传递到子函数。
(3)在子函数中 能否改变 数组的值?
答案是: 可以的
#include<stdio.h>
void print(int a[],int length)
{
int i;
for(i=0;i<length;i++)
{
printf("%3d",a[i]);
}
a[2]=50;
printf("\n");
}
int main() //主函数
{
int a[5] = {1,2,3,4,5};
print(a,5);
printf("a[2]=%d",a[2]);
return 0;
}
结果为:
说明a[2]的值在子函数中发生了改变,
(4) 补充一下“子函数”
在C语言中,子函数通常指的是在主函数(通常是main
函数)之外定义的函数。这些子函数可以被主函数或其他子函数调用,以实现代码的模块化和重用。
注意:
(1)子函数的定义
子函数的定义包括返回类型、函数名和参数列表。
例如:
int add(int a, int b) {
return a + b;
}
这里,add
是一个子函数,它接受两个整数参数a
和b
,并返回它们的和。
(2)子函数的作用
- 代码重用:通过定义子函数,可以避免在多个地方重复编写相同的代码。
- 模块化:将程序划分为多个子函数,每个子函数负责特定的任务,可以提高代码的可读性和可维护性。
- 递归:某些问题可以通过递归调用子函数来解决,即子函数调用自身。
(3)子函数的注意事项 (了解即可)
- 子函数的返回类型必须与返回值匹配。
- 子函数的参数列表必须与实际调用时提供的参数匹配。
- 在调用子函数之前,需要确保子函数已经被定义(或者至少已经声明)。如果子函数在调用它的函数之后定义,可以在调用之前先声明子函数的原型。
(3)子函数的调用
在主函数或其他子函数中,可以通过函数名和参数来调用子函数。
例如:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 4);
printf("The result is: %d\n", result);
return 0;
}
在这个例子中,main
函数调用了add
函数,并将返回值存储在result
变量中。