【C语言】函数的玄机
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加起来,还给我,就是这样啦。
}
这种情况的代码如下(完整的可执行的):
#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?
这种情况就是声明定义在一起,那么写成如下代码(完整的可执行的):
#include <stdio.h>
add(a, b) // 没有声明返回类型,默认int类型!
int a;
int b;
{
return a + b;
}
main()
{
printf("%d\n", add(1, 2));
4return 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.
代码如下:
#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
期待了这么久的C文章,这次文章读来不那么过瘾。
其实我要求也不高,建议用汉字的时候,不要夹带英文,显得不伦不类,英语差看了半天都不知道单词意思。
在c里面是函数,在vb里面是过程。标准库里面没有现成的过程,就需要我们自己定义函数,它有自己的结构(和细胞一样),
使用也很简单,文章已说明。变参听说过,唯独又说得太少!
13*0217 发表于 2016-7-6 11:32
好!接受建议,下次加以修正。
英文的话,我认为提一下declaration,和definition比较重要。
因为毕竟还是建议去查看英文原版资料。
有些中文还是不能完全翻译到位。
比如Variadic functions这个我也不知官方标准翻译是啥。很多中文教科书都没有。
另外一个例子:argument parameter attribute这三个没有固定意思,都可以解释为“参数”,但是要结合上下文,这就很头疼了。
stack overflow翻译为“栈溢出”,但是如果人家还提到stack underflow
overflow就得翻译为“上溢”,underflow翻译为“下溢”。瞬间成“矢量”了。 标题不是全角的方括号看得我好不爽 0xAA55 发表于 2016-7-7 16:49
标题不是全角的方括号看得我好不爽
已经修改,话说osx死活打不出“【】” 13*0217 发表于 2016-7-6 11:32
1、C里面的函数,有时候也可以说成是“过程”,比如没有返回值的函数(比如void xxx();)
2、VB里的过程一般是没有返回值的,比如Sub xxx()而VB里也有函数,那些有返回值,并且返回值很有用的就是函数,比如Sqr()
3、我也想吐槽中英文混合。。那些词其实我也不知道意思,以我的风格我可能会写个中日文混合的帖子呢~~ 可以可以!!
页:
[1]