C 语言函数(C language function)

函数声明

int plus_one(int n) {
  return n + 1;
}

函数声明的语法有以下几点,需要注意。

  • 返回值类型

函数声明时,首先需要给出返回值的类型,上例是,表示函数返回一个整数。

int
plus_one()
  • 参数

函数名后面的圆括号里面,需要声明参数的类型和参数名,表示这个函数有一个整数参数。

plus_one(int n)
n
  • 函数体

函数体要写在大括号里面,后面(即大括号外面)不需要加分号。大括号的起始位置,可以跟函数名在同一行,也可以另起一行。

  • return语句

语句给出函数的返回值,程序运行到这一行,就会跳出函数体,结束函数的调用。如果函数没有返回值,可以省略语句,或者写成。

return
return
return;

函数调用

调用函数时,在函数名后面加上圆括号就可以了,实际的参数放在圆括号里面。

int a = plus_one(13);
// a 等于 14

函数调用时,参数个数必须与定义里面的参数个数一致,参数过多或过少都会报错。

函数必须先声明后使用,否则会报错。C 语言标准规定,函数只能声明在源码文件的顶层,不能声明在其他函数内部。

不返回值的函数,使用关键字表示返回值的类型。没有参数的函数,声明时要用关键字表示参数类型。

void
void
void myFunc(void) {
  // ...
}

main() 方法

C 语言规定,是程序的入口函数。程序总是从这个函数开始执行,如果没有该函数,程序就无法启动。

main()
int main(void) {
  printf("Hello World\n");
  return 0;
}

C 语言约定,返回值表示函数运行成功,如果返回其他非零整数,就表示运行失败,代码出了问题。

0

如果里面省略这一行,编译器会自动加上,即的默认返回值为0。

main()
return 0
main()

参数的传值引用

如果函数的参数是一个变量,那么调用时,传入的是这个,而不是变量本身。

变量的值的拷贝
void increment(int a) {
  a++;
}

int i = 10;
increment(i);

printf("%d\n", i); // 10

如果想要传入变量本身,只能传入变量的地址。

void Swap(int* x, int* y) {
  int temp;
  temp = *x;
  *x = *y;
  *y = temp;
}

int a = 1;
int b = 2;
Swap(&a, &b);

函数不要返回内部变量的指针,因为当函数结束运行时,内部变量就消失了,这时指向内部变量的内存地址是无效的。

函数指针

函数本身就是一段内存里面的代码,C 语言允许通过指针获取函数。

void print(int a) {
  printf("%d\n", a);
}

void (*print_ptr)(int) = &print;

通过函数指针也可以调用函数。

(*print_ptr)(10);
// 等同于
print(10);

C 语言还规定,函数名本身就是指向函数代码的指针,通过函数名就能获取函数地址。

if (print == &print) // true

五种调用函数的写法。

// 写法一
print(10)

// 写法二
(*print)(10)

// 写法三
(&print)(10)

// 写法四
(*print_ptr)(10)

// 写法五
print_ptr(10)

函数原型

函数必须先声明,后使用,而函数是入口函数,因此,其他函数必须在函数之前声明,否则编译时会产生警告。

main()
main()

但是,是整个程序的入口,也是主要逻辑,放在最前面比较好。另一方面,对于函数较多的程序,保证每个函数的顺序正确,会变得很麻烦。

main()

C 语言提供的解决方法是,只要在程序开头处给出函数原型,函数就可以先使用、后声明。

所谓函数原型,就是提前告诉编译器,每个函数的返回类型和参数类型。

int twice(int);

int main(int num) {
  return twice(num);
}

int twice(int num) {
  return 2 * num;
}

exit()

函数用来终止整个程序的运行。一旦执行到该函数,程序就会立即结束。该函数的原型定义在头文件里面。

exit()
stdlib.h

可以向程序外部返回一个值,它的参数就是程序的返回值。

exit()

一般来说,使用两个常量作为它的参数:(相当于 0)表示程序运行成功,(相当于 1)表示程序异常中止。

EXIT_SUCCESS
EXIT_FAILURE

在函数里面,等价于使用语句。

main()
exit()
return

C 语言还提供了一个函数,用来登记执行时额外执行的函数,用来做一些退出程序时的收尾工作,该函数的原型也是定义在头文件。

atexit()
exit()
stdlib.h
int atexit(void (*func)(void));

的参数是一个函数指针。注意,它的参数函数不能接受参数,也不能有返回值。

atexit()

执行时会先自动调用注册的函数,然后再终止程序。

exit()
atexit()

函数说明符

extern 说明符

对于多文件的项目,源码文件会用到其他文件声明的函数。这时,当前文件里面需要给出外部函数的原型,并用说明该函数的定义来自其他文件。

extern
extern int foo(int arg1, char arg2);

int main(void) {
  int a = foo(2, 3);
  // ...
  return 0;
}

由于函数原型默认就是,所以可以不加。

extern
extern

static 说明符

用于函数内部声明变量时,表示该变量只需要初始化一次,不需要在每次调用时都进行初始化。也就是说,它的值在两次调用之间保持不变。

static
#include <stdio.h>

void counter(void) {
  static int count = 1;  // 只初始化一次
  printf("%d\n", count);
  count++;
}

int main(void) {
  counter();  // 1
  counter();  // 2
  counter();  // 3
}

注意,修饰的变量初始化时,只能赋值为常量,不能赋值为变量。

static
int i = 3;
static int j = i; // 错误

在块作用域中,声明的变量有默认值。

static
0
static int foo;
// 等同于
static int foo = 0;

可以用来修饰函数本身,表示该函数只能在当前文件里使用。

static
static int Twice(int num) {
  int result = num * 2;
  return(result);
}

也可以用在参数里面,修饰参数数组。

static
int sum_array(int a[static 3], int n) {
  // ...
}

上面示例中,对程序行为不会有任何影响,只是用来告诉编译器,该数组长度至少为3,某些情况下可以加快程序运行速度。

static

另外,需要注意的是,对于多维数组的参数,仅可用于第一维的说明。

static

const 说明符

函数参数里面的说明符,表示函数内部不得修改该参数变量。

const
void f(const int* p) {
  *p = 0; // 该行报错
}

上面这种写法,只限制修改所指向的值,而本身的地址是可以修改的。

p
p
void f(const int* p) {
  int x = 13;
  p = &x; // 允许修改
}

如果想限制修改 所指向的值,可以把放在前面。

p
const
p
void f(int* const p) {
  int x = 13;
  p = &x; // 该行报错
}

如果想同时限制修改和,需要使用两个。

p
*p
const
void f(const int* const p) {
  // ...
}

可变参数

有些函数的参数数量是不确定的,声明函数的时候,可以使用省略号表示可变数量的参数。

...
int printf(const char* format, ...);

符号必须放在参数序列的结尾,否则会报错。

...

头文件定义了一些宏,可以操作可变参数:

stdarg.h

(1):一个数据类型,用来定义一个可变参数对象。它必须在操作可变参数时,首先使用。

va_list

(2):一个函数,用来初始化可变参数对象。它接受两个参数,第一个参数是可变参数对象,第二个参数是原始函数里面,可变参数之前的那个参数,用来为可变参数定位。

va_start

(3):一个函数,用来取出当前那个可变参数,每次调用后,内部指针就会指向下一个可变参数。它接受两个参数,第一个是可变参数对象,第二个是当前可变参数的类型。

va_arg

(4):一个函数,用来清理可变参数对象。

va_end
double average(int i, ...) {
  double total = 0;
  va_list ap;
  va_start(ap, i);
  for (int j = 1; j <= i; ++j) {
    total += va_arg(ap, double);
  }
  va_end(ap);
  return total / i;
}

上面示例中,定义为可变参数对象,将参数后面的参数统一放入,用来从依次取出一个参数,并且指定该参数为 double 类型,用来清理可变参数对象。

va_list ap
ap
va_start(ap, i)
i
ap
va_arg(ap, double)
ap
va_end(ap)

参考: C 语言教程

参考: C 语言教程

————————

Function declaration

int plus_one(int n) {
  return n + 1;
}

The syntax of function declaration has the following points, which need to be paid attention to.

  • return type

When declaring a function, you first need to give the type of the return value. The above example shows that the function returns an integer.

int
plus_one()
  • parameter

In the parentheses after the function name, you need to declare the type and name of the parameter, indicating that this function has an integer parameter.

plus_one(int n)
n
  • Function body

The body of the function should be written inside the braces, and there is no need to add a semicolon after it (that is, outside the braces). The starting position of braces can be on the same line as the function name or on another line.

  • return语句

Statement gives the return value of the function. When the program runs to this line, it will jump out of the function body and end the function call. If the function does not return a value, you can omit the statement or write it as.

return
return
return;

function call

When calling a function, you can add parentheses after the function name, and the actual parameters are placed in parentheses.

int a = plus_one(13);
// a 等于 14

When calling a function, the number of parameters must be consistent with the number of parameters in the definition. If there are too many or too few parameters, an error will be reported.

The function must be declared before use, otherwise an error will be reported. The C language standard stipulates that functions can only be declared at the top level of the source code file, not inside other functions.

Functions that do not return values use keywords to indicate the type of return value. Functions without parameters should be declared with keywords to indicate the parameter type.

void
void
void myFunc(void) {
  // ...
}

Main() method

C language is the entry function of the program. The program always starts from this function. Without this function, the program cannot be started.

main()
int main(void) {
  printf("Hello World\n");
  return 0;
}

According to the C language convention, the return value means that the function runs successfully. If other non-zero integers are returned, it means that the operation fails, and there is a problem with the code.

0

If this line is omitted, the compiler will automatically add it, that is, the default return value of is 0.

main()
return 0
main()

Parameter value passing reference

If the parameter of a function is a variable, it will be passed in instead of the variable itself.

变量的值的拷贝
void increment(int a) {
  a++;
}

int i = 10;
increment(i);

printf("%d\n", i); // 10

If you want to pass in the variable itself, you can only pass in the address of the variable.

void Swap(int* x, int* y) {
  int temp;
  temp = *x;
  *x = *y;
  *y = temp;
}

int a = 1;
int b = 2;
Swap(&a, &b);

The function should not return a pointer to an internal variable, because when the function ends running, the internal variable disappears, and the memory address pointing to the internal variable is invalid.

Function pointer

A function itself is a piece of code in memory. C language allows you to obtain a function through a pointer.

void print(int a) {
  printf("%d\n", a);
}

void (*print_ptr)(int) = &print;

Functions can also be called through function pointers.

(*print_ptr)(10);
// 等同于
print(10);

C language also stipulates that the function name itself is a pointer to the function code, and the function address can be obtained through the function name.

if (print == &print) // true

There are five ways to write calling functions.

// 写法一
print(10)

// 写法二
(*print)(10)

// 写法三
(&print)(10)

// 写法四
(*print_ptr)(10)

// 写法五
print_ptr(10)

Function prototype

Functions must be declared before use, and functions are entry functions. Therefore, other functions must be declared before functions, otherwise a warning will be generated during compilation.

main()
main()

However, it is the entry of the whole program and the main logic. It is better to put it at the top. On the other hand, for programs with many functions, it will become troublesome to ensure the correct order of each function.

main()

The solution provided by C language is that as long as the function prototype is given at the beginning of the program, the function can be used first and then declared.

The so-called function prototype is to tell the compiler in advance the return type and parameter type of each function.

int twice(int);

int main(int num) {
  return twice(num);
}

int twice(int num) {
  return 2 * num;
}

exit()

Function is used to terminate the entire program. Once the function is executed, the program ends immediately. The prototype of this function is defined in the header file.

exit()
stdlib.h

You can return a value to the outside of the program, and its parameter is the return value of the program.

exit()

Generally speaking, two constants are used as its parameters: (equivalent to 0) indicates that the program runs successfully, and (equivalent to 1) indicates that the program aborts abnormally.

EXIT_SUCCESS
EXIT_FAILURE

In functions, it is equivalent to using statements.

main()
exit()
return

C language also provides a function to register additional functions to be executed during execution and to do some finishing work when exiting the program. The prototype of this function is also defined in the header file.

atexit()
exit()
stdlib.h
int atexit(void (*func)(void));

The argument to is a function pointer. Note that its parameter functions cannot accept parameters or have return values.

atexit()

When executing, the registered function will be called automatically first, and then the program will be terminated.

exit()
atexit()

function specifier

extern 说明符

For multi file projects, the source file will use the functions declared in other files. At this time, the current file needs to provide the prototype of the external function and explain that the definition of the function comes from other files.

extern
extern int foo(int arg1, char arg2);

int main(void) {
  int a = foo(2, 3);
  // ...
  return 0;
}

Since the function prototype is by default, it can be omitted.

extern
extern

static 说明符

When used to declare a variable inside a function, it means that the variable only needs to be initialized once, and it does not need to be initialized every time it is called. That is, its value remains unchanged between calls.

static
#include <stdio.h>

void counter(void) {
  static int count = 1;  // 只初始化一次
  printf("%d\n", count);
  count++;
}

int main(void) {
  counter();  // 1
  counter();  // 2
  counter();  // 3
}

Note that when the decorated variable is initialized, it can only be assigned as a constant, not a variable.

static
int i = 3;
static int j = i; // 错误

In the block scope, declared variables have default values.

static
0
static int foo;
// 等同于
static int foo = 0;

It can be used to decorate the function itself, indicating that the function can only be used in the current file.

static
static int Twice(int num) {
  int result = num * 2;
  return(result);
}

It can also be used in parameters to decorate parameter arrays.

static
int sum_array(int a[static 3], int n) {
  // ...
}

In the above example, it will not have any impact on the program behavior, but is only used to tell the compiler that the length of the array is at least 3, which can speed up the program in some cases.

static

In addition, it should be noted that the parameters of multidimensional arrays can only be used for the description of the first dimension.

static

const 说明符

The specifier in the function parameter indicates that the parameter variable cannot be modified inside the function.

const
void f(const int* p) {
  *p = 0; // 该行报错
}

The above writing method only restricts the modification of the value pointed to, and its own address can be modified.

p
p
void f(const int* p) {
  int x = 13;
  p = &x; // 允许修改
}

If you want to restrict the modification of the value pointed to, you can put it first.

p
const
p
void f(int* const p) {
  int x = 13;
  p = &x; // 该行报错
}

If you want to limit the modification and at the same time, you need to use two.

p
*p
const
void f(const int* const p) {
  // ...
}

Variable parameters

The number of parameters of some functions is uncertain. When declaring a function, you can use ellipsis to represent a variable number of parameters.

...
int printf(const char* format, ...);

The symbol must be placed at the end of the parameter sequence, or an error will be reported.

...

The header file defines some macros that can manipulate variable parameters:

stdarg.h

(1) : a data type used to define a variable parameter object. It must be used first when manipulating variable parameters.

va_list

(2) : a function that initializes a variable parameter object. It accepts two parameters. The first parameter is the variable parameter object, and the second parameter is the parameter in the original function before the variable parameter, which is used to locate the variable parameter.

va_start

(3) : a function used to fetch the current variable parameter. After each call, the internal pointer will point to the next variable parameter. It accepts two parameters, the first is a variable parameter object, and the second is the type of the current variable parameter.

va_arg

(4) : a function used to clean up variable parameter objects.

va_end
double average(int i, ...) {
  double total = 0;
  va_list ap;
  va_start(ap, i);
  for (int j = 1; j <= i; ++j) {
    total += va_arg(ap, double);
  }
  va_end(ap);
  return total / i;
}

In the above example, it is defined as a variable parameter object. The parameters following the parameters are put in a unified way to take out a parameter from one by one, and the parameter is specified as a double type to clean up the variable parameter object.

va_list ap
ap
va_start(ap, i)
i
ap
va_arg(ap, double)
ap
va_end(ap)

Reference: C language tutorial

Reference: C language tutorial