Bootstrap

王道-C语言督学营-5.3 数组访问越界与数组的传递

一、数组的访问越界

下面借助一个数组的实例来掌握数组元素的赋值、访问越界:

#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是一个子函数,它接受两个整数参数ab,并返回它们的和。 

(2)子函数的作用

  1. 代码重用‌:通过定义子函数,可以避免在多个地方重复编写相同的代码
  2. 模块化‌:将程序划分为多个子函数,每个子函数负责特定的任务,可以提高代码的可读性和可维护性
  3. 递归‌:某些问题可以通过递归调用子函数来解决,即子函数调用自身。

(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变量中。

;