C--C语言的函数

一、函数的声明与定义

1、函数的声明

(1)函数声明的一般形式

返回类型 函数名 (形参列表)

(2)举例

//
void swap(int a, int b);
//

函数的形参列表中每个形参的标识符可省

//
void swap(int, int);
//

2、函数的定义

(1)一般形式为

//
存储类说明符(可缺省) 返回类型 函数名 (形参列表) 复合语句
//

注:如果缺省存储类说明符,则默认为extern。extern存储类说明符表示一个函数具有外部连接;static存储类说明符表示一个函数具有内部连接。

 

二、函数调用与实现

1、实参形参的区别

(1)实参是属于函数调用者的对象,然后传递给函数被调者;而形参是属于函数被调者的。

2、函数的栈空间

(1)对于大部分处理器以及操作系统环境来说,每个函数都具有⾃⼰独⽴的上下⽂存储空间,此存储空间往往是栈式存储的,所以又被称为栈(stack)空间。相应地,⼀般处理器会有⼀个专⽤的栈指针寄存器(Stack Pointer Register,⼀般简称为SP)⽤于存放当前函数所属的栈空间的地址。整个过程都是通过压栈(push)操作和出栈(pop)操作完成每个函数存储管理

3、函数的参数传递与返回

(1)被调函数的形参与调⽤者的实参其实是两个不同的对象。函数调⽤者的实参在⾃⼰的栈空间内,⽽在传递给被调函数时,是将保存在⾃⼰栈空间的对象做压栈处理,拷贝到被调函数可访问的栈空间区域,尽管这部分区域仍然属于函数调⽤者。

(2)下面的例子,明显看出形参和实参是两个不同的对象。即便在Foo函数中任意修改形参a的值都不会影响main函数中x对象的值。

//
#include <stdio.h>
static void Foo(int a)
{
// 这⾥将输出:a address is: 00007FFF5FBFF7EC
printf("a address is: %.16tX\n", (uintptr_t)&a);
a += 10;
// 这⾥将输出:a = 110
printf("a = %d\n", a);
}
int main(int argc, const char * argv[])
{
int x = 100;
// 这⾥将输出:x address is: 00007FFF5FBFF80C
printf("x address is: %.16tX\n", (uintptr_t)&x);
Foo(x);
// 这⾥将输出:x = 100
printf("x = %d\n", x);
}
//

4、通过形参修改实参的值

(1)通过形参修改实参的值的办法就是利用指针

(2)举例做对比

a、没用到指针

//
#include <stdio.h>  
  
void swap(int a, int b)  
{  
    int tmp = a;  
    a = b;  
    b = tmp;  
}  
  
int main()  
{  
    int num0 = 3;  
    int num1 = 5;  
    swap(num0, num1);  
    printf("num0 is : %d\n", num0);  
    return 0;  
}  
//输出结果是 num0 is : 3

b、用到指针

//
#include <stdio.h>  
  
void swap(int *a, int *b)  
{  
    int tmp = *a;  
    *a = *b;  
    *b = tmp;  
}  
  
int main()  
{  
    int num0 = 3;  
    int num1 = 5;  
    swap(&num0, &num1);  
    printf("num0 is : %d\n", num0);  
    return 0;  
}  
//输出结果是 num0 is : 5

 

三、数组类型作为函数形参

1、如果⼀个函数的形参是⼀个数组类型的对象,那么它会被调整为指向该数组元素类型的指针,同时如果类型还有限定符(⽐如const、volatile),那么可以在表⽰数组对象的[]⾥添加。如果在[]中含有static关键字,那么实参必须确保⾄少能访问该形参所指定元素个数的元素数量。

 

四、带有不定参数类型及个数的函数声明与调⽤

1、C语⾔函数的形参类型列表的最后可以带有不定参数类型及个数的形参列表,⽤(,...)来表⽰。C语⾔标准明确规定,含有不定参数个数的形参列表中,必须要有⼀个确定的命名形参,并且...后不能再跟其他形参。错误示例如下:

//
// 错误!在 ... 之前必须⾄少要有⼀个命名形参
void Func1(...);
// 错误!在 ... 之后不能再跟任何形参
void Func2(int a, ..., int b);
//

 

五、函数的递归调用

 

1、定义

函数f(x) = x * f(x - 1),我们称f(x)为⼀个递推⽅程。如果把它映射到C语⾔中,那么函数f(x)就是递归调⽤的,也就是在计算这个函数的时候借⽤了该函数本⾝。

2、示例与流程图析

//
#include <stdio.h>

static void Func(int n)
{
    // 如果n等于0,则直接返回
    if(n == 0)
    {
	puts("last level!");
	return;
    }
    // 打印当前形参n的值,以确定现在是第⼏层递归调⽤
    printf("n = %d\n", n);
    // 递归调⽤Func,并且将n - 1作为实参传⼊
    Func(n - 1);
    puts("call over");
}

int main(int argc, const char * argv[])
{
    Func(3);
}
//
//运行结果
n = 3
n = 2
n = 1
last level!
call over
call over
call over
//

 

 

 

六、内敛函数

1、inline是函数说明符,表明该函数是内联函数,该关键字告知C编译器对该函数调用尽可能快。

 

七、函数的返回类型与无返回类型

1、在C语⾔中,函数的返回类型⼏乎可以是任意类型,包括整型、浮点型等基本类型,枚举、结构体、联合体等⽤户⾃定义类型,也可以是指向上述这些类型的指针,但唯独不允许数组类型。除了返回类型为void的情况外,⼀个函数中的任⼀分⽀代码最终必须要触碰⼀条return语句进⾏函数返回。对于返回类型为void的函数,在其函数体结尾处会默认隐含⼀条return语句。当函数体中出现return语句时,return后⾯跟着的表达式的类型必须要与函数返回类型兼容,如果return后⾯是⼀条空表达式(⽐如直接以分号结尾),那么表⽰返回的是⼀个void表达式

 

八、指向函数的指针

1、待加

 

九、C语言中的主函数main

1、C语言中将入口函数命名为main函数。

2、示例

a、无参数

//
#include <stdio.h> 
  
int main(void) 
{ 
  printf("Hello World!\n"); 
  return 0; 
} 
//

b、有2个参数

习惯上第一个参数是整型argc,保存了外部调用命令的参数个数,第二个参数是指针数组或二级指针argv,以字符串形式保存了与argc对应的参数。argc⼀般存放执⾏当前程序时输⼊的命令字符串个数argv则存放了指向各个输⼊字符串的指针。假设我们现在对C源⽂件编译构建后,⽣成了⼀个名为test的可执⾏⽂件。那么我们在控制台中输⼊test arg1 arg2,再按回车,那么此时,main函数的第⼀个参数argc的值为3,因为test其实就属于要传⼊到argv数组的第1个参数,然后后⾯跟着2个命令⾏参数arg1和arg2;所以argv对应的实参内容为:{“test”,“arg1”,“arg2”},即由应⽤程序名与其命令⾏参数字符串所构成的数组。

//
#include <stdio.h> 
  
int main(int argc, char* argv[]) 
{ 
  int i = 0; 
  for (; i < argc; i++) { 
    printf("%s\n", argv[i]); 
  } 
  printf("Hello World!\n"); 
  return 0; 
} 
//

c、有3个参数

在argc和argv的基础上多了一个环境变量参数,环境变量的形式是“ENV=value”,参数类型是指针数组或二级指针

//
int main(int argc, char* argv[], char* envp[]) 
{ 
  int i = 0; 
  for (; envp[i] != '\0'; i++) { 
    printf("%s\n", envp[i]); 
  } 
  printf("Hello World!\n"); 
  return 0; 
} 
//

 

十、函数与函数调⽤作为sizeof操作符

 

1、C语⾔标准明确规定,sizeof操作符不应该应⽤于:①具有函数类型;②⼀个不完整类型的表达式;③访问⼀个位域成员的表达式。_Alignof操作符不应该应⽤于⼀个函数类型或不完整类型。这⾥⼤家要注意的是,当⼀个函数标志作为sizeof或_Alignof的操作数时,它不会被隐式转换为指向该函数类型的指针类型,这个与它单独⽤于其他计算表达式有所不同。因此,假定我们定义了⼀个函数:void Foo(void),那么sizeof(Foo)的结果是未定义的;⽽sizeof(&Foo)是合法的,其结果相当于sizeof(void(*)(void)),也就是⼀个指针对象⼤⼩。如果sizeof的操作数是⼀个函数调⽤表达式,那么它的结果相当于sizeof(函数返回类型),同时,作为sizeof操作数的函数调⽤将不会发⽣。由于函数返回类型不能是⼀个可变修改类型,因此这⾥不会涉及在运⾏时对可变修改类型对象所占存储空间⼤⼩的计算。

 

致谢

1、《C语言编程魔法书》作者 陈轶

2、《C语言程序设计》[第四版],作者谭浩强

3、C语言main函数的三种形式实例详解

 

 

 

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读