C语言(自定义)函数

函数概念

函数可以分为函数库(预制菜)和自定义函数(自己做的菜)

函数库之前的文章讲解过一部分数组的、字符串的、动态内存的,有兴趣可以我的学习专栏看一看,里面都有

今天重点讲解的函数是自定义函数

先来几道预制菜

输入/输出函数(头文件为 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、指针一些语法和指针与数组

工业老哥祝大家新的一年顺顺利利

以上均是本人理解,如有不对欢迎各位大佬评论区指出~

相关推荐
对,就是哥3 分钟前
ABAP弹出对对话框错误信息设计
java·数据库·算法
从以前9 分钟前
解析 World Football Cup 问题及其 Python 实现
开发语言·python·算法
我的运维人生9 分钟前
机器学习算法深度解析:以支持向量机(SVM)为例及实战应用
算法·机器学习·支持向量机·运维开发·技术共享
_.Switch16 分钟前
FastAPI 响应模型与自定义响应
开发语言·前端·数据库·python·fastapi·命令模式
青出于兰16 分钟前
C语言| 二维数字的定义
c语言·数据结构·算法
傻啦嘿哟19 分钟前
Python多线程与类方法的交互:锁提升安全性的奥秘
java·开发语言
半盏茶香19 分钟前
启航数据结构算法之雅舟,悠游C++智慧之旅——线性艺术:顺序表之细腻探索
c语言·开发语言·数据结构·c++·算法·机器学习·链表
已是上好佳30 分钟前
java实验4 反射机制
java·开发语言
小园子的小菜36 分钟前
Rockect基于Dledger的Broker主从同步原理
java·开发语言
Abelard_40 分钟前
LeetCode--复原IP地址(面试手撕算法高频题)
tcp/ip·算法·leetcode