(十)C语言的函数

一.函数的定义与使用

 函数:一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值
 一般形式:
          <数据类型><函数名称>(<形式参数说明>){
                  语句序列;
          return[(<表达式>)];
          }
#include <stdio.h>

void show()
{
 printf("hello world!");
 	
}
int main(int argc ,char *argv[])
{
  
	show();

	    
	return 0;
	
	
	
}

题目:定义求x的n次方值的函数

二.函数的声明

在C语言中,函数的定义顺序是有讲究的:默认情况下,只有后面定义的函数才可以调用前面定义过的函数。如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明。
声明格式:

返回值类型  函数名 (参数1, 参数2, ...)
#include<stdio.h>
 int sum(int a, int b);//函数声明
  
  int main()
  {
      int c = sum(1, 4);//函数调用
      printf("%d",c);
      return 0;
 }
  
 // 函数的定义
 int sum(int a, int b) {
     return a + b;
 }

如果只有函数的声明,而没有函数的定义,那么程序将会在链接时出错。

三.函数的参数传递

参数传递方式:全局变量,复制传递,地址传递
1)全局变量:函数体外说明的变量
2)复制传递(值传递):调用函数将实参传递个被调用函数,被调用函数将创建同类型的形参并用实参初始化。形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参。
3) 地址传递(指针传递):实参作为变量的地址(数组的指针),而形参为同类型的指针。被调用函数中对形参的操作,将直接改变实参的值

函数的参数,形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用

#include <stdio.h>

void swap(int *x,int *y);
int main(int argc ,char *argv[])
{ int a=10;
  int b=20;
  printf("before:%d %d\n",a,b);
  
  swap(&a,&b);
  
  printf("after:%d %d\n",a,b);
  
	
	    
	return 0;
		
	
}

void swap(int *x,int *y)
 {  int t;
    t= *x;
    *x =*y;
    *y=t;
 	
 	
 }

四.局部变量与全局变量

局部变量:变量只是在程序的局部范围内有效;
局部变量定义在那些位置:

  1. 函数的开头;
  2. 函数内的复合语句内定义;
  3. 形式参数;
  4. 函数中间(非开头);
#include <stdio.h>
void main()
{
}
void fun1(char l0)  //形式参数:只在此函数有效;
{
    char l1 = 'a';  //函数的开头:在本句以下的函数内有效;
    {
        char l2 = 'b';  //函数的复合语句内定义:只能在本复合语句内切本行以下有效;
    }
    char l3 = 'c';  //函数中间(非开头):只能在本行以下有效;
}

全局变量:变量,可以在全局范围内有意义的变量;所谓全局也并不是真正的全局,而是在定义处以下的范围内才是有效的;
全局变量定义的位置:

  1. 文件开头;
  2. 函数前;
  3. 函数后;
  4. 文件结尾;
#include <stdio.h>

char Global_1 = 'A';  //源文件开头:在此行以下的所有定义的函数都有效;
void main()
{
}

char Global_2='B';  //函数后和函数前:此全局变量在此行之前是无效,只能在此之下的函数中有效;

void fun1()
{
    char l1 = 'a';
    {
        char l2 = 'b';
    }
    char l3 = 'c';
}

char Global_3 = 'B';//程序结尾:程序结尾的全局变量是没有意义的;

全局变量与局部变量在内存中的区别:
1)全局变量保存在内存的全局存储区中,占用静态的存储单元;
2)局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。

五.指针函数与函数指针

指针函数:一个函数的返回值为地址量的函数
一般形式:

  <数据类型>*<函数名称>(<参数说明>){
      语句序列;
      }
   int main1(int x,int y);
   int* main1(int x ,int y);//()的优先级大于*,先是函数,返回一个指针

返回值:全局变量的地址/static变量的地址/字符串常量的地址/堆的地址,不可返回局部变量的地址

函数指针:指向函数的指针变量
通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针类型的声明:

 <数据类型> (*<函数指针名称>)(<参数说明列表>)
int main1(int x,int y);
int (*p)(int x,int y );//()的优先级大于*,先是指针,指向一个函数

以下实例声明了函数指针变量 p,指向函数 max:

#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    int a, b, c, d;
 
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
 
    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
 
    printf("最大的数字是: %d\n", d);
 
    return 0;
}
请输入三个数字:1 2 3
最大的数字是: 3

六.递归函数

递归函数:一个函数的函数体中直接或间接调用该函数本身,分递推阶段和回归阶段

C 语言支持递归,即一个函数可以调用其自身。但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等

void recursion()
{
   statements;
   ... ... ...
   recursion(); /* 函数调用自身 */
   ... ... ...
}
 
int main()
{
   recursion();
}

七.空指针与野指针

空指针:声明指针后,让它指向空(如0,NULL)表示没有指向任何地址。
后果:如果对空指针解引用,程序会崩溃
规避空指针
1)如果对空指针使用delete运算符,系统将忽略该操作,不会异常。所以内存被释放后,也应该把指针指向空。
2)在函数中,应该有判断形参是否为空指针的代码,保证程序的健壮性。

int* p=0;
delete p;

野指针:指向的不是一个有效(合法)的地址
指针变量也是变量,是变量就可以任意赋值。但是,任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。野指针不会直接引发错误,操作野指针指向的内存区域才会出问题。

int a = 100;
int *p;
p = a; //把a的值赋值给指针变量p,p为野指针, ok,不会有问题,但没有意义

p = 0x12345678; //给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义

*p = 100;  //对野指针进行赋值操作就不可以了

野指针的成因
1)指针未初始化:指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它所指的空间是随机的
2.)指针越界访问:指针指向的范围超出了合理范围,或者调用函数时返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放
3 )指针释放后未置空:有时指针在free或delete后未赋值 NULL,便会使人以为是合法的。其实它们只是把指针所指的内存给释放掉,但并没有把指针本身忘记。此时指针指向的就是无效内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”

int main()
{
	int * p;//指针变量有操作系统随机赋值,未指向一个具体空间
	*p = 20;
	//改后:
	//int *p =NULL;
	//int a = 10;
	//p = &a;
	

    int arr[10] = {0};
	int *p = arr;
	for(int i = 0; i <= 11; i++)//i<=10
	{
		*(P++) = i;//当指针指向的范围超出数组arr的范围,p变成野指针。
	}

    int *p = NULL;
	p = malloc(10 * sizeof(int));
	if (!p)
	{
		return;
	}
	//p = NULL;避免野指针
	free(p);
	


	return 0;
}

规避野指针
1)初始化指针
2)避免指针越界
3)开辟的指针释放后置为NULL
4) 避免返回局部变量的地址

int * t1()
{
	int a = 20;
	return &a;
}
int main()
{
	int *p = NULL;
	p = t1();
	printf("%d\n", *p);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值