交大C语言程设课的题目,让我意识到了自己为什么只读了大专

今天,鼠鼠看到了朋友圈里的一个交大✌在讨论一道C语言程序设计的代码题。不知道天高地厚的鼠鼠就附庸风雅地去讨论了一下,这才发现自己为什么只能读大专!

题目看起来非常简单,鼠鼠好像也会,我在这里简单地阐述一下:

把下面的代码补充完整,包括在main函数之前添加2行代码以及在最后添加一个函数的定义,使得该程序能在一行里输出三个数:输出的第一个数是x(即用户输入的第一个数)减去y(即用户输入的第二个数)的差的个位数,第二个数是x的3倍,第三个数是y的2倍。

鼠鼠寻思,这好像就是实现三个简单到不能再简单的功能:

第一,两个整数x和y相减;第二,把相减的结果的个位数取出来;第三,按照题意更新x和y。但一看要补充的代码,鼠鼠才发现原来自己只是一只会耍杂技的小丑!读大专的我只是想着如何机械地去实现题目的各个要求,全然不知这道题背后能隐藏的逻辑游戏的极大魅力。

大家请看这段代码:

#include <iostream>
using namespace std;

//请在下面补充两行代码

//
int myfun(int* a,int* b);

int main (void)
{
	int x,y;
	cin>>x>>y;
	cout<<myfun(x,y);
	cout<<x<<' '<<y;
} 

int myfun(int* a,int* b)
{
	int c=*a-*b;
	*a *=2;
	*b *=3;
	return c;
}

//请在下面补充函数的定义

鼠鼠直接被难住了。读大专的鼠鼠只学过一点点c语言,不太看得会这些C++代码。好在鼠鼠翻过几页《C++ Primer Plus》,懂一点c++的皮毛。但即使这样,鼠鼠还是不太懂这道题该怎么写。

小丑啊小丑,到最后才发现鼠鼠就是一个在大专耍杂技的小丑,怎么能忝列交大✌之中讨论这种高智商者的思维游戏呢?

好在最后交大✌大发慈悲,把答案发给了我,让鼠鼠见识到了一点点小小的交大震撼。大专的鼠鼠,真是,开了眼了!

#include <iostream>
using namespace std;
//
char* myfun(int& a,int &b);
char s[3]="  ";
//
int myfun(int* a,int* b);

int main (void)
{
	int x,y;
	cin>>x>>y;
	cout<<myfun(x,y);
	cout<<x<<' '<<y;
} 

int myfun(int* a,int* b)
{
	int c=*a-*b;
	*a *=2;
	*b *=3;
	return c;
}
//
char *myfun(int &a,int &b)
{
	s[0]='0'-myfun(&b,&a)%10;
	return s;
}
//

—————————————————分割线

嘿嘿,上面只是鼠鼠的自嘲。上海交通大学是很好的学校,北航虽然比不上交大,但也是一所挺好的大学。鼠鼠还在这里学到了很多东西的,请大家图一乐。

下面我将介绍一下这段代码中所运用的一些C语言学习者不常用的C++性质,同时也会解释一些比较基础的语法。

一、函数的传参

在C语言中,函数的传参是以传值的形式进行的,这就意味着,main函数里传入myfunction函数的a和myfunction函数里进行运算的a不是一个变量虽然名称相同,但它们是储存在不同的地址里的!这么说,其实很模糊,下面我将举例子具体阐述:

我要在如下代码段中实现一个函数double_it,目的是把括号里的两个数字,也就是这个函数的两个参数分别乘2。

#include <stdio.h>

int main(void)
{
    int a,b;
    a=1;
    b=2;
    double_it(a,b);
    printf("%d %d",a,b);
    //或者,按照C++的习惯,写std::cout<<a<<b,
    //不过这样,要改一下头文件
    return 0;
}

​

那么我可以用两种方式向函数里传参

第一种:

#include <stdio.h>

void double_it(int a,int b);

int main(void)
{
    int a,b;
    a=1;
    b=2;
    double_it(a,b);
    printf("%d %d",a,b);
    return 0;
}

void double_it(int a,int b)
{
    a=a*2; 
    b=b*2;
    return;
}

传入函数的参数就是两个数a,b本身。在这种情况下,你会发现,打印出来的结果还是1和2,完全没有实现加倍的目的!

这是因为,函数的传参是以传值的方式实现的,你向函数double_it中传入了a和b,不是真的让a和b被函数改变,而是让把a和b的值复制给了函数中新开辟的两个int型变量。这样,改变函数里的a和b,当然不能改变函数外的a和b!

第二种:

#include <stdio.h>

void double_it(int* a,int* b);

int main(void)
{
    int a,b;
    a=1;
    b=2;
    double_it(&a,&b);
    printf("%d %d",a,b);
    return 0;
}

void double_it(int* a,int* b)
{
    *a=*a*2; 
    *b=*b*2;
    return;
}

使用传地址的方式,传入函数的参数是a和b的两个地址。传入函数后,即使这两个地址被复制进函数内的新变量的,但它们仍然指向a和b这两个变量。函数可以通过地址访问的方式修改a和b的值。

在这种情况下,使用函数时,要将a和b取地址,方能正确传入参数

double_it(&a,&b);

相应的,函数的参数应该定义为

void double_it(int* a,int* b)

因为传入的是int的地址,int地址的类型是int*,也就是指向int的指针!

还有,函数种使用a和b需要解引用

*a=*a*2; 
*b=*b*2;

编译运行这段代码你会发现,结果变成了2和4,符合要求了!

二、函数传参时直接引用

答案中定义的函数char *myfun(int &a,int &b)出现了奇怪的int &a和int &b。这不是别的,就是把之前所说的C语言传参方式颠覆掉的方式!

如果你这样设置,你把a和b传入myfun函数,a和b就能直接被myfun改变了!

还是用之前的例子,为了让a和b翻倍,我们还能这样改代码:

#include <stdio.h>

void double_it(int &a,int &b);

int main(void)
{
    int a,b;
    a=1;
    b=2;
    double_it(a,b);
    printf("%d %d",a,b);
    return 0;
}

void double_it(int &a,int &b)
{
    a=a*2; 
    b=b*2;
    return;
}

这段代码编译运行,你会发现,结果变成了我们期待的2和4!

三、函数名的重载(令人窒息的操作)

下面是交大代码题的一个片段:

int main (void)
{
	int x,y;
	cin>>x>>y;
	cout<<myfun(x,y);
	cout<<x<<' '<<y;
} 

这里出题者竟然使用了myfun(x,y),而由它题目中int myfun(int* a,int* b);的定义,传入myfun函数的参数应该是int*类型的数据,而不是int类型的数据。x和y是int类型的数据,这里好像错了!

但这就是这道题目诡谲的地方,它就是想让你再写一个名称为myfun的函数。让你实现它的功能!

但,两个同样名称的函数,不会矛盾吗?

这也是最惊讶我的地方,但转念一想,只要这两个函数的返回值和传入参数都不一样,是不是可以同时存在了呢?

编译器告诉我的答案,是,是的!就是可以同时存在的,非常神奇!只要用传入参数和返回值的差异来区分就行。

四、其他

至于这段代码的逻辑,我就不赘述了。它不是一个正常的编码者会使用的。更多的是出题者的一种巧妙设计。这种设计存在的意义是未定义的。

—————————————————分割线

但是,我还是忍不住要吐槽这一段破代码。不管时为了做实用性的代码开发,还是写竞赛性的代码。这种逻辑,这种代码都是一种故弄玄虚的矫揉造作!

可能它能锻炼同学们的智力,拓展同学们的认知。但就其本身而言,这就是一段及其糟糕的代码!

一、逻辑混乱

函数被设计出来的意义是把一系列复杂的功能和逻辑简化,达到逐个击破的效果;或者说它可以把一些常用的功能封装起来。

这段代码用函数,则是把一段完整清晰的逻辑人为地,随意地割裂成一个个小片段,让解题者去拼凑。

你看,你作差就作差罢了,为什么要在作差之后乘2和乘3,把它们写到主函数或者别的函数里不香吗?而且它的逻辑又非常奇怪,让非常正常的取个位算法由s[0]='0'+myfun(&b,&a)%10;变成了s[0]='0'-myfun(&b,&a)%10; 怪哉怪哉!

二、使用语法混乱

这其实是一个C语言程序设计课的代码,为什么要用cin>>x>>y这样的输入方法和int &a,int &b这样的C++性质。我不理解。

照道理,用C++性质应该一以贯之,但字符串又用了s[0]这样的c语言风格字符串,不应该用String吗?

如果你当作C语言,那这个文件无法以.c后缀文件被编译;如果你当作C++文件,那应该来体现一下面向对象的性质啊!

不理解,不过这可能是个人风格,我觉得它也不置可否!

三、训练的意义?

我们学习C,C++,是为了掌握编写实用程序的方法。但我感觉这道题目貌似流于炫技,没有太大的实际意义。有种数学考试对数学知识本身异化的感觉。学会C,不是为了去写程序解决学习和生活种的现实问题,而是为了以它为媒介玩智力游戏,把它变成区分智商的手段,这就有些令人生厌了吧。

也许,在AI技术和新语言日新月异的今天,C和C++这种古老的语言,真的会沦为一种滋生题目的媒介,失去其本身的固有价值,像儒学那样成为了科举考试的附庸。这对我们这些C语言的殉道者无疑是肝肠寸断之痛。

但不管怎么说,历史的潮流是难以改变的。如果C语言真的死了,就让它以这种形式继续它的生命吧!

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值