技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 887|回复: 6
收起左侧

【C语言】函数的玄机

[复制链接]

85

主题

263

帖子

3536

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1135 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
发表于 2016-7-6 03:31:49 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
This is an (most of Chinese Cpl teacher) untold story of the function in plain C.
C语言的函数,我们并不陌生。
要学习生物体,就得先学习生命最基础的表现形式——细胞。
同样,因为C是面向过程的,函数就是程序的细胞。我们学习C语言,总错过不了她。
细胞有细胞膜、细胞壁opt、细胞质、细胞器、细胞核。
那么,函数有啥内容值得学习?
首先我们要明确函数书写的两大基本概念:函数的声明(declaration)和函数的定义(definition)
函数的声明确定了函数的形态,特征。这个形态特征决定函数名称(一个合法的C标识符),和函数参数列表。
我们声明一个函数:
int add(a, b); // 声明后面必加分号!
另一种声明方式,在这种声明方式中,我们明确了参数列表内各参数的类型:
int add(int a, int b); // 注意要加分号!
我们将上面那个函数声明成外部的:
extern int add(int a, int b); // 这里也要加分号!
再来看函数的定义:函数的声明确定了函数执行什么样的运算过程。
我们定义那个声明好的add函数:
int add(int a, int b)
{
        return a + b;
}
同时,函数的定义也可以与函数的声明写在一起:
extern int add(a, b) // 还没明确说明参数a,b的类型呢!
int a; // 此时我们也可以事后加以说明。
int b; // 要有分号哦!
{
        return a + b;
}
那么,大家有没有被我一套大道理、官话讲晕掉了呢?
好了!In summarize:
如果函数在定义处之前被调用(Invoke)那么,这个函数需要在调用处(invocation)之前被声明。
换句话讲,我让张三做一件叫做add的事情(这是声明)。
到底张三怎样做add这件事呢?我告诉他,把我给你的a加上我给你的b,然后还给我(这些是定义)。

如果我先说:张三哪,你去做add(a, b);
等到张三拿到我给他的a b(实际参数):x = add (1, 2); 时候就问了:领导,add这件事,我怎么去实施啊?
当然我是一个负责任的领导,会这样说:笨呐!自己动动脑子啊!养你这么多年,钞票都喂猪啦?(如果我真的这样说,编译器会给我使脸色:找不到函数add的参照。)
我会耐心说道:
int add(int a, int b) // add这件事呢,你首先拿好我给你的俩宝贝,最后你要还我一个int类型的宝贝。
{
        return a + b; // 具体来讲,你要把宝贝a和宝贝b加起来,还给我,就是这样啦。
}
这种情况的代码如下(完整的可执行的):
[C] 纯文本查看 复制代码
#include <stdio.h>
int add(int, int);
int main()
{
    printf("%d\n", add(1,1));
    return 0;
}
int add(int a, int b)
{
    return a + b;
}


换一种情况:我一口气说:
张三你去做add这件事,等你拿到俩宝贝,最后你要还我一个int型的宝贝,你需要把宝贝a和宝贝b加起来还给我,do you get it?
这种情况就是声明定义在一起,那么写成如下代码(完整的可执行的):
[C] 纯文本查看 复制代码
#include <stdio.h>
add(a, b) // 没有声明返回类型,默认int类型!
int a;
int b;
{
    return a + b;
}

main()
{
    printf("%d\n", add(1, 2));
 4  return 0;
}


最后要注意:正确的做法是在声明的时候加上inline(内联)和extern(外部)或者static(静态)关键字,而定义的时候不加inline和extern或者static关键字。

接下来,我们再谈一个比较高大上的内容,
那就是,不定参数函数(Variadic functions)。
典型的不定参数函数就是众所周知的printf函数和scanf函数。
我们来看看printf怎么使用的:int a = 1, b = 2, c = 3; printf("%d,%d,%d", a, b, c);
再来看看printf怎么声明的:printf(const char * restrict, ...);
printf函数的第一个参数restrict实际是一个参数检查列表,这个参数指向的字符串内让打印多少个int就得打印多少个int。
在实际的程序编写过程中,你想让她打印几个int就写几个%d,写1000个%d都行,printf就傻傻地打印1000个int。
这是一项超级棒的语法啊!我认为其好玩性仅次于vb的可选函数参数和函数参数默认值语法:
Declare Function Bar(a As Integer, Optional b As Integer = 1) As Integer
想象一下我们如何利用它?
对了,做一个求和函数:
大概是这样:sum(1,2,3)得出6; sum(1,2,3,4,5)得出15.
代码如下:
[C] 纯文本查看 复制代码
#include <stdio.h>
#include <stdarg.h>     // 包含允许使用不定参函数的头文件。
int sum(int count, ...) // 参数count代表要求几个数的和。不确定的参数列表使用 “...” 代替。
{
	va_list var; // 使用va_list宏定义一个变化参数
	int s = 0;   // 定义s存放累加的结果。
	va_start(var, count); // 用va_start宏将检查列表装入var,表示要检查一共count个参数。
		while (count)
		{
			s += va_arg(var, int); // 每次使用va_arg宏将下一个参数取出,转换成为int类型,并且累加。
			count--;
		}
	va_end(var); // 加上va_end宏表示一直要累加到最后一个。
	return s;
}
int main()
{
	// 计算1加到10的和减去1加到5的和他们俩的差。
	printf("%d\n", sum(10 ,1,2,3,4,5,6,7,8,9,10) - sum(5 ,1,2,3,4,5));
	return 0;
}

关于Variadic functions的更详细信息,这里有俩个外部链接可供大家参考:
1.wiki上的关于stdarg.h头文件的说明:https://en.wikipedia.org/wiki/Stdarg.h
2.本站的ISO C Standard白皮书(E文原版):https://www.0xaa55.com/forum.php?mod=viewthread&tid=1759

本帖被以下淘专辑推荐:

In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

34

主题

179

帖子

1702

积分

用户组: 管理员

UID
8
精华
1
威望
14 点
宅币
1475 个
贡献
15 次
宅之契约
0 份
在线时间
237 小时
注册时间
2014-1-27
发表于 2016-7-6 11:32:15 | 显示全部楼层

      
期待了这么久的C文章,这次文章读来不那么过瘾。         
             其实我要求也不高,建议用汉字的时候,不要夹带英文,显得不伦不类,英语差看了半天都不知道单词意思。         
             在c里面是函数,在vb里面是过程。标准库里面没有现成的过程,就需要我们自己定义函数,它有自己的结构(和细胞一样),         
             使用也很简单,文章已说明。变参听说过,唯独又说得太少!

        

85

主题

263

帖子

3536

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1135 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
 楼主| 发表于 2016-7-6 12:50:51 | 显示全部楼层


好!接受建议,下次加以修正。
英文的话,我认为提一下declaration,和definition比较重要。
因为毕竟还是建议去查看英文原版资料。
有些中文还是不能完全翻译到位。
比如Variadic functions这个我也不知官方标准翻译是啥。很多中文教科书都没有。

另外一个例子:argument parameter attribute这三个没有固定意思,都可以解释为“参数”,但是要结合上下文,这就很头疼了。
stack overflow翻译为“栈溢出”,但是如果人家还提到stack underflow
overflow就得翻译为“上溢”,underflow翻译为“下溢”。瞬间成“矢量”了。
In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

1007

主题

2232

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
199
威望
263 点
宅币
16684 个
贡献
33359 次
宅之契约
0 份
在线时间
1595 小时
注册时间
2014-1-26
发表于 2016-7-7 16:49:13 | 显示全部楼层
标题不是全角的方括号看得我好不爽

85

主题

263

帖子

3536

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1135 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
 楼主| 发表于 2016-7-7 23:41:40 | 显示全部楼层
0xAA55 发表于 2016-7-7 16:49
标题不是全角的方括号看得我好不爽

已经修改,话说osx死活打不出“【】”
In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

1007

主题

2232

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
199
威望
263 点
宅币
16684 个
贡献
33359 次
宅之契约
0 份
在线时间
1595 小时
注册时间
2014-1-26
发表于 2016-7-9 00:34:11 | 显示全部楼层

1、C里面的函数,有时候也可以说成是“过程”,比如没有返回值的函数(比如void xxx();)
2、VB里的过程一般是没有返回值的,比如Sub xxx()而VB里也有函数,那些有返回值,并且返回值很有用的就是函数,比如Sqr()
3、我也想吐槽中英文混合。。那些词其实我也不知道意思,以我的风格我可能会写个中日文混合的帖子呢~~

0

主题

41

帖子

45

积分

用户组: 初·技术宅

UID
3351
精华
0
威望
2 点
宅币
0 个
贡献
0 次
宅之契约
0 份
在线时间
0 小时
注册时间
2018-1-14
发表于 2018-1-14 14:44:58 | 显示全部楼层
可以可以!!
回复

使用道具 举报

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-11-20 13:42 , Processed in 0.085174 second(s), 15 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表