C语言学习笔记08-函数、变量作用域{}

C语言自定义函数

函数 / 方法(面向对象语言),将功能重复的代码段(原始的方式是cv)提取抽象出main方法外,将其放入一个新的函数中,从而在main中只需要调用该函数就可完成一项复杂的工作,代码会更清晰简洁。之前我们使用的printf、scanf等都是库函数(linux系统使用man命令可以查看函数如何使用,windows系统可以网络查询),我们自己也可以自定义函数。初学者可以把函数想像成数学中的y=f(x),但其实在编程中还是有区别的。

补充说明一点:函数在C语言中默认是外部的,跨文件调用函数只需要声明即可,或者声明在一个头文件中,main文件声明调用该头文件也行。(具体演示见下文)需要函数只在内部起作用加static修饰。

1. 变量作用域(有点类似定义域)

局部 / 本地(local)或自动(生存期、作用域) 变量:写在一对大括号内——块(函数块、语句块),需要注意变量的块内外重定义和同名问题(外面的在里面仍有效,里面的重定义同名的会掩盖外面的,即变量在生存期内不一定起作用)。
不同块中可以定义同名变量,同一个块中不能重复定义。

全局(global)变量:与main并列的。不做初始化默认被初始化为0或NULL。优先级低于局部变量。
全局变量的初始化在main之前,因此必须用编译它时已知的量(常量)初始化!

关键字 extern (外部external链接属性)用于声明同一个项目不同源文件的全局变量(外部变量)。关键字 static 静态本地变量-则使全局变量不能跨文件调用,但static可以使局部变量出了局部块不被销毁(可以保留变量的值,重新进入函数后用上一次的结果计算)【内存的栈、堆、静态区】。(生存期、作用域可以不一致,注意区别)

【局部变量(生命伴随当前函数终始)存储在栈区。
全局变量、静态变量(全局静态当前源文件可访问,局部静态当前函数可访问)存储在全局区(生命伴随一个程序终始)。】

java中的{}表示优先执行,static{}表示静态代码块;全局变量是定义在类内方法外,局部变量是定义在方法内,访问权限(可见性) +public -private #protected default

在这里插入图片描述
在这里插入图片描述

  dev创建项目——分为空项目(手动搭建)、控制台(自带main函数)、窗口应用(窗体)等,此处创建空项目。新建源文件、头文件等,头文件用来存放所有对外公开的函数原型和全局变量的声明
注意不同的头文件定义的宏名称要保证不同,因此需采用一定的规范。
在这里插入图片描述
在这里插入图片描述

调用test1.c的变量a:

在这里插入图片描述
在这里插入图片描述

具体细节需要了解编译过程中链接环节的属性(外部、内部)等。

2. 自定义函数

函数用于接收一个或多个参数,做一件事,并返回零个或一个值。比如:int main(int argc, char *argv[]) —— 第一个参数存放参数个数,第二个参数存放参数,并且argv[0]是可执行文件路径或符号链接,命令行中执行时可以在后面增添新的参数。main函数最后return 0 表示程序正常退出(Windows借助main返回值不同做批处理;Unix Bash: echo $?、Csh: echo $status查看return值)。
C语言不允许重复定义同名函数(哪怕参数不同);C++、java等面向对象语言支持方法的重载。【因为函数名代表了一块指定的内存空间的首地址】

2.1. 函数定义和调用

定义(函数头+体):<返回类型> <函数名> (入参表, <参数类型> (形式)参数名) {函数体(最后可能需要返回值);}

调用:
函数名(按照类型顺序和数量给出参数值, ); 【()必须加,即使函数不需要入参。不加()表示的是什么?可以联系指针的知识思考】
函数入参值可以是字面量、变量、函数返回值、 计算结果【(a, b) 与 ((a, b))】。
函数return返回的是调用位置的下一处语句。(return讲解参见C语言学习笔记07)

注意:
(1)函数的参数类型和所附值需要匹配。C语言最大的漏洞就是在此处会悄悄帮你把不匹配的类型自动转换好(田螺姑娘)造成数据损失,导致结果可能非你所望。后续的C++/java在这方面则很严格。
(2)传入函数的参数值是一个单纯的值(C语言调用函数时永远只能传值给函数)。
(3)每个函数有自己的变量空间,参数也位于该独立空间中(本地变量),和其它函数没有关系。【除非运用数组、指针】
(4)函数声明使用()表示入参未知,使用(void)真正表示无入参。

2.2. 函数的使用

函数使用前必须先声明函数原型(函数头 加 ;)(声明可以在main上方或main中调用函数前;声明可以不指定函数入参名字,编译器只判断类型是否一致,但为保持可读性最好与定义时的参名一致)或写在主函数上面(C编译器是自上而下顺序分析代码),否则能否编译具体看编译器。
return可以停止函数并送回一个值:return; 或 return 表达式; 。一个函数可以使用多个return语句,不必在最后使用(if),但不符合单一出口。

函数返回值可以赋值给变量。也可以给函数(嵌套),甚至可以丢弃(使用函数附带作用而非结果)

函数的定义不能嵌套!!!

2.3. 标准样例

统计素数并求和——

#include <stdio.h>

//自定义函数声明
void swap(int *a, int *b); //swap(&a, &b)进行两数交换 C语言函数只能值传递 
int isPrime(int n); //判断 素数/质数 函数 return 0 1

/* ***主函数*** */
int main() {
	int x, y;
	printf("输入两个自然数,统计两数间(含这两个数)的素数个数并求和。\n");
	scanf("%d %d", &x, &y);
	while (getchar() != '\n') {;}
	
	if (x > y) {
		swap(&x, &y);
	}
	
	int cnt = 0, sum = 0;
	int i;
	for (i = x; i <= y; i ++) {
		if (isPrime(i)) {
			cnt ++;
			sum += i;
		}
	}
	printf("[%d,%d]内素数个数是%d,这些素数的和是%d。\n", x, y, cnt, sum);
	
	return 0;
}



/*
* *自定义函数功能定义*
*/

//形参(x, y)使用实参值副本,实参值不会变 利用指针值交换(地址交换)实现真正交换
//int *p = &i 将i的地址赋给指针p p指向整型
//int* p,q与int *p,q意义一样:声明指针p和普通整型q
void swap(int *a, int *b) {
	// * 取出内容  *a是值
	int t = *a;
	*a = *b;
	*b = t;
}

//素数或质数:指在大于1的自然数中,除了1和本身外没有其它因数的数。
int isPrime(int n) {
	int flag = 1;
	int i;
	if (n < 2) flag = 0;
	for (i = 2; i < n; i ++) {
		if (n % i == 0) {
			flag = 0;
			break;
		}
	}
	return flag;
}

在这里插入图片描述

自定义函数声明和定义写在下面例子的地方也可以,总之需要先声明或定义了才能调用:

#include <stdio.h>

void cheer() {
	printf("Cheers!\n");
}

/* Main */
int main() {
	//cheer(); //cheer;
	
	int max(int a, int b);
	int a, b, c, m;
	a = 5, b = 6, c = 2;
	m = max(max(a, b), c);
	printf("%d\n", m);
	
	return 0;
}



int max(int a, int b) {
	int ret; //单一出口
	if (a > b) {
		ret = a; //return a;
	}
	else {
		ret = b; //return b;
	}
	
	return ret;
}

3. C语言函数定义的补充说明

如果没有预先声明或定义函数就调用,C语言编译器是会根据函数调用值的类型先猜测函数的样子。如main中调用未知的sum(1,10),C语言会猜测在后面可能会存在有sum的定义如下: int sum(int , int) ,如果后面实际定义不匹配编译器不会完成编译同时会报错。
因此还需要注意定义时不要采用void swap(),而使用void swap(void)(确定函数不需要参数)或void swap(double , double)(确定函数所需参数类型)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值