嵌入式系列文章之第一章 C语言の第七节 函数

本文介绍了嵌入式Linux学习中的C语言函数,包括函数的基本用法、调用,参数传递方式,如值传递和指针传递,以及函数间的数组参数处理。此外,还讨论了指针函数、递归函数和函数指针的概念,并给出了相关示例代码。
摘要由CSDN通过智能技术生成

嵌入式(Linux)系列文章目录

第一章 C语言の第一节 必备Linux命令和C语言基础
第一章 C语言の第二节 数据类型、常量、变量及运算符
第一章 C语言の第三节 输入输出专题
第一章 C语言の第四节 控制语句
第一章 C语言の第五节 数组和字符串
第一章 C语言の第六节 指针
第一章 C语言の第七节 函数



前言

本文章采用VMware Workstation Pro进行嵌入式(Linux)系列学习

从零开始,重新学习(嵌入式相关内容)并记录知识点(涉及基础c语言(数据结构)、linux编程、shell脚本、文件io、网络编程)等),同时也是为了方便自身回顾,如有不对的地方请大佬多多指教,谢谢!->欢迎评论区留言


第一章 C语言

第七节 函数

1.函数的基本用法

  • 函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值
  • 格式
<数据类型> <函数名称>(<形式参数说明>)
{
	语句序列;
	return[(<表达式>)];
}
  • <数据类型>是整个函数的返回值类型。return[(<表达式>)]语句中表达式的值,要和函数的<数据类型>保持一致。如无返回值应该写为void型
  • <形式参数说明>是逗号","分隔的多个变量的说明形式
  • 大括弧对{<语句序列>},称为函数体,<语句序列>是大于等于零个语句构成的
  • 函数的说明就是指函数原型
  • <形式参数说明>可以缺省说明的变量名称,但类型不能缺省
1.1 函数的使用(调用)
  • 格式
函数名称(<实际参数>);
  • 实参就是在使用函数时,调用函数传递给被调用函数的(确切的)数据
  • 函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句
  • 代码示例
#include <stdio.h>
//int scanf(const char *format, ...);
//int printf(const char *format, ...);

double power(double, int);

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;
}

double power(double x, int n)
{
	double r = 1;
	int i;
	
	for (i = l; i <= n; i++)
		r *= x;
	return r;
}
  • 结果
input:2 5
2.000000 5 = 32.000000 
  • 思考
  • 什么叫函数的说明,如何进行函数的说明?

答:函数的说明就是指函数原型,可以省略变量名,但不可省略类型, 函数需要先说明(实现)再调用

  • 使用库函数时,为什么引入头文件,有没有别的替代写法?

答:头文件中包含了函数的原型声明,引入头文件主要是为了使代码更加简洁

enshang@enshang:~$ man 3 printf // man 3 查找库函数
enshang@enshang:~$ gcc -E power.c -o power.i // 预处理 将.c文件生成同名.i文件
enshang@enshang:~$ vi power.i // 将头文件的内容展开

2.函数之间的参数传递方式

  • 全局变量
  • 全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的
  • 全局变量一经定义后就会在程序的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响。不建议使用
  • 复制传递方式(值传递)
  • 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
  • 形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参
    复制传递方式的示意图
  • 地址传递方式(指针传递)
  • 按地址传递,实参为变量的地址,而形参为同类型的指针
  • 被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作,相当于对实参本身的操作)
  • 思考
  • 复制传递和地址传递方式有什么区别?

答:复制传递开辟了新的空间,仅将值传递,无法改变实参;地址传递可以改变实参

  • 如何编程可以实现地址传递方式也不能改变实参?

答:加const修饰符

3.函数中传递数组参数

  • 全局数组传递方式
  • 复制传递方式
  • 实参为数组的指针,形参为数组名(本质是一个指针变量)
  • 地址传递方式
  • 实参为数组的指针,形参为同类型的指针变量

注:字符串以外的字符数组(整形的一维数组),需要传递元素和个数;字符串仅需要传递数组即可,不需要传个数,因为有’\0’

4.指针函数(需注意内存管理问题)

  • 指针函数是指一个函数的返回值为地址量的函数
  • 格式
<数据类型> *<函数名称>(<参数说明>)
{
    语句序列;
}
  • 指针函数的可返回值类型
  • 全局变量的地址
  • static变量的地址
  • 字符串常量的地址
  • 堆的地址(通过malloc申请且未释放的地址)
  • 代码示例(删除一个字符串中的空格)
#include <stdio.h>
#include <string.h>

char *del_space(char *s);

int main(int argc, char *argv[])
{
	char *r;
	char str[] = "   how   are you ";
	
	r = del_space(str);
	printf"---%s---\n", r);
	puts(str);
	
	return 0;
}

char *del_space(char *s)
{
	char *r = s;
	char *p = s;
	
	while (*s)
	{
		if (*s == ' ')
		{
			s++;
		}
		else
		{
			*p = *s;
			s++;
			p++;
		}
	}
	*p = '\0';
	
	return r;
}
  • 结果
---howareyou---
howareyou

注:对于一维数组来说需要传一维数组名和元素个数;字符串仅需要传递数组名,程序内部可以通过‘/0’判断结束

  • 代码示例(实现字符串连接)
#include <string.h>

//char *strcat(char *dest, const char *src);
char *mstrcat(char *dest, const char *src);

int main(int argc, char *argv[])
{
	char dest[] = "welcome";
	char src[] = "toxian";
	
	puts(mstrcat(dest, src));
	puts(dest);
	
	return 0;
}

char *mstrcat(char *dest, const char *src)
{
	char *r = dest;
	
	while (*dest)
	{
		dest++;
	}
	
	while (*src)
	{
		*dest = *src;
		dest++;
		src++;
	}
	#dest = '\0';
	
	return r;
}
  • 结果
welcometoxian
welcometoxian
  • 思考
  • 指针函数中可以返回什么样的指针?

答:全局变量的地址; static变量的地址;字符串常量的地址;堆的地址(通过malloc申请且未释放的地址)

  • 编写一个指针函数,输入整数123转化成字符串“123”

答:

  • 代码示例
#include <std1o.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;
		j--;
		i++; 
	}
	
	return p;
}
  • 结果
input:1234
1234

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);
}
  • 结果
input: 4
24
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;
}
  • 线程创建
#include <pthread.h>

int pthread create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • 排序
#include <stdlib.h>

void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));
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);
	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库中的qsport函数来实现整形数组排序

答:

  • 代码示例
#include <stdio.h>
#include <stdlib.h>

int compare(const void *, const void *);

int main(int argc, char *argv[])
{
	int s[] = (5,2,1,13,14,0}, 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);	
}
  • 结果
0 1 2 5 13 14

总结

本章为嵌入式学习系列文章 第一章:C语言の第七节 函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值