OK,那么在了解完了如何用指针作函数参数的情况以后啊, 我们再来看一下指针用作函数返回值的情况。 毫无疑问,啊我们可以从函数里头啊返回一个指针。 那比方说以前呢,我们定义一个返回整型的一个函数,啊我们可以这么来定义。 那么当我们去定义一个返回指向整数类型的指针的函数 的时候,我们就可以这样来定义。非常的简单,也容易理解。 那么在函数名之前呢,写一个指针符号。 然后呢,在这个指针符号前面写清楚所返回的指针类型的基类型。 我想呢,大家都比较容易理解。我们来看一个例子。 在这个例子里头啊,我们定义了一个函数。 这个函数呢就能够返回一个指针,并且呢是指向int型类型的一个指针。 它的作用是这样的,当主程序里头定义一个二维数组的时候, 那我们就可以把这个数组的数组名当作参数传递给这个函数。 那么这个函数呢就能够把其他的两个参数 所指定的相应的行和列上的地址返回出来。 那么比方说,我们在这传进去的是2和3,那么这个函数呢就能够把 数组a的第二行第三个元素的地址返回出来。 这就是get函数的含义。那么我们来观察一下这个程序。首先呢,我们传给函数的 参数是一个数组名,而且是一个二维数组的数组名。 那么函数的形参呢,就必须是与之相对应的 变量。那这个变量呢,在这个程序里头啊是这样来定义的, int arr [][4]。那这样定义可以吗?首先根据我们前面所讲过的 当我们在函数的形参里面去定义的是一个数组的时候,就相当于我们定义了一个 指针变量。 这是我们刚刚讲过的。除此以外,由于我们传入函数的参数,啊是一个a, 这个a呢,是一个指向包含4个整型元素的一位小数组的指针, 对不对。所以说,在这儿的这个arr也必须符合 这个含义。那么这样的一种定义方式也能够 满足程序的要求,当然除了这种定义方式之外我们曾经说过,我们照样可以这样来定义, int然后arr然后4,那么这两种定义方式都是可以的。 这是刚刚我们讲过的。 那么get这个函数呢,在进行完相应的操作以后, 获取到第n行第m列的 指针以后,它呢,就要返回这个指针,当然在这 我们借助一个临时的指针变量pt做一个过渡了, 直接返回这个指针,那么主函数呢,获取到这个指针以后, 我就可以直接通过*操作,把相应的内容给打印出来。 啊这是这个程序。那么通过这个程序我们就可以看到, 那么定义一个返回指针的函数还真没那么难,对不对。 嗯但是,有一些事情啊我们必须要注意。 比方说我们来看这么一个程序,那么请你呢,阅读一下这个程序, 看看这个程序的输出结果将会是怎样的。 OK,我们来分析一下。 在这个程序里头呢定义了一个函数,getInt1[],它呢, 会返回一个指针,这个指针的类型呢,是指向Int型的。 这是定义了一个具有指针类型返回值的这么一个函数, 然后在主函数里头呢,我定义了一个普通指针变量p, 它呢也指向整数类型。然后呢, 调用getInt1,并且呢,利用 getInt1的返回结果,对p进行赋值。 赋完值以后呢。我就用*p, 把p所指向的那个存储单元的内容给它打印出来。 那现在我们的问题是,这个p会打印出什么来呢? 当然,p打印出什么内容来,我们要看一下getInt1将会返回一个什么样的指针, 对不对,啊我们先来分析一下,看它会返回一个什么样类型的指针。那么在 getInt1里头呢,我定义了一个变量,叫value1,并且呢,赋初值为20。 当然,在函数里面所定义的一个变量,这属于局部变量, 然后呢我把这个value1的地址直接返回出来,作为函数的返回值。 也就是说啊,main函数里边这个指针变量p, 所得到的这个指针,指向谁的呀它是? 指向value1的。啊是一个value1的指针。 那么在这儿,当我们去打印这个p的时候,会打印出什么来呢? 那有的同学说,那应该会打印20啊。是这样的么? 那有的同学说为什么不是这样的呢? 那是因为啊,在这儿所定义的这个value1,它是一个局部变量。 那么根据以前我们所讲过的,局部变量作用范围的知识,我们 就知道value1的有效范围是从哪儿到哪儿啊, value1的有效范围实际上是从定义开始一直到这个 函数的结束。嗯,是这样的一个作用范围。 也就是说,当这个函数存在的时候, value1呢是存在的。当函数被调用完毕, 函数的存储空间被从内存中释放掉的时候, 这个变量还存在吗?也不存在了。 所以说这个时候你再去打印*p的值,你知道会打印出什么结果吗。 我也不知道会打印出什么样的结果。OK,我们再来回顾一下这个 程序的运行过程。首先呢我们定义了两个函数,一个main函数, 一个getInt1的函数。在main函数的里面呢,我们又定义了一个指针变量p, 然后呢我们调用getInt1这个函数给p赋值, 那么当我们调用getInt1这个函数的时候, 那么就在内存里头生成一片内存空间,把getInt1 load进来,对不对。这是我们以前讲过的。 然后呢,getInt1就开始运行。 当getInt1运行的时候呢,在它的里面定义了一个 临时的指针变量value1,并且呢,赋初值为20。 并且呢函数getInt1的运行结果是把value1的地址 传递给了指针变量p,也就是说p指向了 value1,对吧,这是这句程序执行完毕以后,得到的一个状态。 那么当这句程序执行完毕以后, 那么getvalue1就被释放掉了,啊这片内存空间就被释放掉了。 那么释放掉以后,程序继续往下执行,执行到哪一句啊, 执行到这一句,cout*p, 这个时候p所指向的这片内存空间已经被 释放掉了。这个时候我们再来打印*p, 会打印出什么结果来呢,那谁也不知道可能会打印出什么样的结果来。 那打印的这个结果就严重取决于p所指向的 这片内存空间里面现在是一个什么样的值。 对不对,那么如果刚才的赋值20没有 被新的内容给覆盖掉,那么仍然会打印出来 20。如果它已经被覆盖掉了, 那么就会打印出新的内容来。至于新的内容是什么, 谁也不知道。这取决于系统使用的状态了。所以说, 这个程序的返回结果是无法完全预测的。 也就是说这个程序所返回的值,是一个非常不稳定的值。 因为这个p指向了一片已经被释放掉的存储空间。 那有的同学说,李老师我怎么去运行这个程序的时候啊, 我总能够得到20呢,无论我运行多少遍,它总得到的都是20。 那你真的是比较幸运,那么接下来呢我就构造一个新的程序,你看这个程序, 那么你在去运行这个程序的时候,你看看你的结果应该是怎样的。 那么在我的机器上呢,那么这个程序运行的结果呢是30。 呃我们来看一下它应该不应该等于30。 主程序里头定义了两个指针变量,p和q,我们分别用 getInt1和getInt2的返回值,对p和q进行赋值。 最后输出的时候呢,我只输出*p的值,也就是说第一次调用的时候, 啊调用的getInt1的返回的那个指针所指向的存储单元的内容。 嗯我们来看一下getInt1和getInt2都做了些什么。在getInt1里头我定义了一个 临时变量value1,它的值呢是20。啊这跟刚才我们的 程序里头是一样的,那么在getInt2里头呢 我定义了一个临时的指针变量Value2,它的值呢是30, 然后呢,这两个函数分别返回value1和value2的 地址,当然,这value1和value2都是 局部变量,对不对,它们都是局部变量,也就是说, 它们返回的值,都跟刚才我们程序里面介绍的情况是一样的 然后呢,我在这打印的时候,打印的是*p,也就是getInt1, 所返回的值,那么按照刚才的那个程序,这个时候呢,可能大多数情况下,会打印出来 20,对不对?啊,那么实际你去运行 这个程序的时候,你会发现,很多时候,你得到的结果是30, 也就是说啊,当调用完,getInt1, 以后,那么getInt1的这片空间就被释放 掉了,那么系统呢,又利用了这片已经被释放掉的空间存储了 getInt2,所以呢,当你调用完getInt1,又调用完getInt2 然后返回头来,要去打印指向getInt1里面的这个Value1的指针的时候 实际上,你打印出来的值是第2个函数, 里面value2的值,在大多数情况下得到的是这样的 一个值,也就是说,它已经被覆盖 掉了,当然,在这需要强调的是,就是你得到的是30,也不是一个稳定的值,因为 getInt2在被调用之后,也被释放掉了 对不对,所以说,在这个地方啊,打印出来的这个值仍然 是不能够确定的,仍然 是一个危险的值,那么通过这两个例子,我们就可以看得出来,当我们从一个函数里面返回一个 指针,并且呢,这个指针是指 向这个函数里面的一个临时变量 的时候,这个时候是非常 非常危险的,啊,实际上,你返回的这个值是一个指向已经被释放掉的内存空间 的一个值,啊,它是非常非常的不稳定 的,所以说啊,这就告诉我们,当我们要从函数里面返回一个指针的时候 请你务必确保这个指针是有意义的,也就是说,它必须指向 一片没有被释放掉的,有意义的,仍然活 着的内存空间,啊,比方说,这个例子,我们再来看 这个例子,那么这个例子呢,就不同了 我们来分析一下,仍然呢,是定义了两个函数,一个叫getInt1, getInt2,它们都会返回两个指针,那么getInt1呢 返回的是Value1的地址,getInt2呢,返回的是 value2的地址,跟前面的程序所不同的是,在这个程序里头,value1和value2 是全局 变量,全局变量,也就是说,它们两个的 生存周期啊,是从这个变量的定义开 始,一直持续到整个文件的 结束,也就是说,value1和value2的作用范围, 是从它们定义开始一直持续到整个 文件的结束,也就是说,就算,当getInt1和getInt2被 调用结束之后,value1和value2的存储空间仍然不会 被释放 掉,所以在调用完getInt1和getInt2之后,我们再 去打印p里面所指向的那片内存空间的时候,p所指向的内存空间有没有意义啊? 有意义,因为p指向value1,而value1呢,是一个 全局变量,它的作用范围是从它的定义开始,一直到整个 文件的结束,所以说,当我们在这里再去打印*p的时候,它仍然是有 意义的,啊,所以说,在这个程序里头 啊,getInt1的这个做法,返回的value1的这个 值,是正确的,所以程序的运行结果呢,也是, 稳定的,那么在这种情况下,我们就可以说,getint1 和getint2返回的都是处于生命周期中的 变量的地址,啊,而不是一个死的地址 不是一个被释放掉的地址,那,可能有的同学可能问了 那,那,为了确保我返回的是一个有意义的地址 是不是,我必须得使用全局变量啊,那你老师曾,以前 可是讲过,使用全局变量要非常非常的慎重, 啊,因为,全局变量会破坏程序的结构性, 那有没有什么其他的办法,可以让我返回一个有意义的 值呢?有,那么除了全局变量之外,你还可以在函数里面 定义一些静态局部 变量,比方说,在刚才的这个程序里头,那么如果我们不把value1 和value2定义成全局变量,但是呢,我们又想确保 它们返回的value1和value2的这个值是有意义的, 是没有被 释放掉的,那么我们就可以把value1和value2定义成static 啊,也就是静态局部变量,什么叫静态局部变量 啊,ok,接下来呢,我们就来了解一下什么是静态局部变量。