目录

前文列表

程序编译流程与 GCC 编译器

C 语言编程 — 基本语法

C 语言编程 — 基本数据类型

C 语言编程 — 变量与常量

C 语言编程 — 运算符

C 语言编程 — 逻辑控制语句

函数

函数的本质就是针对变量的操作过程,同时可能也会改变当前程序的状态。它接受多个输入值,计算并返回一个输出值。

函数大体上分为 3 类:

  1. 主函数:每个 C 程序都至少有一个 main()
  2. 内置函数:由 C 标准库提供。例如,strcat() 用来连接两个字符串,memcpy() 用来复制内存到另一个位置。
  3. 自定义函数

函数的声明

函数声明就是告诉 C 编译器函数的名称、返回类型和参数以及如何调用函数。函数的实际主体可以单独定义,但当你在一个源文件中定义函数且在另一个源文件中调用函数时,函数声明是必需的。

声明函数时,首先将返回值的类型写在前面,后面紧跟函数的名字,而后的一对圆括号里面包裹函数的输入参数,参数之间用 , 进行分割。

函数声明包括以下几个部分:

return_type function_name(parameter list);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面两种都是有效的声明:

int max(int num1, int num2);
int max(int, int);

函数的定义

函数体部分紧跟其后,

函数定义除了定义函数的名称、返回类型、形参列表之外,最重要的是编写函数的主题,即函数体。包裹在 {} 里,里面包含了函数执行的所有语句,语句之间使用 ; 分隔。return 语句用来结束函数的执行,并返回一个值。

C 程序中函数的结构如下:

return_type function_name(parameter list){
body of the function
}
  • 返回类型:一个函数可以返回一个值,返回类型定义了函数返回的值的数据类型。有些函数不需要返回值,在这种情况下,return_type 是关键字 void
  • 函数名(函数标识符):函数名和参数列表一起构成了函数签名。
  • 形式参数:形参就像是占位符。当函数被调用时,向形参传递一个值,而这个值被称为实际参数。形参列表包括函数参数的类型、顺序、数量。形参是可选的,即形参列表可以为。
  • 函数主体:包含了一组定义函数执行任务的语句。
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{
/* 局部变量声明 */
int result; if (num1 > num2)
result = num1;
else
result = num2; return result;
}

函数的形参与实参

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。当调用函数时,有两种向函数传递参数的方式:

  • 值传递:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
  • 引用传递:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

值传递

默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。

  • swap.c
/* 函数定义 */
void swap(int x, int y)
{
int temp; temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */ return;
}
  • main.c
#include <stdio.h>

/* 函数声明 */
void swap(int x, int y); int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200; printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b ); /* 调用函数来交换值 */
swap(a, b); printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b ); return 0;
}

引用传递

通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。

  • swap.c
/* 函数定义 */
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 temp 赋值给 y */ return;
}
  • main.c
#include <stdio.h>

/* 函数声明 */
void swap(int *x, int *y); int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200; printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b ); /* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b); printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b ); return 0;
}

可变长形参列表

有时候我们需要函数带有可变数量的参数,而不是预定义数量的参数。C 语言提供了 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。

int func(int, ... ) {}

int main() {
func(1);
func(1, 2);
func(1, 2, 3);
return 0;
}
  • 定义一个函数,int 形参代表了要传递的可变参数的总数,... 可变长形参运算符标识函数的形参数量是可变的。
  • 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
  • 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
  • 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
  • 使用宏 va_end 来清理赋予 va_list 变量的内存。
#include <stdio.h>
#include <stdarg.h> double Avg(int num, ...) {
va_list valist;
double sum = 0.0;
int i; /* 初始化 valist,数量为 num 个 */
va_start(valist, num);
for (i = 0; i < num; i++) {
/* 访问所有赋给 valist 的参数 */
sum += va_arg(valist, int);
}
/* 清理为 valist 保留的内存 */
va_end(valist); return sum / num; } int main() {
printf("Average of 2, 3, 4, 5 = %f\n", Avg(4, 2, 3, 4, 5));
printf("Average of 5, 10, 15 = %f\n", Avg(3, 5, 10, 15));
return 0;
}

运行:

$ ./main
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

函数的调用

定义 C 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。

#include <stdio.h>

/* 函数声明 */
int max(int num1, int num2); int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret; /* 调用函数来获取最大值 */
ret = max(a, b); printf( "Max value is : %d\n", ret ); return 0;
} /* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{
/* 局部变量声明 */
int result; if (num1 > num2)
result = num1;
else
result = num2; return result;
}

函数的指针

函数名本质就是一个地址,也就是一个指针。函数指针是指向函数的指针类型变量,函数指针可以像一般函数一样,用于调用函数、传递参数。也就是说,函数指针其实可以看做就是一个函数的 “别名”。

函数指针很方便,可以作为实参传入函数,在函数体中,仅仅通过指针就可以调用函数,这也是为了提高代码的效率。函数指针和普通指针差不多,主要的区别是函数指针指向了特定的函数。

声明一个函数指针:

typedef int (*fun_ptr)(int, int);

e.g.

#include <stdio.h>

int max(int x, int y){
return x > y ? x : y;
} int main(void){
/* 定义并初始化一个函数指针 */
int (*p)(int, int) = &max; // 可以省略地址运算符 &
int a, b, c, d; printf("Input three numbers:");
scanf("%d %d %d", &a, &b, &c); d = p(p(a, b), c);
printf("MAX: %d\n", d); return 0;
}

回调函数

回调函数就是将函数指针作为实参传入到某个函数,即:回调函数就是一个通过函数指针调用的函数。

#include <stdlib.h>
#include <stdio.h> void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) { size_t i; for(i = 0; i < arraySize; i++) {
array[i] = getNextValue();
}
} int getNextRandomValue(void) {
return rand();
} int main() {
int i;
int myarray[10]; populate_array(myarray, 10, &getNextRandomValue); // & 可以省略,因为函数名的本质就是一个指针 for(i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
} printf("\n");
return 0;
}

递归函数

递归,指的是在函数的定义中调用函数自身

void recursion()
{
statements;
... ... ...
recursion(); /* 函数调用自身 */
... ... ...
} int main()
{
recursion();
}

但在使用递归时,一定要注意编写退出条件,否则会进入死循环。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。

数的阶乘

#include <stdio.h>

double factorial(unsigned int i) {
if(i <= 1)
{
return 1;
}
return i * factorial(i - 1);
} int main() {
int i = 15;
printf("%d 的阶乘为 %f\n", i, factorial(i));
return 0;
}

斐波那契数列

#include <stdio.h>

int fibonaci(int i) {
if (i == 0) {
return 0;
}
if (i == 1) {
return 1;
}
return fibonaci(i-1) + fibonaci(i-2);
} int main() {
int i;
for (i = 0; i < 10; i++) {
printf("%d\t\n", fibonaci(i));
}
return 0;
}

构造函数(Constructor)

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。例如:

  • Java 中的 new 运算符
  • Python 中的 __init__ 函数

通常的,一个类中只有一个构造函数;特别的,一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们即构造函数的重载。

析构函数(Destructor)

析构函数与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做 “清理善后” 的工作。

以 C++ 为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如:~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括 void 类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

C 语言编程 — 函数的更多相关文章

  1. Linux下C语言编程实现spwd函数

    Linux下C语言编程实现spwd函数 介绍 spwd函数 功能:显示当前目录路径 实现:通过编译执行该代码,可在终端中输出当前路径 代码实现 代码链接 代码托管链接:spwd.c 所需结构体.函数. ...

  2. LINUX下C语言编程调用函数、链接头文件以及库文件

    LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...

  3. C语言编程学习:使用函数必须知道的3点注意事项

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  4. C语言编程中函数指针的定义及使用

    C语言中函数指针的定义: typedef int (*funcPtr)(int, int)表示定义了一个函数指针funcPtr,这个函数指针只能指向如下: int add(int, int).int ...

  5. C语言编程实现Linux命令——who

    C语言编程实现Linux命令--who 实践分析过程 who命令是查询当前登录的每个用户,它的输出包括用户名.终端类型.登录日期及远程主机,在Linux系统中输入who命令输出如下: 我们先man一下 ...

  6. 个人c语言编程风格总结

    总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的.首先声明,我下面提到的编程规范,是自己给自己定的,不是c语言里面规定的. 一件事情,做成和做好中间可能隔了 ...

  7. C语言的函数

    "函数"在英文的翻译是"function",无论在自然科学还是计算机科学都是这个词,而"function"的本意是"功能" ...

  8. 混合语言编程:启用CLR(公共语言运行时编译)让C#调用C++

    前言 关于混合C#和C++的编程方式,本人之前写过一篇博客(参见混合语言编程:C#使用原生的Directx和OpenGL),在之前的博客中,介绍了在C#的Winform和WPF下使用原生的Direct ...

  9. Linux基础与Linux下C语言编程基础

    Linux基础 1 Linux命令 如果使用GUI,Linux和Windows没有什么区别.Linux学习应用的一个特点是通过命令行进行使用. 登录Linux后,我们就可以在#或$符后面去输入命令,有 ...

  10. LINUX下C语言编程基础

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

随机推荐

  1. JDK14中的java tools简介

    目录 故事发生了 java tools简介 jaotc jar jarsigner java javac javadoc javap jcmd jconsole jdb jdeprscan jdeps ...

  2. 巴延兴:从主导多个SIG组到OpenHarmony“代码贡献之星”,我是如何做到的?

    编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献.分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点. 巴延兴 深圳开鸿数 ...

  3. 在python中通过面向对象方式,实现烤地瓜案例

    例子:烤地瓜,不同时间,反馈不同状态,并给不同状态地瓜加入不同味道 烤地瓜时间 0-3分钟,生的 4-7分钟,半生不熟的 8-12分钟,熟了 12分钟以上,已烤熟,糊了 用户可以按自己的意思添加调料 ...

  4. leetcode-数组中两元素的最大乘积

    题目描述 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值. 请你计算并返回该式的最大值. 示例 1: 输入:nums ...

  5. 打造美团外卖新体验,HarmonyOS SDK 持续赋能开发者共赢鸿蒙生态

    从今年 8 月起,所有升级到 HarmonyOS 4 的手机用户在美团外卖下单后,可通过屏幕上的一个"小窗口",随时追踪到"出餐.取餐.送达"等订单状态.这个能 ...

  6. HarmonyOS“一次开发,多端部署“优秀实践——玩机技巧,码上起航

    随着终端设备形态日益多样化,分布式技术逐渐打破单一硬件边界,一个应用或服务,可以在不同的硬件设备之间按需调用.互助共享,让用户享受无缝的全场景体验.作为应用开发者,广泛的设备类型也能为应用带来广大的潜 ...

  7. HDC2021技术分论坛:HarmonyOS本地模拟器重磅来袭!

    作者:longjiangyun,模拟器开发工程师 HarmonyOS模拟器是应用开发者使用IDE进行代码开发.调试.测试等活动中必不可少的工具,它分为本地模拟器和远程模拟器,其中远程模拟器又分为单设备 ...

  8. 4天带你上手HarmonyOS ArkUI开发——《HarmonyOS ArkUI入门训练营之健康生活实战》

     <HarmonyOS ArkUI入门训练营之健康饮食应用>是面向入门开发者打造的实战课程系列.特邀华为终端BG高级开发工程师作为本次训练营讲师,以健康饮食为例,开展技术教学及实战案例分享 ...

  9. Linux之驱动管理

    一.相关概念 驱动概念 驱动与底层硬件直接打交道,充当了硬件与应用软件中间的桥梁. 将驱动程序载入内核,应用程序可以通过系统调用接口来访问(驱动)底层的硬件设备. 驱动功能 对设备初始化和释放 把数据 ...

  10. WPF随笔收录-DataGrid固定右侧列

    一.前言 在项目开发过程中,DataGrid是经常使用到的一个数据展示控件,而通常表格的最后一列是作为操作列存在,比如会有编辑.删除等功能按钮.但WPF的原始DataGrid中,默认只支持固定左侧列, ...