嵌入式(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语言の第七节 函数