C语言编程---函数与头文件

函数

函数定义

  • 函数,是对关联代码的封装,便于维护、使用;
  • 在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语言编程—指针

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laufing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值