函数概念
函数可以分为函数库(预制菜)和自定义函数(自己做的菜)
函数库之前的文章讲解过一部分数组的、字符串的、动态内存的,有兴趣可以我的学习专栏看一看,里面都有
今天重点讲解的函数是自定义函数
先来几道预制菜
输入/输出函数(头文件为 stdio.h):用于完成输入/输出功能;(最基本的)
字符串函数(头文件为 string.h):用于字符串操作和处理;
数学函数(头文件为 math.h):用于数学函数计算;(就包括平方、开方)
内存管理函数(头文件为 stdlib.h):用于内存管理;(malloc、free就是它)
日期和时间函数(头文件为 time.h):用于日期、时间的转换操作;(取随机数用)
接口函数(头文件为 dos.h):用于与 DOS、BIOS 和硬件的接口。(u3d爱用)
......
除了这些还有一个最基本的 main() 函数,这个东西一个c语言文件只能有一个,因为它相当于一个入口,编译的时候会直接进入main函数,执行里面的东西,入口多了编译器怎么执行?直接就 开摆!
接着自己做菜
函数可以有多种类型,这里主要从之前见过的几种函数里面总结
基本原理
函数,主要就是传入几个东西,再加工产出一些东西(或者不产出就加工,就是void类型),做重要的是,函数是个拷贝原理,拷贝要传入的值,再将(如果有的话)要传出的值拷贝给对应类型变量。
重要性
这里函数的知识与文件什么的都可以属于一个叫程序组织结构 的大类(与数据结构不太一样),就是讲求模块化,这样以后大型工程合作,一个人弄一块最后一汇总就ok了,不要说都丢在main函数里面(甚至都不能都丢在一个文件,这块文件再细说),要不不好分工,如果感兴趣可以看看一些软件
(哇,还有私货)
你就可以看看,都是一堆文件最后集合编译形成一个exe(这个还良心了把源代码贴图也打包了,现在完全可以就传一个exe,这样就能让数据保密了防止逆向工程),如果写个游戏什么的都在一个c语言文件,那这个游戏要不就是十分简单,要不就是神人写的
这块有点扯远了,说到文件那里了,言归正传看看函数用法
返回一个类型的函数
返回一个基本类型(int、char)
这种是最基本的,就是返回一个相关类型的东西
举个例子
cs
int power(int x,int n);
int main()
{
int a,b,s;
scanf("%d%d",&a,&b);
s = power(a,b);
printf("%d\n",s);
return 0;
}
int power(int x,int n)
{
int sum = x + n;
return sum; 返回一个int类型的数值变量的拷贝值
}
main函数里面int类型的s变量对应返回的int类型变量sum的值的拷贝
返回一个指针
一到指针就脑袋疼,不过有个小窍门,就是看它到底有没有开辟内存
举个例子
cs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 函数声明,用于删除字符串中的空格
char* removeSpaces(const char* str);
int main() {
char input[256]; // 假设输入的字符串不会超过255个字符
printf("请输入一段文本:\n");
fgets(input, sizeof(input), stdin); // 读取一行文本
// 移除输入字符串末尾的换行符
input[strcspn(input, "\n")] = 0;
// 调用函数删除空格
char* noSpacesStr = removeSpaces(input);
// 打印结果
printf("删除空格后的文本:\n%s\n", noSpacesStr);
// 释放动态分配的内存
free(noSpacesStr);
return 0;
}
// 函数定义,删除字符串中的空格并返回新的字符串
char* removeSpaces(const char* str) {
if (str == NULL) {
return NULL;
}
// 计算没有空格的字符串长度
int length = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
length++;
}
}
// 分配内存以存储没有空格的字符串
char* result = (char*)malloc(length + 1);
// 复制非空格字符到新字符串
int j = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
result[j++] = str[i];
}
}
result[j] = '\0'; // 添加字符串终止符
return result;
}
这里 char * removeSpace(const char * str)就是一个传入一个有内存的指针(数组、字符串)然后返回一个char类型指针,如果我们给要返回的指针开辟了空间,就相当于返回了一个数组(字符串),需要开辟空间,所以c语言直接 返回一个数组这个说法是错误的,只能返回指针但是这个指针可以是开辟了空间的(也就是数组、字符串),间接返回数组(当然是针对要弄个新的,和直接修改数组不太一样),不直接修改可以避免改变原数组
具体操作可以看
cs
char* result = (char*)malloc(length + 1);
有几块语法强调一下
char * 对应 char *类型
cs
char* noSpacesStr = removeSpaces(input);
而且这句话只能是用等号,赋值函数(strcpy)不可以 ,因为前面那个是个指针,没有开辟空间,赋值的话就会爆内存(前文讲过) ,而等号就可以,因为我这个函数有句话就是在开辟内存,产生了一个result的占用内存的指针也就是字符串,等号表示赋值,就会把这个字符串赋给noSpacestr这个指针,函数里开辟空间相当于给noSpacestr这个指针开辟空间(用等号)
字符串(指针)这里还是晕可以看看我个人主页的一些相关文章
返回一个结构体
这里就是数据结构链表用的多
cs
#include <stdio.h>
#include <stdlib.h>
// 函数声明
NODE *init(); // 初始化链表
void Foreach(NODE* h); // 遍历链表
typedef struct node
{
int n;
struct node *next;
} NODE;
int main()
{
NODE *l = init();
Foreach(l);
return 0;
}
NODE *init() // 初始化链表
{
NODE *h = (NODE*)malloc(sizeof(NODE)); // 创建一个头节点
h->n = -1;
h->next = NULL;
NODE *pR = h; // 尾部指针
int val = -1;
while(1)
{
printf("Enter a number(-1 to End):\n");
scanf("%d",&val);
if(val == -1)
{
break;
}
NODE *new = (NODE*)malloc(sizeof(NODE)); // 创建新节点
new->n = val;
new->next = NULL;
pR->next = new; // 新节点插入到链表中
pR = new; // 更新尾部指针指向
}
return h;
}
void Foreach(NODE* h)
{
if(NULL == h)
{
return;
}
NODE *pC = h->next; // 辅助指针变量
while(pC != NULL)
{
printf("%d ", pC->n);
pC = pC->next;
}
}
NODE *init就是返回一个结构体类型的变量的函数(一堆链表欢迎回顾通关文牒)
void(什么都不返回)类型函数
一般就是打印一个或者什么的不需要返回一个数,但是也有个例,还记得老朋友(该死的)数组吗?数组是地址,这个概念要有,那么函数操作数组就不是复制操作了,而是直接修改(和上面返回指针那个创建一个新的修改过的数组就不太一样了,虽说写法简单,但是修改了原数组),也不需要return数组
cs
#include <stdio.h>
// 函数声明
void inputNumbers(int arr[], int size);
int main() {
int numbers[5]; // 声明一个可以存储5个整数的数组
// 调用函数来录入数字
inputNumbers(numbers, 5);
// 打印录入的数字
printf("录入的数字是: ");
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
// 函数定义
void inputNumbers(int arr[], int size) {
printf("请输入 %d 个数字:\n", size);
for (int i = 0; i < size; i++) {
printf("数字 %d: ", i + 1);
scanf("%d", &arr[i]); // 从用户那里读取一个整数并存储在数组中
}
}
并没有返回这个数组,因为已经通过地址修改过这个数组了,相当于直接操作,不需要return
固定语法格式
定义函数时括号里面是形参,他的生命只存在于函数里面
使用函数时括号里面是实参,就是main函数的变量传进去
函数如果写在main函数类型后面,开头要引用(可以看上面例子main函数上面那行代码,函数格式、类型要对应)
这里就用最开始的例子
cs
int power(int x,int n);
int main()
{
int a,b,s;
scanf("%d%d",&a,&b);
s = power(a,b);
printf("%d\n",s);
return 0;
}
int power(int x,int n)
{
int sum = x + n;
return sum; 返回一个int类型的数值变量的拷贝值
}
x、n是形参,只存在于函数里面 ,而且通用不传入实参没有具体值(有点像数学函数里面的变量)
而a、b就是实参,实实在在的参数(数学函数计算带进去的具体值)
总结
函数这块我们见了很多类型,重要的是养成这个模块化的思维,还是学会拆分任务
我们学习了(人机味略重)
1、函数
2、返回不同类型函数
3、一些固定语法
4、指针一些语法和指针与数组
工业老哥祝大家新的一年顺顺利利
以上均是本人理解,如有不对欢迎各位大佬评论区指出~