函数是我们可以将代码结构化为子程序的方式,我们可以。
- 给予一个名称
- 当我们需要它们时调用
从你的第一个程序开始,一个 "Hello, World!",你就立即使用了C语言的函数。
#include <stdio.h>
int main(void) {
printf("Hello, World!");
}
main() 函数是一个非常重要的函数,因为它是一个C程序的入口。
下面是另一个函数。
void doSomething(int value) {
printf("%u", value);
}
函数有4个重要方面。
- 它们有一个名字,所以我们可以在以后调用("调用")它们
- 它们指定一个返回值
- 它们可以有参数
- 它们有一个主体,用大括号包裹着
函数主体是我们在调用一个函数时执行的一组指令。
如果函数没有返回值,你可以在函数名称前使用关键字void 。否则,你要指定函数的返回值类型(int 为整数,float 为浮点值,const char * 为字符串,等等)。
你不能从一个函数中返回一个以上的值。
一个函数可以有参数。它们是可选的。如果它没有参数,我们在括号内插入void ,像这样。
void doSomething(void) {
/* ... */
}
在这种情况下,当我们调用该函数时,我们会在括号内没有任何东西的情况下调用它。
如果我们有一个参数,我们指定参数的类型和名称,像这样。
void doSomething(int value) {
/* ... */
}
当我们调用该函数时,我们将在括号中传递该参数,像这样。
我们可以有多个参数,如果有的话,我们用逗号把它们分开,在声明和调用中都是如此。
void doSomething(int value1, int value2) {
/* ... */
}
doSomething(3, 4);
参数是通过复制来传递的。这意味着如果你修改value1 ,它的值会在本地被修改,而在函数之外,即在调用中传递的地方,它的值不会改变。
如果你传递一个指针作为参数,你可以修改这个变量的值,因为你现在可以使用它的内存地址直接访问它。
你不能为一个参数定义一个默认值。C++可以做到这一点(所以Arduino语言程序可以),但C语言不能。
请确保你在调用函数之前定义了它,否则编译器会发出警告和错误。
➜ ~ gcc hello.c -o hello; ./hello
hello.c:13:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
hello.c:17:6: error: conflicting types for
'doSomething'
void doSomething(int value1, char value2) {
^
hello.c:13:3: note: previous implicit declaration
is here
doSomething(3, 4);
^
1 warning and 1 error generated.
你得到的警告是关于排序的,这一点我已经提到了。
错误是关于另一件事,与此有关。由于C语言在调用前没有 "看到 "函数声明,它必须做出假设。它假定该函数返回int 。然而,该函数返回void ,因此出现了错误。
如果你把函数定义改为:
int doSomething(int value1, int value2) {
printf("%d %d\n", value1, value2);
return 1;
}
你就会得到警告,而不是错误。
➜ ~ gcc hello.c -o hello; ./hello
hello.c:14:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
1 warning generated.
在任何情况下,确保你在使用该函数之前声明该函数。要么把函数移上去,要么在头文件中添加函数原型。
在一个函数内部,你可以声明变量。
void doSomething(int value) {
int doubleValue = value * 2;
}
变量是在调用函数时创建的,并在函数结束时被销毁,而且从外部不可见。
在一个函数内部,你可以调用函数本身。这被称为递归,它提供了一些特殊的机会。