(新版)SJTU-OJ-1038.小可怜的指针题

题目描述

助教是个小可怜,因为她不知道要出什么机考题了。 然后她发现大家已经学完了指针,欣喜若狂。但是她要跑实验,不是很想出题。于是她偷了一道憨憨助教出的指针题,改一改准备拿来出题。

写出以下代码中的两个函数 f u n A funA funA f u n B funB funB 的声明与定义,使得 f u n A ( p ) = a funA(p)=a funA(p)=a 能实现通过指针 p p p访问用户输入的数组 a a a f u n B funB funB 能将 p , q p,q p,q 指向的两个数组的对应位相乘,并存储到数组 c c c 的对应位中。

#include <iostream>
#include <cstring>
using namespace std;

int n;
// 写出两个函数的声明

int main() {
    int a[100], b[100], c[100];
    cin >> n;
    for (int i = 0;i < n; ++i)
        cin >> a[i];
    for (int i = 0;i < n; ++i)
        cin >> b[i];
    int **p,**q;
    funA(p) = a;
    funA(q) = b;
    funB(c,p,q);
    for (int i = 0; i < n; ++i)
        cout << c[i] << " ";
    return 0;
}

// 写出两个函数的定义

要求如下:

除了实现 f u n A funA funA f u n B funB funB外,不能修改包括 m a i n main main 函数在内的其他代码,不能在全局定义新的变量、指针、数组和函数等,不能调用其他的库。

f u n A funA funA f u n B funB funB 内不能调用任何输入输出函数(包含但不限于cin,cout,scanf,prinf,getchar,putchar等等)。

不允许存在内存泄漏

用户输入的单个数组长度不会超过98,而且 − 1000 ≤ a i , b i ≤ 1000 -1000 \leq a_i, b_i \leq 1000 1000ai,bi1000.

输入格式

(无)

输出格式

(无)

样例输入

8
5 4 7 4 9 1 1 0
1 -9 2 -6 0 -8 1 -7

样例输出

5 -36 14 -24 0 -8 1 0 

数据范围

(无)

题目吐槽

       首先看到这个题目,是不是有一种冲动,把 c[i] 改成 a[i] * b[i],然后直接完事了。事实证明确实可以通过AC,但是显然题目不想让我们这样去做,我们要通过函数,返回一个引用类型。
       什么?不会?给我回去看课本!171面!球球你们好好看看课本,这个真的课本上有。什么?老师没讲?那没事了,自学去23333(doge)如果不想看书就好好看这篇文章也可以。(本段内容全部划掉)
       看了一下机考情况,这道题目考试的时候只有一个人AC了,另外还有四只小盆友尝试了一次,然后就没有然后了。看来这个题目对于初学者确实很烦。函数在等号的左边确实很少见,而且这个知识点也很偏僻,初学基本不怎么用到。加上这个题还融合了二级指针一起考,难度更大了。
       这是什么助教?到底是谁可怜 2333。卖菜什么的最讨厌了(不是)。在正式开始之前我们先复习一下指针所有的知识,毕竟指针也是很重要的一个知识点,也是难点。

  • 指针的定义、访问、赋值(一级指针)[这个还不会真的不用做这个题目了2333 快回去看书]
  • 常量指针、指针常量、指向常量的指针常量 [课本 P155面]
  • 动态变量、动态变量的创建与回收、动态数组、动态数组的创建与回收
  • 引用类型

指针

       不妨我们用几个简单的问题复习指针。

  • 问题一:下列关于定义指针说法正确的有哪些?
  • int * p1, p2 定义的是两个指针,因为 int * 属于一个类型名。
  • int a; int *p1 = &a;是正确的指针赋值语句。
  • int a; int *p1; p1 = &a;是正确的指针赋值语句。
  • int a; int *p1; *p1 = &a;是正确的指针赋值语句。
  • 解答:选项二、选项三正确。int * p1, p2 定义的是一个指针, int * 也不属于一个类型名,星号属于变量名。选项二、选项三语句是等价的,*p1经过解引用,等式变成了 a = &a; 显然不对。
  • 问题二:常量指针,指针常量,指向常量的指针常量的定义方法与区别?
  • 解答:课本P155

题目解答

       在正式解答这个题目之前,我们首先看看课本的案例,代码清单的7-9给出了一个返回引用值的案例。下面这个案例来自课本。之前没好好看的可以现场看看。

//文件名:7-9.cpp
//返回引用值的函数示例
#include <iostream>
using namespace std;
int a[]={1, 3, 5, 7, 9};
int &index(int); //声明返回引用的函数

void main()
{   
	index(2) = 25; //将a[2]重新赋值为25
    cout << index(2);
}

int &index(int j)
{   return a[j]; }

       那么问题来了,既然是等号左边是函数,上面这个案例函数index(2) 返回的是什么?是a[2],有人就要问了,a[2] = 5 呀,为啥返回的不是5呢,这就是 int &index(int j) 函数与 int index(int j) 的差别了,前者返回的是一个变量名,可以在等式的坐标,后者只能返回一个数字。明白了这一部分,你应该明白 & 的精妙了。
       继续回到我们的试题,funA(p) = a;,我们还是看看这个等式:等式的左边是什么?是一个函数,等式的右边是什么?是一个 a 我们知道,数组名实际上就是一个指针,a也就是一个一级指针,也就是说,这个函数 funA(p) 的返回值是一个一级指针,函数是传入值是一个二级指针。[这是谁出的这个题目出来挨打!不是] 也就是说我们要实现一个函数,传入的二级指针,返回的是一个一级指针!而且返回的这个一级指针还可以通过后面的 funB(c,p,q); ,获取一级指针 a b的内容。
       完了绕晕了怎么办。简单点说,我们首先要明白这个函数目的是啥,传入的是啥类型,传出的是什么,仅此而已够了!
       那么,好啦,我们可以把函数大概写出来。

int *&funA(int **a)
{
	// 写内容
}

       上面的代码哪里错了?hhh如果你学的比较扎实的话一眼就可以看出来,传入的设置有问题,这个函数中 int **a 是一个形参!形参在函数运行完了就没有然后了!变量销毁,失效了,很明显我们这个题目要的就是要操作二级指针 **p ,你需要对传入的这个指针 **p 操作,结果上面那个函数你弄一个形参操作来操纵去有什么意义呢?
       如果没有懂还是请看课本,171面,有这样的一段话:注意,在定义返回引用的函数时,不能返回该函数的局部变量,因为局部变量的生存期仅限于函数内部,当函数返回时,局部变量就消失了。此时引用该变量就会引起一个无效的引用,导致程序运行时出错。返回引用的函数中,返回的值也不能是一个表达式,因为表达式不是左值而是一个临时值。 什么?不会左值?回去看课本 P172!doge
       请认真阅读上面的这一段话。你不能返回局部变量,所以你更不可以操作一个形参!那不是开玩笑?既然我们要操作的不可以是新参,在这个函数里面我们要操作的是真实的二级指针p,怎么办?如果不会那我们再看一个例子。
       案例: 下面哪一个交换 main 函数中的int变量 xy的函数是正确的?

	void swap(int a, int b)
	{
		int temp = a;
		a = b;
		b = temp;
	}
	// 主函数中这样调用 swap(&x,&y);
	void swap(int* a,int* b)
	{
		int temp = *a;
		*a = *b;
		*b = temp;
	}

       当然是第二个。那么我们在看下面的代码:

	// 主函数中这样调用 swap(x,y);
	void swap(int &a,int &b)
	{
		int temp = a;
		a = b;
		b = temp;
	}

       这就是引用,函数调用的时候,int &a = x; int &b = y;,说白了就是给变量 x,y起啦一个新的名字 a,b;然后你可以操作a,b,这与你操作 x y是等价的。这就是引用!请看课本P169。我们再次回到这题目来,所以函数应该像下面这么写:(补充 int 与 *写在一起,不要问为什么,就是这样,C++习惯,这并不意味着int * 是一个类型!只是为了方便,一眼看出这个一个一级指针,指向 int 数据类型的一级指针)

	int * &funA(int ** &a)
	{
		// 写内容
	}

       然后,请你默写出二级指针的赋值方法。什么?又不会?请看课本的P175面。好啦明白了,二级指针的赋值距离如下:

	int x = 10;
	int *p;
	int **q;
	p = &x;		// 一级指针等于指向的变量取址
	q = &p;		// 二级指针等于一级指针取址

       于是就有人会问,那我能不能这样写 f u n A funA funA

	int * &funA(int ** &a)
	{
		int *m;			// 定义一个一级指针
		a = &m;			// 赋值二级指针
		return *a		// 返回这个一级指针
	}

       回答是:错误,完全错误! 为什么?理由是你定义了一个一级指针,其值是随机的,可能指向某一个地址,或者说是一个野指针(野指针就是说指针指向的地址是未经过申请的,通过野指针访问的地址的非法的,我们会拒绝这种非法的访问。)所以,你定义的这个一级指针没人知道指向什么地方,也不是通过合法的途径申请的地址,所以这个地址就是非法的,非法的就不能访问,访问就会报错!
       所以我们还是要做合法公民,按照合法的途径申请一个地址。合法的方法当然是 new,当然,你 new 了就一定要 delete

	int * &funA(int ** &a)
	{
		a = new int *		// 赋值二级指针
		return *a			// 返回这个一级指针
	}

       补充说明:*和乘号还是要区分一下,通过括号解决问题。

void funB(int *c,int **p,int **q)
{
    for(int i = 0 ; i< n ; i++)
    {
        c[i] = (*p)[i]*(*q)[i];
    }
    delete p;
    delete q;
}

       完整 AC 代码!规范不泄露内存的哦!

#include <iostream>
#include <cstring>
using namespace std;

int n;
// 写出两个函数的声明
int *&funA(int **&p);
void funB(int *c, int **x, int **y);

int main() {
    int a[100], b[100], c[100];
    cin >> n;
    for (int i = 0;i < n; ++i)
        cin >> a[i];
    for (int i = 0;i < n; ++i)
        cin >> b[i];
    int **p,**q;

    funA(p) = a;
    funA(q) = b;
    funB(c,p,q);
    for (int i = 0; i < n; ++i)
        cout << c[i] << " ";
    system("pause");
    return 0;
}

// 写出两个函数的定义

int* &funA(int **&z)
{
    z = new int*;
    return *z;
}
void funB(int *c,int **p,int **q)
{
    for(int i = 0 ; i< n ; i++)
    {
        c[i] = (*p)[i]*(*q)[i];
    }
    delete p;
    delete q;
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值