函数
函数定义
- 函数,是对关联代码的封装,便于维护、使用;
- 在C项目中,只有一个main函数(入口函数),内部调用其他函数;
- 函数定义格式
// 声明不含 主体{}
// 定义包含 主体{}
返回类型 函数名(形参){
函数体
}
如:
int calSum(int a, double b){
return a + (int)b ; // 强制类型转换
}
- 通常一个函数,完成一个任务;
- C函数内部不能再定义函数,不像python;
- 可以嵌套调用、递归调用;
函数使用
- 同一个源文件中,A函数内调用B函数时,若调用前已经定义了B函数,则可以直接调用;否则必须先声明,再调用。
//定义无返回值的A函数
void A() {
B(); //面向过程,此时还没有B函数 报错
}
//B函数在下面定义
void B() {
printf("B函数...");
}
需要先声明B函数:
void B(); //声明B函数
void A() {
B(); //面向过程,此时还没有B函数
}
void B() {
printf("B函数...");
}
对于一个C项目:
- 所有的源文件中,只能有一个源文件包含main函数;
- 调试时,从main函数开始执行;
- 不同源文件之间, A函数内部调用B函数时,必须(本身源文件)先声明,才能调用。
// 声明
int B(); // 本源文件找不到,就找其他的源文件
// 定义一个返回int的主函数
int A() {
// 调用
B(); // 其内部用到的变量等会从其源文件中查找;
// 函数体结束,return int
return 0;
}
- 不同源文件也可以通过头文件相互包含,声明放在头文件中。
sourceB.c 源码,
sourceB.h 源码头文件,将源码中的内容声明到头文件中,供其他模块(源文件)使用。
#ifndef __SOURCEA_H__
#define __SOURCEA_H__
void B(); // 头文件中声明;
#endif
在sourceA.c中包含该头文件:
#include <stdio.h> //标准头文件(编译器提供)
#include "xxx/sourceB.h" //包含自定义头文件
函数的传参
- 基本类型是
值传递
,将变量的值复制一份给形式参数,函数内部的改变不影响外部全局变量的值;- 整型、 浮点型、字符、枚举;
- 结构体
变量
、共用体变量 - 数组变量传地址
地址引用
,通过指针方式,可以引用变量的地址,内部对指针的值的修改,会影响外部的全局变量;- sizeof 只能在函数外部或者main函数中使用,计算数组的长度;
- 函数的返回值可以参与计算,或者作为函数实参;
// 其他 源文件xx.c
/* 函数定义 */
void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
return;
}
// 主程序的源文件
#include <stdio.h>
/* 函数声明 */
extern 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;
}
以上案例为值传递,函数内部的改变,不影响外部的变量;
如下为指针形式,交换变量的值;
/* 函数定义,传参指针 */
void swap(int* x, int* y)
{
int temp;
temp = *x; /* 保存 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 temp 赋值给 y */
return;
}
//使用时
swap(&a, &b);
- 传参数组
// 定长数组
int func(int arr[10], int size){
return 0;
}
// 边长数组
int func2(int arr[], int size){
return 0;
}
// 函数调用
func(arr);
// 赋值、打印
函数的应用
- 函数实现冒泡排序
- 数组中n个数,则n-1次冒泡;
void bubbleSort(int arr[], int size) { // 除了main函数外,其他函数中不能使用sizeof
int temp;
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int arr[] = { 4,3,2,1,5 };
bubbleSort(arr, 5);
for (int i = 0; i < 5; i++) {
printf("arr[%d]=%d\n", i, arr[i]);
}
return 0;
}
- main函数接收命令行参数,并通过一个printFunc 递归打印所有的参数;
void printFunc(char* arr[], int size) {
if (arr <= &arr[size - 1]) {
printf("%s\n", *arr);
arr++; // 地址偏移一个类型
}
else {
return;
}
// 递归调用
printFunc(arr, size);
return;
}
int main(int argc, char* argv[]) {
printFunc(argv, argc);
return 0;
}
编译并执行:
# 编译
C:\Users\lenovo\source\repos\laufing>gcc *.c
C:\Users\lenovo\source\repos\laufing>a.exe jack tom lucy lili
a.exe
jack
tom
lucy
lili
内部函数与外部函数
- 内部函数
- 仅在定义的源文件中使用,使用static修饰;
- 不同源文件的内部函数,可以重名;
- 外部函数,
- 在一个源文件中定义,可以在其他的源文件中声明并使用,
- 定义时使用extern修饰(extern可以省略,即默认定义的函数就是外部函数);
- 使用时,使用extern声明;
- 案例,将上例
main函数接收命令行参数
中的printFunc函数,放入另一个源文件,并分别使用static、extern修饰,查看在main函数中的使用效果;
extern int printFunc(char* arr[], int size);
int main(int argc, char* argv[]) {
printFunc(argv, argc);
return 0;
}
# 找到所有的C源文件,并编译
C:\Users\lenovo\source\repos\laufing>gcc ./*.c ./pp/*.c -o a.exe
C:\Users\lenovo\source\repos\laufing>a.exe a b c
a.exe
a
b
c
- 局部变量,函数内部定义的变量,包括函数的参数;
- 全局变量,函数外部定义的变量;
编译器内置库
-
math.h,数学计算
- abs(int) 整数绝对值;
- labs(long) 长整型的绝对值;
- fabs(double) 浮点数的绝对值;
- sin 正弦值;
- cos 余弦值;
- tan;
- pow(int, 3) 次幂;返回double;
- powf/powl 浮点型、长整型的次幂;
-
ctype.h 字符的操作;
- isalpha(int) 是否字符;
- isdigit(int) 是否数字(ASCII码对应的0-9数字);
- isalnum(int) 是否字符或数字;
-
time.h 时间的处理;
- time(time_t *a) 获取当前的时间,并存入a地址;
- localtime(time_t *a) 将a地址的时间,转为本地时间,返回时间结构体struct tm
- r.tm_hour
- r.tm_min
- r.tm_sec…
-
stdio.h 标准输入、输出
- getchar(void) 输入一个字符 (可以获取到回车的换行符),并返回int;
- putchar(int) 输出一个字符;
- gets_s(char* ptr, int size) 输入一个字符串,存入ptr地址;
int main(int argc, char* argv[]) {
char* a = (char*)malloc(sizeof(char)*10); // 分配内存
memset(a, '\0', 10);// 初始化内存
gets_s(a, 10); // 输入字符串
puts(a); //输出字符串
return 0;
}
- puts(char* p) 输出一个字符串;
- scanf(“%s”, &a); 扫描字符串,并存入地址,遇到空格、换行则结束;
- printf(“%d\n”, a); 打印出数值;
头文件
- C中以.h结尾的文件;
- 包含了函数的声明、函数的定义、宏(参数化)定义等,可以被其他源文件引用,并直接使用函数、宏;
// 自定义头文件
#ifndef __BASE_H__ //保证头文件仅导入一次
#define __BASE_H__
// 函数的声明
int main1();
// 函数的定义
int lauf(int a, int b) {
return a + b;
}
// 参数化的宏定义,计算数组的长度
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
// 参数化的宏定义,拼接字符串
// 直接传参
#define LAUFCAT(a, b) (#a "拼接" \
#b) // 预处理器指令末尾没有分号;# 参数名字本身 字符串化; \ 宏延续运算符
#endif // !__BASE_H__
-
引用头文件使用
#include
- 引用标准头文件
#include <stdio.h>
,即编译器自带的头文件; - 引用自定义头文件
#include "myHead.h"
,从当前源文件的相对目录中搜索。
- 引用标准头文件
-
为保证
头文件只引入一次
,头文件的内容放在条件编译中。
#ifndef HEADER
#define HEADER
content
#endif
多个条件引用
#if SYSTEM_1
#include "system_1.h"
#elif SYSTEM_2
#include "system_2.h"
#elif SYSTEM_3
...
#endif
- 宏定义代替,定义宏常量,代替后面的内容;
#define HEAD "header.h"
#include HEAD
- 当头文件较多时,可以使用一个global.h包含其他的头文件;然后在源文件中只包含该global.h即可。
作用域
- 函数外,为全局作用域;
- 函数内,为局部作用域;
- 函数形参,也是局部变量;
- 局部作用域的变量为局部变量,全局作用域的变量为全局变量;
- 局部变量不会自动初始化,全局变量会自动初始化;
全局变量的初始化:
int -> 0
char -> ‘\0’
float -> 0.0
double 0.0
pointer NULL
变量使用前一定要初始化;
[上一篇]:C语言编程—基础学习
[下一篇]:C语言编程—指针