C语言学习笔记之函数
文章目录
- 1、函数的基本用法
- 2、函数的参数传递
- 2.1 全局变量
- 2.2 复制传递方式
- 2.3 地址传递方式
- 3、函数的传参—数组
- 4、指针函数
- 5、递归函数和函数指针
- 5.1 递归函数
- 5.2 函数指针
- 5.3 函数指针数组
1、函数的基本用法
函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值。
一般形式如下:
-
<数据类型> <函数名称>(<形式参数说明>) { 语句序列; return[(<表达式>)]; }
-
<数据类型>
是整个函数的返回值类型。 -
return[(<表达式>)]
语句中表达式的值,要和函数的<数据类型>
保持一致。如无返回值应该写为void型 -
<
形式参数说明>
是逗号”,
”分隔的多个变量的说明形式 -
大括弧对
{<语句序列> }
,称为函数体;<语句序列>
是大于等于零个语句构成的
函数的说明就是指函数原型
其中,<形式参数说明>
可以缺省说明的变量名称,但类型不能缺省
- 例如:
double Power(double x, int n) ;
double Power(double, int);
函数的使用也叫函数的调用,形式如下:
函数名称(〈实际参数〉)
- 实参就是在使用函数时,调用函数传递给被调用函数的数据。需要确切的数据
- 函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句。
编写一个函数显示 “Hello, guy! ”,然后编写主程序main调用它。
#include <stdio.h>void Displayhello()
{printf("Hello,guy!\n");
}
int main()
{Displayhello(); //调用return 0;
}
定义求xn值的函数( x是实数, n为正整数)
#include <stdio.h>double power(double x, int n) //函数的说明(需要放在主函数前面)
{double r = 1;int i;for (i = 1; i <= n; i++)r *= x;return r;
}int main()
{double x, ret;int n;printf("input:");scanf("%lf%d", &x, &n);ret = power(x, n);//函数的调用printf("%lf %d = %lf\n", x, n, ret);return 0;
}
2、函数的参数传递
函数之间的参数传递方式:
- 全局变量
- 复制传递方式
- 地址传递方式
2.1 全局变量
- 全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的
- 全局变量一经定义后就会在程序的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响。不建议使用
例如:
#include <stdio.h>double x = 2;//全局变量
int n = 3; //全局变量double power();int main()
{
// double x = 2;
// int n = 3;double ret;ret = power();printf("%lf %d = %lf\n", x, n, ret);return 0;
}double power()
{double r = 1;int i;for (i = 1; i <= n; i++)r *= x;return r;
}
2.2 复制传递方式
- 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
- 形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参
例如:
#include <stdio.h>double power(double, int);int main()
{double x = 2;int n = 3;double ret;printf("&x=%p &n=%p\n", &x, &n);ret = power(x, n);printf("%lf %d = %lf\n", x, n, ret);return 0;
}double power(double a, int b) //double a = x; int b = n;
{double r = 1;int i;printf("&a=%p &b=%p\n", &a, &b);for (i = 1; i <= b; i++)r *= a;return r;
}
例如:写一个函数,实现两个数据的交换
#include <stdio.h>void swap(int x, int y);int main()
{int a = 10;int b = 20;printf("before:%d %d\n", a, b);swap(a, b);printf("after:%d %d\n", a, b);return 0;
}
//int x = a; int y = b;
void swap(int x, int y)
{int t;t = x;x = y;y = t;
}
2.3 地址传递方式
- 按地址传递,实参为变量的地址,而形参为同类型的指针
- 被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作,相当于对实参本身的操作)
#include <stdio.h>void swap(int * x, int * y);int main()
{int a = 10;int b = 20;printf("before:%d %d\n", a, b);swap(&a, &b);printf("after:%d %d\n", a, b);return 0;
}
//int * x = &a; int *y = &b;
void swap(int * x, int * y)
{int t;t = *x;//a*x = *y;*y = t;
}
例如:编写一个函数,统计字符串中小写字母的个数,并把字符串中的小写字母转化成大写字母
#include <stdio.h>int str_fun(char * p);int main(int argc, char *argv[])
{char s[] = "welcome2025Beijing";int n;n = str_fun(s);printf("n=%d %s\n", n, s);return 0;
}int str_fun(char * p) //char * p = s;
{int num = 0;while (*p != '\0') {//while (*p)if (*p <= 'z' && *p >= 'a') {num++;*p -= ' ';}p++;}return num;
}
3、函数的传参—数组
全局数组传递方式
复制传递方式
- 实参为数组的指针,形参为数组名(本质是一个指针变量)
地址传递方式
- 实参为数组的指针,形参为同类型的指针变量
例如:编写函数,计算一个一维整形数组的所有元素的和
//写法1:
#include <stdio.h>int array_sum(int data[], int n);int main(int argc, char *argv[])
{int a[] = {5, 9, 10, 3, 10};int sum = 0;sum = array_sum(a, sizeof(a)/sizeof(int));//sizeof(a)/sizeof(int)=元素个数printf("sum=%d\n", sum);return 0;
}int array_sum(int data[], int n) // int data[] = a;error int * data = a;
{//int n = sizeof(a)/sizeof(int);int ret = 0;int i;for (i = 0; i < n;i++) {printf("%d\n", data[i]);ret += data[i];}return ret;
}
//写法2:
#include <stdio.h>int array_sum(int * data, int n);int main(int argc, char *argv[])
{int a[] = {5, 9, 10, 3, 10};int sum = 0;sum = array_sum(a, sizeof(a)/sizeof(int));printf("sum=%d\n", sum);return 0;
}int array_sum(int * data, int n) //int * data = a;
{//int n = sizeof(a)/sizeof(int);int ret = 0;int i;for (i = 0; i < n;i++) {printf("%d\n", data[i]);ret += data[i];}return ret;
}
例如:编写函数,删除字符串中的空格
#include <stdio.h>void del_space(char * s1);int main(int argc, char *argv[])
{char s[] = " h a sdf g ";puts(s);del_space(s);puts(s);return 0;
}void del_space(char * s1)
{char * s2;s2 = s1;while (*s1) {if (*s1 == ' '){s1++;}else {*s2 = *s1;s1++;s2++;}}*s2 = '\0';
}
多维数组与函数
- 多维数组作为参数在函数间的传递也有全局数组、复制传递和地址传递三种方式。
4、指针函数
指针函数是指一个函数的返回值为地址量的函数
指针函数的定义的一般形式如下
<数据类型> * <函数名称>(<参数说明>) {
语句序列;
}
返回值:全局变量的地址/static变量的地址/字符串常量的地址/堆的地址
下面程序是否有问题,若有问题,如何修改?
#include <stdio.h>
char * mystring() {char str[20];//局部变量,strcpy(str, “Hello”);return str;
}
int main(void)
{printf(“%s\n”, mystring());//最后结果乱码return 0;
}
修改后
#include <stdio.h>
#include <string.h>//char str[20];
char * mystring();int main(int argc, char *argv[])
{char * r;r = getstring();printf("---%s---\n", getstring());//(*r)++;puts(r);return 0;
}char * mystring()
{//char str[20];//error//static char str[20]; //静态变量char * str = "hello"; //不需要改的情况下可以使用// strcpy(str, "hello");return str;
}
例如:编写一个指针函数, 删除一个字符串中的空格
#include <stdio.h>
#include <string.h>char * del_space(char * s);int main(int argc, char *argv[])
{//char * r;char str[]= " how are you ";char s[50], s2[50];// r = del_space(str);// printf("---%s---\ n", r);//strcpy(s, del_space(str));strcpy(s2, strcpy(s, del_space(str)));//m=n=kputs(str);puts(s);puts(s2);return 0;
}char * del_space(char * s) //char * s = str;
{char * r = s;char * p = s;while (*s) {if (*s == ' ')s++;else {*p = *s;s++;p++;}}*p = '\0';return r;
}
例如:编写一个指针函数, 实现字符串连接
#include <stdio.h>char * mstrcat(char * dest, const char * src);int main(int argc, char *argv[])
{//char * r;char dest[50] = "welcome to";char src[] = "China";puts(mstrcat(dest, src));puts(dest);return 0;
}char * mstrcat(char * dest, const char * src)
{char * r = dest;while (*dest++);dest--;while (*dest++ = *src++);return r;/*char * r = dest;while (*dest++);dest--;while (*src) {*dest++ = *src++;}*dest = '\0';return r;*//*char * r = dest;while (*dest) {dest++;}while (*src) {*dest = *src;dest++;src++;}*dest = '\0';return r;*/
}
例如:编写一个指针函数,把整数123转化成字符串”123”。
#include <stdio.h>char * itoa(int n);int main(int argc, char *argv[])
{int n;char * s;printf("input:");scanf("%d", &n);s = itoa(n);puts(s);return 0;
}char * itoa(int n)
{int r, i = 0, j;static char p[50];while (n) {r = n % 10;n /= 10;p[i] = r + '0';i++;}p[i] = '\0';j = i-1;i = 0;while (i < j) {r = p[i];p[i] = p[j];p[j] = r;i++;j--;}return p;
}
5、递归函数和函数指针
5.1 递归函数
递归函数是指一个函数的函数体中直接或间接调用了该函数自身
递归函数调用的执行过程分为两个阶段:
- 递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件
- 回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解
例如:编写一个递归函数,计算n!
#include <stdio.h>int fac(int n);int main(int argc, char *argv[])
{int n;printf("input:");scanf("%d", &n);printf("%d\n", fac(n));return 0;
}int fac(int n)
{if (n == 0 || n == 1)return 1;return n * fac(n-1);
}
例如:编写一个递归函数,计算斐波那契数列(与下面类似)
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?
我们不妨拿新出生的一对小兔子分析一下:第一个月小兔子没有繁殖能力,所以还是一对两个月后,生下一对小兔对数共有两对三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对
#include <stdio.h>int fib(int n);int main(int argc, char *argv[])
{int n = 1;while (n <= 10) {printf("%d ", fib(n));n++;}printf("\n");return 0;
}int fib(int n)
{if (n == 1 || n == 2)return 1;return fib(n-1)+fib(n-2);
}
5.2 函数指针
函数指针用来存放函数的地址,这个地址是一个函数的入口地址
函数名代表了函数的入口地址
函数指针变量说明的一般形式如下
<数据类型> (*<函数指针名称>)(<参数说明列表>);
<数据类型>
是函数指针所指向的函数的返回值类型<参数说明列表>
应该与函数指针所指向的函数的形参说明保持一致(*<函数指针名称>)
中,*说明为指针()不可缺省,表明为函数的指针
#include <stdio.h>int add(int a, int b) {return a+b;
}
int sub(int a, int b) {return a-b;
}
int mul(int a, int b) {return a*b;
}int main(int argc, char *argv[])
{int m = 10, n = 20;int (* p)(int, int);p = add;//printf("%d\n", add(m, n));printf("%d\n", (*p)(m, n));p = sub;printf("%d\n", (*p)(m, n));return 0;
}
5.3 函数指针数组
函数指针数组是一个保存若干个函数名的数组
一般形式如下
<数据类型> (*<函数指针数组名称> [<大小>] )(<参数说明列表> );
- 其中,<大小>是指函数指针数组元数的个数
- 其它同普通的函数指针
#include <stdio.h>int add(int a, int b) {return a+b;
}
int sub(int a, int b) {return a-b;
}
int mul(int a, int b) {return a*b;
}int main(int argc, char *argv[])
{int m = 10, n = 20;int (* p[2])(int, int);//int * p[3]p[0] = add;//printf("%d\n", add(m, n));printf("%d\n", (*p[0])(m, n));p[1] = sub;printf("%d\n", (*p[1])(m, n));return 0;
}
例如:调用C库中的qsort函数来实现整形数组的排序。
#include <stdio.h>
#include <stdlib.h>int compare(const void *, const void *);int main(int argc, char *argv[])
{int s[] = {89, 23, 10, 8, 7, 61}, n, i;n = sizeof(s)/sizeof(int);qsort(s, n, sizeof(int), compare);for (i = 0; i < n; i++)printf("%d ", s[i]);puts("");return 0;
}int compare(const void * p, const void * q)
{return (*(int *)p - *(int *)q);}
相关文章:
C语言学习笔记之函数
文章目录 1、函数的基本用法2、函数的参数传递2.1 全局变量2.2 复制传递方式2.3 地址传递方式 3、函数的传参—数组4、指针函数5、递归函数和函数指针5.1 递归函数5.2 函数指针5.3 函数指针数组 1、函数的基本用法 函数是一个完成特定功能的代码模块,其程序代码独立…...
集合进阶2
Java不可变集合、Stream流与方法引用深度解析 一、不可变集合(Immutable Collections)进阶指南 1.1 不可变集合核心特性 防御性编程:防止外部修改数据(如传递集合给第三方库时)线程安全:天然支持多线程读…...
2025云上人工智能安全发展研究
随着人工智能(AI)技术与云计算的深度融合,云上AI应用场景不断扩展,但安全挑战也日益复杂。结合2025年的技术演进与行业实践,云上AI安全发展呈现以下关键趋势与应对策略: 一、云上AI安全的主要挑战 数据泄露…...

【C++】模版(1)
目录 1. 泛型编程 2. 函数模版 2.1 函数模版概念 2.2 函数模版格式 2.3 函数模版的原理 2.4 函数模版实例化方式 隐式实例化 显式实例化 2.5 模版参数的匹配原则 3. 模版类 模版类的定义格式 模版类的实例化 1. 泛型编程 如何实现一个通用的交换函数呢?…...

基于开源AI智能名片链动2+1模式S2B2C商城小程序源码的去中心化商业扩散研究
摘要:本文探讨在去中心化商业趋势下,开源AI智能名片链动21模式S2B2C商城小程序源码如何助力企业挖掘数据价值、打破信息孤岛,实现商业高效扩散。通过分析该技术组合的架构与功能,结合实际案例,揭示其在用户关系拓展、流…...

5月19日day30打卡
模块和库的导入 知识点回顾: 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑:找到根目录(python解释器的目录和终端的目录不一致) 作业:自己新建几个不同路径文件尝试下如何导入 一、导入官方库 …...

白杨SEO:不到7天,白杨SEO博客网站百度搜索显示和排名恢复正常!顺带说说上海线下GEO聚会分享和播客红利
大家好,我是白杨SEO,专注SEO十年以上,全网SEO流量实战派,AI搜索优化研究者。 5月开始,明显就忙起来了,不管是个人陪跑还是企业顾问,不管是需要传统SEO还是新媒体流量,还是当下这个A…...
Windows软件插件-音视频捕获
下载本插件 音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。 本插件捕获电脑外接的音频和视频。最多可以同时获取4个视频源和4个音频源。插件可以在win32和MFC程序中使用。 使用方法 首先,加载本“捕获”DLL,…...
go 与面向对象编程(OOP)
Go 语言在设计上与传统面向对象(OOP)语言(如 Java、C)有明显差异,官方明确表示它并非纯面向对象语言。然而,它通过独特的方式实现了部分面向对象的核心特性。以下是关键分析: 1. Go 对传统 OOP…...
Mergekit——任务向量合并算法Ties解析
Mergekit——高频合并算法 TIES解析 Ties背景Ties 核心思想具体流程总结 mergekit项目地址 Mergekit提供模型合并方法可以概况为四大类:基本线性加权、基于球面插值、基于任务向量 以及一些专业化方法,今天我们来刷下基于任务向量的ties合并方法…...

Java 应用中的身份认证与授权:OAuth2.0 实现安全的身份管理
Java 应用中的身份认证与授权:OAuth2.0 实现安全的身份管理 在当今的软件开发领域,身份认证与授权是构建安全可靠应用的关键环节。而 Java 作为广泛使用的编程语言,在实现这一功能上有着诸多成熟的框架和方案。其中,OAuth2.0 凭借…...

【氮化镓】偏置对GaN HEMT 单粒子效应的影响
2025年5月19日,西安电子科技大学的Ling Lv等人在《IEEE Transactions on Electron Devices》期刊发表了题为《Single-Event Effects of AlGaN/GaN HEMTs Under Different Biases》的文章,基于实验和TCAD仿真模拟方法,研究了单粒子效应对关断状态、半开启状态和开启状态下AlG…...

Mysql 索引概述
索引(index)是帮助Mysql高效获取数据的数据结构 索引优点:1. 提高排序效率 2. 提高查询效率 索引缺点:1.索引占用空间(可忽略)2.索引降低了更新表的速度,如进行insert,update,delette 时效率降…...
HttpServletRequest常用功能简介-笔记
javax.servlet.http.HttpServletRequest 是 ServletRequest 接口的子接口,专用于处理 HTTP 协议相关的请求。它提供了访问请求行、请求头、请求参数以及请求属性等方法。 1.请求行(Request Line) ✅ 功能说明 请求行包含客户端发送的 HTTP …...

解决RAGFlow部署中镜像源拉取的问题
报错提示 Error response from daemon: Get "https://registry-1.docker.io/v2/ ": context deadline exceeded 解决方法 这个原因是因为拉取镜像源失败,可以在/etc/docker/daemon.json文件中添加镜像加速器,例如下面所示 {"registry…...

uniapp打包H5,输入网址空白情况
由于客户预算有限,最近写了两个uniapp打包成H5的案例,总结下面注意事项 1. 发行–网站-PCWeb或手机H5按钮,输入名称,网址 点击【发行】,生成文件 把这个给后端,就可以了 为什么空白呢 最重要一点…...
wsl2中Ubuntu22.04配置静态IP地址
第一步找到/etc/netplan目录下的01-netcfg.yaml文件,(如果不存在则自己创建一个)在里面配置一下代码: network:version: 2renderer: networkdethernets:eth0:dhcp4: noaddresses: [192.168.3.222/24]routes:- to: 0.0.0.0/0via: …...

C++(21):fstream的读取和写入
目录 1 ios::out 2 ios::in和is_open 3 put()方法 4 get()方法 4.1 读取单个字符 4.2 读取多个字符 4.3 设置终结符 5 getline() 1 ios::out 打开文件用于写入数据。如果文件不存在,则新建该文件;如果文件原来就存在,则打开时清除…...

NAT/代理服务器/内网穿透
目录 一 NAT技术 二 内网穿透/内网打洞 三 代理服务器 一 NAT技术 跨网络传输的时候,私网不能直接访问公网,就引入了NAT能讲私网转换为公网进行访问,主要解决IPv4(2^32)地址不足的问题。 1. NAT原理 当某个内网想访问公网,就必…...

Unity 多时间源Timer定时器实战分享:健壮性、高效性、多线程安全与稳定性能全面解析
简介 Timer 是一个 Unity 环境下高效、灵活的定时任务调度系统,支持以下功能: •支持多种时间源(游戏时间 / 非缩放时间 / 真实时间) •支持一次性延迟执行和重复执行 •提供 ID、回调、目标对象等多种查询和销毁方式 •内建…...
深入解析Spring Boot与Spring Security的集成实践
深入解析Spring Boot与Spring Security的集成实践 引言 Spring Security是Spring生态中用于处理认证和授权的强大框架。在Spring Boot项目中集成Spring Security可以轻松实现用户认证、权限控制等功能。本文将详细介绍如何从零开始集成Spring Security,并解决实际…...

【iOS】探索消息流程
探索消息流程 Runtime介绍OC三大核心动态特性动态类型动态绑定动态语言 方法的本质代码转换objc_msgSendSELIMPMethod 父类方法在子类中的实现 消息查找流程开始查找快速查找流程慢速查找流程二分查找方法列表父类缓存查找 动态方法解析动态方法决议实例方法类方法优化 消息转发…...
用户账号及权限管理:企业安全的基石与艺术
在当今数字化时代,用户账号及权限管理已成为企业IT安全体系中不可或缺的核心组件。它不仅是保护敏感数据的第一道防线,更是确保业务运营效率和合规性的关键。本文将深入探讨用户账号及权限管理的重要性、最佳实践以及实施策略,助您构建一个安全、高效且灵活的访问控制体系。…...

413 Payload Too Large 问题定位
源头 一般是服务器或者nginx 配置导致的 nginx http {client_max_body_size 50m; # 调整为所需大小(如 50MB)# 其他配置... }nginx 不配置,默认是1M 服务器 spring 不配置也是有默认值的好像也是1M 如果出现413 可以试着修改配置来避…...

2025年渗透测试面试题总结-360[实习]安全工程师(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 1. 自我介绍 2. WAF及其绕过方式 3. IPS/IDS/HIDS 4. 云安全 5. 绕过安骑士/安全狗 6. Gopher扩展…...

Ubuntu16.04升级gcc/g++版本方法
0 前言 gcc与g分别是GNU的c和c编译器,Ubuntu16.04默认的gcc和g的版本是5.4.0,在使用一些交叉编译工具链会提示找不到GLIBC_2.27,而GLIBC_2.27又需要gcc 6.2以上版本,因此本文介绍Ubuntu16.04升级gcc/g版本的方法。 1 Ubuntu16.0…...

微信小程序van-dialog确认验证失败时阻止对话框的关闭
使用官方(Vant Weapp - 轻量、可靠的小程序 UI 组件库)的before-close: wxml: <van-dialog use-slot title"名称" show"{{ show }}" show-cancel-button bind:cancel"onClose" bind:confirm"getBackInfo"…...
边缘计算模块
本文来源 :腾讯元宝 边缘计算模块是一种部署在网络边缘(靠近数据源)的集成化硬件/软件设备,用于实时处理本地数据,减少云端依赖,提升响应速度与安全性。以下是其核心要点: 1. 核心组成 …...
【极兔快递Java社招】一面复盘|数据库+线程池+AQS+中间件面面俱到
【极兔快递Java社招】一面复盘|数据库线程池AQS中间件面面俱到 📍面试公司:极兔快递 👜面试岗位:Java后端开发工程师 🕐面试时长:约 60 分钟 🔄面试轮次:第 1 轮技术面&…...

OceanBase 的系统变量、配置项和用户变量有何差异
在继续阅读本文之前,大家不妨先思考一下,数据库中“系统变量”、“用户变量”以及“配置项”这三者之间有何不同。如果感到有些模糊,那么本文将是您理清这些概念的好帮手。 很多用户在使用OceanBase数据库中的“配置项”和“系统变量”&#…...