探讨C语言返回struct的实现

C语言基本类型的返回通过寄存器实现。

寄存器容量不大,一个结构体的大小不定,多塞几个成员,很容易就会超过寄存器的容量,不太可能通过寄存器一次性返回一个结构体。

把以下代码转为汇编,探个究竟:

优化后的代码可能比较晦涩,64位寄存器数量是32位的一倍,为了简便理解,关闭代码优化,并且转为32位汇编,命令:

结果:

汇编代码已写上注释。

在get_struct()函数中,调用fun()之前,会首先把get_struct()函数中要接收返回值的地址压入栈中。

在fun()中,要返回值时,直接从get_struct()的栈帧取得保存结构体值的地址,直接对get_struct()的栈帧进行操作。

总的来说,一个函数要返回一个结构体,则从调用者函数的栈帧中取得接收结构体的地址(被赋值结构体的指针),直接对调用者函数的栈帧进行修改。

关于Visual Studio C LANG下pow()函数的重载

pow

当初看到Visual Studio 2010的这个提示,我真当VS实现了C LANG的函数重载。

今天即兴尝试VS下C LANG的函数重载,虽然IDE没报语法错误,但编译器编译时会报错。无奈之下只好打开长长的math.h一探究竟。

通过全文搜索很容易找到math.h声明的pow()原型:

声明的这些原型一眼看上去没什么问题,挺正常的重载方式。不过math.h内容有点多,不细心点还是看不出来,实际上pow()的重载使用了条件编译:

查找了一下资料,得知”__cplusplus”是CPP下才有的预定义宏,毕竟”C Plus Plus”……

也就是说,编译C LANG的情况下,仅有这个原型起作用:

其他的重载用的原型根本就不会被编译。

 

这下结论明确,C LANG根本没有函数重载,那就更加别谈对pow()的重载了,只是IDE对条件编译的检测不完善,才有本文首图的那种提示。

最后自己弄一个伪函数重载玩玩:

 

为何C语言函数可以返回struct

struct可由多个数据类型构成,初步看起来应该类似数组,变量本身是一个指向首字节的指针,无法直接通过此指针赋值struct,在函数中声明为自动变量时,也无法返回此指针所指向的值。

但事实上就并非如此,struct之间可以直接赋值,使用指针指向其地址时需要使用”&”,函数中声明为自动变量时可以直接return其值。

那struct变量本身到底是何?

写了这样一个东西,运行后瞬间明白:

struct变量就和基本类型,int, char等一样,直接指向其值,而且struct有多少字节,那个值就有多少字节。

也就是说,我们声明一个struct类型时,就是的的确确等于声明了一个确定大小的类型,就和int, char等基本类型一致。

既然是指向值,有确定大小,而且不是地址,那么就可以解释为何可以直接赋值以及可以被函数return了。

 

返回数组:

 

C float类型精确到N位小数的值的比较

博客更换域名后的第一篇文章。

本周,程序设计基础老师布置了一个上机作业,其中一项是一次生成两个零到九的随机数与加减乘除中的一个运算符号,输出两个数的运算式子,让用户输入运算结果,再把用户输入的值与程序运算的结果比较,从而判断用户运算的结果是否正确。

看到这项作业,我担心遇到无理数的时候如何处理,不可能让人也算到那么多位小数吧……

奇葩的是,老师的给我们的演示截图中恰好没有出现除不尽的情况,不知道是不是特意的?不然我就可以看看老师是偷懒用了整数型还是有高明的算法用浮点型了。

task_serven

 

首先想到的是:以数学解决问题

根据多年的人工计算经验,一般都是保留两位小数,因此要求用户输入的数字精确到二位小数即可。

于是手工推导出了判断精确到二位小数后数值正确的条件(字写得不好看,就不拍图了):

-0.005<用户输入值-计算机计算的结果<=0.005

 

不过缺点也很明显的,你无法阻止用户输入有小于0.005的误差的值。

整数还好解决,强制转换为整数型再相减,结果为0即可判断出用户输入的值是否有误差。

如果是0.550这种,用户输入了0.5519999999999999999999999999……,根据上述推导的判断条件,成立,回答正确。

 

如何判断用户输入的小数是否多于二位?

于是换了个方向,如何处理计算机的计算结果为保留二位小数后的数值?因为这样的话,就不需要“-0.005<用户输入值-计算机计算的结果<=0.005”这个判断条件了。

如果要手工把浮点型转换为字符串,太麻烦了,假如尾数是1.111,那么总和是1+0.5+0.25+0.125,要实现小数加法……

还是放弃数学方法好了……

 

平时都用printf(),可以很方便的把各种类型数据格式化为字符串输出,且“.x”修饰符会自动为你四舍五入,既然如此,我可以把格式化输出和格式化输入工作交给C的stdio库了。

printf()是输出到标准输出的,不能用这个,平时用Linux,习惯了数据流重定向,于是想fprintf到stdin,然后scanf stdin,不过很可惜stdin是READ ONLY的。

还有sprintf()与sscanf(),这两个函数直接操作内存,输出和读入的都为字符类型。

 

到这里,方向明确了,声明一个变量,作为buffer,然后使用sprintf()格式化输出到buffer,再用sscanf()从buffer格式化读入。

 

最终的代码如下:

 

Float Compare

 

Task Serven Answer

 

这里还有一个更简单的方法: