C语言复习整理(十)——函数与变量存储类型

函数

模块化设计时需要遵循模块独立性的原则。即模块之间的关系应尽可能的简单:
(1)一个模块只能完成一个特定的功能。
(2)模块之间只能通过参数进行调用。
(3)一个模块只有一个入口和一个出口。

函数的分类:
(1)从用户使用角度:库函数与用户自定义函数。
(2)从函数参数传送的角度:有参函数与无参函数。
(3)从函数的使用范围角度:内部函数和外部函数。

C语言规定,程序中使用到的所有函数,除编译系统提供的标准库函数外,其他的函数必须“先定义,后使用”。(所有函数的定义是并列的、独立的,各个函数之间没有嵌套或者从属关系)一般形式如下:
类型名 函数名(形成那类型说明表)
{
说明语句
执行语句

函数定义的一些要求:
(1)函数名必须是合法且不重名的标识符。
(2)类型名指定函数返回值的类型,如果定义函数时不指定函数类型,系统会隐含指定函数类型为int型。无返回值的函数的类型名应指定为void空类型。
(3)形参说明表即形参列表,一般形式为(数据类型名1 形参名1,…,数据类型名n 形参名n)。如果是无参函数,形参列表可以省略。
(4)说明语句和执行语句合在一起称为函数体。函数体可以暂时没有内容,此时表示占一个位置,实现一定的功能,以后加以补写。

swap()
{}

这是最简单的一个C合法函数,不执行任何操作,在程序开发过程中常用来代替尚未完全开发的函数。
(5)函数包括函数首部与函数体两部分。函数首部用来定义函数的调用规范,函数题用于定义函数要完成的工作。

函数的调用与返回值

函数调用举例:

#include <stdio.h>
int max(int x,int y)
{
	return x>y?x:y;
}
void print_max_num()
{
	printf("最大数为:");
}
int main()
{
	int a,b,m;
	scanf("%d,%d",&a,&b);
	m=max(a,b);
	print_max_num();
	printf("%d\n",m);
}
/*
运行结果为:
2,3
最大数为:3
*/

函数的返回值:指函数被调用、执行完成后返回给主函数的值。
返回语句的一般形式:return 表达式;(将表达式的值返回给主调函数)

返回语句的说名:
(1)函数内可以有多条返回语句,但每条返回语句的返回值只有一个
(2)当函数不需要返回值时,可以这样写: return ; 当函数中无返回语句是,表示最后一句执行完自动返回,相当于最后加一条:return ;
(3)无返回值的函数用void声明。但对于非‘’空类型‘’函数,如果没有指明返回值,函数执行后实际上不是没有返回值,而是返回一个不确定的值。
(4)函数可以出现多个return语句,但真正只执行到一个,无论执行到哪一个,都要返回到主调函数,并带回返回值。
(5)返回值的类型为函数的类型,如果函数的类型和return中表达式的类型不一致,以函数类型为准,先将表达式的值转换成函数类型后,再返回。

函数的参数

传值

在C语言中,实参向形参传递数据的方式是“值传递”:
(1)形参定义时编译系统并不为其分配存储空间,也无初值,只有在函数调用时,临时分配存储空间,接收来自实参的值。函数调用结束,内存空间释放,值消失
(2)实参可以是常量、变量、表达式,但必须在函数调用之前有确定的值
(3)实参与形参之间是单向的值传递。即形参的改变并不会改变实参变量的值
(4)实参与形参必须类型相同或赋值兼容,个数相等,一 一对应。

例:一维数组名作为函数参数(冒泡排序)

//冒泡排序:
#include <stdio.h>
int sort(int arr[10],int cnt)
{
	int i,j,t;
	int flag=1;
	for(i=0;i<cnt-1&&flag;i++)//注意这里只需要i<cnt-1,因为每次是两个数作比较,只需要循环cnt-1次
	{
	    flag=0;//如果一趟比较都没有交换发生,说明已经有序
	    for(j=0;j<cnt-i-1;j++)//每执行一次循环把最大的数放在最后,就会少一个数参与比较
	    {
	        if(arr[j]>arr[j+1])//如果前面的变量大于后面的,交换位置
	        {
	            t=arr[j];//用中间变量交换数组两个位置的值
	            arr[j]=arr[j+1];
	            arr[j+1]=t;
	            flag=1;//交换发生,flag置为1
	        }
	    }
	}
}
int main()
{
    int a[10]={11,2,6,55,-33,104,88,-2,40,5};
    sort(a,10);
    for (int q = 0; q < 10; q++)
		printf("%d  ", a[q]);
}
// 输出结果:-33  -2  2  5  6  11  40  55  88  104  

例:二维数组传参(求最大值)

#include <stdio.h>
int max(int arr[][4])
{
	int i,j,max_num=arr[0][0];
	for(i=0;i<3;i++)
	{
	    for(j=0;j<4;j++)
	    {
	        if(max_num<arr[i][j])
	        max_num=arr[i][j];
	    }
	}
	return max_num;
}
int main()
{
    int m,a[3][4]={11,2,6,55,-33,104,88,-2,40,5,22,127};
    m=max(a);
	printf("%d ",m);
}
//输出结果:127

引用传递

值传递方式不会改变实参的值,如果想要通过函数改变实参的值,就要用到引用方式。(最根本的原理是引用方式可以从地址层面,对地址里面存的值进行操作,传参传的是地址)
例:

#include <stdio.h>
void swap(int *p,int *q)
{
    int a;
    a=*p;
    *p=*q;
    *q=a;
}
int main()
{
    int m=1,n=2;
	printf("%d %d\n",m,n);
	swap(&m,&n);
	printf("%d %d\n",m,n);
}
/*输出结果为
1 2
2 1
(通过交换*p,*q实现了m,n的交换)
*/
//如果将swap函数改为
void swap(int *p,int *q)
{
    int *a;
    a=p;
    p=q;
    q=a;
}
/*输出结果为
1 2
1 2
(只实现了指针的交换,未实现m,n的交换)
*/

递归调用

汉诺塔问题:

#include <stdio.h>

// 将 n 个盘子从 x 借助 y 移动到 z
void move(int n, char x, char y, char z)
{
    if (1 == n)
    {
        printf("%c-->%c\n", x, z);
    }
    else
    {
        move(n - 1, x, z, y);      // 将 n-1 个盘子从 x 借助 z 移到 y 上
        printf("%c-->%c\n", x, z); // 将 第 n 个盘子从 x 移到 z 上
        move(n - 1, y, x, z);      // 将 n-1 个盘子从 y 借助 x 移到 z 上
    }
}
int main()
{
    int n;
    printf("请输入汉诺塔的层数: ");
    scanf("%d", &n);
    printf("移动的步骤如下: \n");
    move(n, 'X', 'Y', 'Z');
    return 0;
}

变量名的作用域

按照变量的作用范围分为:局部变量与全局变量。
局部变量:不同函数中定义的变量,其作用范围都限定在各自函数内,即使用相同的变量名也不会相互干扰影响。主函数中定义的变量也是局部变量。形参也是局部变量。(编译时,编译系统不会给局部变量分配内存,而是在程序运行中,当局部变量所在函数被调用时,编译系统才根据需要临时分配内存。调用结束,内存空间释放
全局变量:在所有函数(包括main函数)外定义的变量即为全局变量。全局变量存放在静态存储区中,作用域是从定义的位置开始到本源文件的结束。对于全局变量,如果在定义是不初始化,系统会自动赋初值,对数值型赋值0,对字符型赋值‘\0’。
(全局变量的缺点:(1)全局变量作用范围大,为此要付出其长时间占用存储单元的代价,在程序的全部执行过程中都占据着存储单元。(2)降低了函数使用的通用性和安全性。)

变量的存储类型

C语言有五种存储类型的变量:自动局部变量,静态局部变量,寄存器变量,静态全局变量和外部变量。
内存中供用户使用的存储空间可分为程序区、动态存储区和静态存储区。程序区用于存放程序代码;动态存储区和静态存储区用来存放数据(动态和静态存储区中的变量生存周期不同。)

静态存储方式,在编译时就分配了存储空间。在整个程序运行期间,一直占有固定的存储空间,程序结束后,释放存储空间。这类变量生存期为整个程序。静态局部变量和全局变量都存放在静态存储区中。
动态存储方式,在程序运行过程中,只有当变量所在函数被调用时,编译系统才会临时为该变量分配一段内存单元。函数调用结束后,所占空间释放,变量值消失。这类变量生存期仅在函数调用期间。

static定义的局部变量是静态局部变量。静态局部变量的初始化只在编译时进行一次,程序运行期间不再重新进行初始化。(在下一次函数调用时,静态局部变量的值是上一次函数调用结束时保存的值)
例:

#include <stdio.h>
int n=1;
void fun()
{
    static int a=2;
    a+=2;
    ++n;
    printf("fun:n=%d,a=%d\n",n,a);
}
int main()
{
    static int a;
    printf("main:n=%d,a=%d\n",n,a);
    fun();
    a+=10;
    printf("main:n=%d,a=%d\n",n,a);
    fun();
    printf("main:n=%d,a=%d\n",n,a);
}
/*输出:
main:n=1,a=0
fun:n=2,a=4
main:n=2,a=10
fun:n=3,a=6
main:n=3,a=10
*/
自动变量

不加关键字static的局部变量都属于自动变量,此外还可以使用auto作自动类型说明。

auto int a;	等价于  	int a;
auto float b;	等价于 	float b;

例:

#include <stdio.h>
int n=1;
void fun()
{
    auto int a=2;
    a+=2;
    ++n;
    printf("fun:n=%d,a=%d\n",n,a);
}
int main()
{
    int a;
    printf("main:n=%d,a=%d\n",n,a);
    fun();
    a+=10;
    printf("main:n=%d,a=%d\n",n,a);
    fun();
    printf("main:n=%d,a=%d\n",n,a);
}
/*输出:
main:n=1,a=0
fun:n=2,a=4
main:n=2,a=10
fun:n=3,a=4
main:n=3,a=10
*/

自动变量注意:
(1)复合语句中说明的变量及函数的形参变量均属于自动局部变量。
(2)全局变量不能是自动变量。
(3)若不对自动变量赋值,则其值是随机的。(C语言执行时会主动为变量开辟存储空间,但不会主动为变量赋值,如果地址里面有值,则自动变量的值就是这个地址里面已经有的值

寄存器变量

用的不多,后续在整理。

静态全局变量和非静态全局变量

全局变量:可以用static限定全局变量,将变量的作用域限制在本文件内。静态全局变量禁止在其他文件中访问。没有static修饰的就是非静态全局变量,可以在其他文件中进行访问(其他文件中用extern修饰(继承)即可)。
非静态全局变量不是存储在动态存储区,而是存储在静态存储区
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值