首页 > 其他分享 >C语言——函数

C语言——函数

时间:2024-08-02 21:59:03浏览次数:7  
标签:调用 函数 int sumR C语言 数组 main

C语言——函数

函数的主要思想是:函数其实是从上到下逐步求解的过程,把一个大的问题拆成多个小的子问题或者说把一个大的功能拆成小的功能模块,通过实现小的功能最终实现大的功能的过程。

函数的语法

类型标识符 函数名(形式参数)
{
函数体语句;
}

其中类型标识符是函数要带出结果的类型也就是返回值的数据类型,注意数组类型不能作为返回值类型。如果函数不需要带出结果时就可以将返回结果的类型标识符设置为void。如果返回结果的类型和函数的类型标识符不一致,其最终结果的类型以类型标识符为准,也就是最终结果的类型都会转换为类型标识符的类型,所以在返回结果时要注意返回结果的类型和类型标识符是不是一致,否则可能会导致数据类型有高精度转为低精度而导致返回结果的精度丢失。还需要注意的是如果不写类型标识符默认是int类型。

函数名,函数名的命名规则和命名标识符的命名规则是一致的,名字必须以字母数字下划线构成、数字不能开头,不能和关键字或库文件重名,在进行函数名的命名时建议起与函数功能有关的名字这样做在后期再查看代码时看到函数名就能大致知道该函数的作用。

形式参数,形式参数表示的是函数体会用到的数据,它的作用是用来接收实际参数的,那么实际参数是怎么传给形式参数的呢?这里暂且只介绍值传递,一般情况下我们会在main函数中定义实际参数然后在调用函数的时候把实际参数传给形式参数,此时需要注意的是main函数定义的实际参数和函数中的形式参数是两个不同的变量,它们存放的内存空间也不同,所以再传参是实际上是把实际参数的数值传给了形式参数,这一个过程就叫做值传递,对于值传递形式参数的改变是不会影响到实际参数的。如果函数不需要接受参数时可以把形式参数设置为void,实际参数和形式参数是有一定的对应关系的:1、实参和形参的类型必须匹配;2、传进函数的参数必须和定义的形式参数个数相同;3、传参的顺序要一 一对应;
形参的写法:
数据类型 形参变量名1,数据类型 形参变量名2 … …
在定义形参变量是要注意:形参变量必须明确指定类型,而不能写成:
int max(int x, y),这里形参y并没有指定类型。

函数体代码,函数体代码就是函数要实现功能的那一部分代码,在编写函数体代码实现功能时要尽量保证函数功能的单一性。

**函数定义的位置,**函数定义的位置有两种:1、定义在main函数之前;2、定义在main函数之后;如果函数定义在main函数之后,需要在使用(函数调用)前作函数声明,函数声明就是函数头+分号。

下面用判断一个数是否为素数的例子来说明函数的定义到调用最后实现功能过程;

#include <stdio.h>


int isPrimeNum(int num)
{
	int i = 2;
	int flag = 1;

	for (i = 2; i < num; ++i)
	{
		if(num % i == 0)
		{
			flag = 0;
			break;
		}
	}

	return flag;
}

int main(void)
{
	int num = 0;

	scanf("%d", &num);

	if(isPrimeNum(num))
	{
		printf("%d is primenumber\n", num);
	}
	else
	{
		printf("%d is not primenumber\n", num);
	}

	return 0;
}

这个函数实现了在键盘上输入一个数据并判断该数据是否为素数,其实这个功能可以直接在main函数中实现转换成函数的方式实现可以让代码看起来更加整洁,在后期维护的时候也更加方便。

函数的调用关系

函数的调用关系中只有调用者和被调用者的关系,对于main函数它是整个程序的入口所以它只能是调用者别的函数不能去调用main函数,但是对于其他的函数调用者和被调用者的关系是相对的,一个函数可以去调用别的函数也可以被别的函数去调用。例如:

int isLeapYear(int year)

getMonthDays(int year, int month)
{
	isLeapYear(int year);
}

main()
{
	getMonthDays(year, month);
}

这里main函数去调用getMonthDays函数而getMonthDays函数去调用isLeapYear函数,从这里就可以看出函数调用和被调用的关系是相对的。
还需要注意的是:函数是不支持嵌套定义的,但是支持嵌套调用。

函数名代表的是函数的入口地址,在调用的时候可以通过函数名来查找相关函数,那么CPU在执行别的函数的时候再回到main函数CPU是怎么知道从哪一个位置继续往下执行的呢?
其实是这样的当要执行别的函数跳出main函数前做了一个保护现场的动作,当执行完别的函数的时候再回到main函数时恢复现场CPU就能继续执行下去了。那CPU是怎么保护现场的呢?其实是通过栈(是一块内存空间)这种数据结构来保护现场的,在调用别的函数时会把main函数的数据进行压栈再恢复现场时只需要把数据出栈就可以了,上述过程对于其他的函数也适用,如果存在多个函数嵌套调用时,在调用下一个函数前都会把当前函数的数据进行压栈,直到被调用的最后一个函数执行完之后,会把数据出栈利用栈先进后出的特点先进栈的数据会在后面出栈,这特点就能够让程序正确运行,能让现场得到正确的恢复。
栈也是有大小的默认情况下是8M大小但是可以修改,如果一致往栈上放数据直到栈满程序会报段错误。
C语言程序把内存划分了5个区域:
1、栈,主要用来存放自动变量或函数调用的数据 ;
2、堆的特点是:空间大,堆上的空间需要手动申请手动释放 ;
3、字符串常量区,这片内存是只读的;
4、静态区(全局区)用来存放全局变量和静态变量 ;
5、代码区这片区域也是只读的 ;

递归

递归是一种特殊的函数嵌套调用和循环,解决递归问题的思路:要求解决问题n就必须依赖于问题n-1的解决;
递归代码实现思路:
递推关系怎么从问题n到问题n-1再从为题n - 1然后怎么由问题n - 1返回给问题n实现回归的过程,递归对于我来说还是会有一些难理解但是通过话递归展开图能让我清楚的了解递归的过程,下面以一个例子来说吧:
1、递归求1~n的和;

#include <stdio.h>

int sumR(int n)
{
	if(n == 1)
	{
		return 1;
	}
	else
	{
		return sumR(n - 1) + n;
	}
}

int main(void)
{
	int n = 0, sum = 0;
	scanf("%d", &n);

	sum = sumR(n);

	printf("sum = %d\n", sum);

	return 0;
}

以求1~5的和为例,5传进sumR函数5不等于1会执行else里的语句sumR(5 - 1) + 5,此时调用了sumR函数但参数为4再一次进行判断4不等于1执行else里的语句sumR(4 - 1) + 4,这里再一次调用了sumR函数但参数为3再一次进行判断3不等于1执行else里的语句sumR(3 - 1) + 3,这里再一次调用了sumR函数但参数为2再一次进行判断2不等于1执行else里的语句sumR(2 - 1) + 2,这里再一次调用了sumR函数参数为1,1不等于1执行往上一级返回1,这里的返回是往上一级返回而不是作为最终结果返回,1返回给sumR(1)然后1 + 2作为返回值返回给sumR(2),1 + 2 + 3作为返回值返回给sumR(3),1 + 2 + 3 + 4作为返回值返回给sumR(4),最后返回最终结果1 + 2 + 3 + 4 + 5;

在进行函数传参时数组也可以作为函数参数,数组作为参数总共有两种情况:
1、数组元素作为函数参数 ;
2、数组本身作为函数参数 ;

在一维整型数组本身作为函数的参数时,可能会出现的问题:

int printArray(int a[])
{
	int len = sizeof(a) / sizeof(a[0]);
	int i = 0;
	
	for(i = 0; i < len; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main(void)
{
	int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	printfArray(a)
	return 0;
}

上述程序的输出结果是1 2,原因是printArray(int a[])的形参int a[]在编译时编译器会把它识别为
int *a这是一个指针,指针在64位系统中的大小是占8个字节,所以在计算len时8/4=2,最终的打印结果是1 2,通过上述情况可以知道在把数组作为实参传给函数是同时也要把数组的长度传给函数用作函数中对数组元素的调用。对于一维数组:
int a[10];
数组名 代表类型 —int[10] 这种数组类型 ;
数组名 代表的值 —首元素的地址(数组所占内存空间的首地址) ;
下面以数组作为参数传给函数的方式实现冒泡排序来看程序执行的效果吧:

在这里插入图片描述
程序输出结果:
在这里插入图片描述
其实从输出结果上看跟在main函数上实现的冒泡排序没有什么区别,只是通过函数的方式去实现且以数组作为参数,但是数组是用作处理大批数据的在我们需要传大量的数据的时候相比与传多个值我只传一个数组就能解决问题这样效率会高得多,不过也要具体问题具体分析,今天先到这了明天还会继续更新的!

标签:调用,函数,int,sumR,C语言,数组,main
From: https://blog.csdn.net/attitude_lu/article/details/140851967

相关文章

  • 【C语言】程序环境,预处理,编译,汇编,链接详细介绍,其中预处理阶段重点讲解
    目录程序环境翻译环境1.翻译环境的两个过程2.编译过程的三个阶段 执行环境 预处理(预编译) 1.预定义符号2.#define 2.1用#define定义标识符(符号)2.2用#define定义宏 2.3#define的替换规则 2.4#和##的用法2.5宏和函数2.6#undef3.命令......
  • 6.C基础_输入输出函数
    putchar功能:输出一个字符函数声明:intputchar(intc);返回值:参数c的ASCLL码值c:要输出的字符,可以为字符常量、字符变量或表达式注意点:输出的结果不带'\n'getchar功能:从键盘读一字符函数声明:intgetchar(void);返回值:获取数据的ASCLL码值,当输入ctrl+d时会退出获取,此......
  • C语言指针与数组
    在上一篇对指针介绍的文章当中,我们初次了解到了指针,并且知道了地址和内存间的关系,懂得了如何取地址和对指针的解引用,算是对指针有了一个初步的了解。而今天让我们对指针进行更深一步的了解吧~一、指针与数组名我们知道,指针变量是一个用来存放地址的变量,比如我们定义一个整形......
  • 从汇编层面看c/c++函数调用过程
    函数调用分析前置知识:全局变量:在函数内部定义的变量局部变量:在函数外部定义的变量esp:存储当前函数栈底的地址ebp:存储当前函数栈顶的地址对于函数形参(实际上):简单:cpu寄存器中复杂:栈中开空间函数调用机制:局部变量占用的内存是在程序执行过程中“动态”地建立和释放的......
  • 嵌入式软件--C语言高级 DAY 8.5 相关函数
    递归函数在嵌入式中应用不常见,但对于学习C语言的我们,也要时刻记得它的作用和用法。此外还要记住sprintf尤其重要!还有时间戳!一、递归函数1.概念一个函数在函数体内又调用了本身。但必须满足两个条件:具有明显的结束条件;趋近于结束条件的趋势。2.递归原理#include<stdio.h>......
  • 嵌入式软件--C语言高级 DAY 7数组
    一、概念数组array:是多个相同类型数据按一定顺序排列的集合,并使用一个标识符命名。并通过编号(索引,亦称为下标或角标)的方式对这些数据进行统一管理。数组的长度=元素的个数标号角标是从0开始。二、define_array.c定义数组的三种形式:1.定义数组,可以先确定数组的元素个......
  • 函数指针和指针函数的使用
    指针函数1:本质函数,返回值为指针1.2:格式:数据数据*函数名(形参){函数体return地址;//失败一般会返回NULL}#include<stdio.h>#include<stdlib.h>char*yue(){//chara[32]="hello";//栈区,函数调用结束后空间被释放//char*s="hello";//常量区,不会被释......
  • C语言数据在内存中的存储超详解
    文章目录1.整数在内存中的存储2.大小端字节序和字节序判断2.1什么是大小端?2.2为什么会有大小端?2.3练习3.浮点数在内存中的存储3.1一个代码3.2浮点数的存储3.2.1浮点数存的过程3.2.2浮点数取的过程3.3题目解析1.整数在内存中的存储在操作符......
  • C语言自定义类型结构体与位段超详解
    文章目录1.结构体类型的声明1.1结构体声明1.2结构体变量的创建和初始化1.3结构体的特殊声明1.3结构体的自引用2.结构体内存对齐2.1对齐规则2.2为什么存在内存对齐2.3修改默认对齐数3.结构体传参4.结构体实现位段4.1什么是位段4.2位段成员的内存......
  • rows和range的区别--窗口函数
    目录1.rows和range关键字的区别2.例题:2.1 求最近三个月(前两个月和当前月)的累计销量2.2 计算最近3个月(前两个月和当前月)的累计销量1.rows和range关键字的区别rows和range关键字,都可以用来定义windowframe范围:rowsbetween上限and下线rangebetween上限and......