C中的函数

函数

C语言没有类,因此封装完全依赖函数,C语言自带的标准库、第三方库都是以函数方式提供的,C不会自动将函数定义提升,c标准要求在定义前使用必须先声明,这也是产生头文件的原因。

主函数

对于面向过程的C语言,程序的入口是main函数,不同的编辑器生成的main函数也不同,例如eclipse CDT为:
int main(void)
{
return EXIT_SUCCESS;
}

dev c++为:
int main(int argc, char *argv[])
{
return 0;
}
eclipse CDT默认生成的main()不接受任何参数,dev c++生成的main()可以接受任意个数的参数,eclipse CDT自动导入stdlib.h,EXIT_SUCCESS是在stdlib中定义的常量,值为0。返回0表示成功退出,不为0表示返回异常退出编号,在main中不添加return也可以通过编译,编译器会自动添加return语句,关于如何访问main()函数的参数将在指针章节讲述。

函数定义

一个标准函数定义格式如下:
int fun(int a, int b)
{
return a+b;
}
与其他语言一样,如果函数没有返回值,则返回类型为void,无需return,例如:
void fun()
{
}
没有参数也可以写成fun(void),即使没有返回值也可以通过单独的return退出函数。

函数参数

虽然C语言没有对象的概念,但有指针,因此参数也分为形参和实参,实参按值传递,形参传递内存地址,也就是指针。对于数组来说,参数可以使用数组名也可以是指针,例如:
#include<stdio.h>

int a[3]={1,2,3};

void fun(int a[3]) //也可以是a[]
{
printf("%d%d%d\n",a[0],a[1],a[2]);
}

void fun1(int* b)
{
printf("%d%d%d\n",b[0],b[1],b[2]);
}

int main()
{
fun(a);
fun1(a);
return 0;
}
这里的数组名是形参而不是数据集合,加上元素个数是为了代码更加清晰,虽然名称相同但可以进行自增自减操作,如果是二维数组,使用数组名时编译器需要知道数组的列数,例如数组为arr[3][2],参数需要写成arr[3][2]或int arr[][2]。

函数返回值

虽然可以在一个函数中创建数组这样的局部栈内存,但不应该使用外部变量引用它,也不要用return返回它,因为栈空间在函数运行结束后会释放,如果用外部变量引用了局部创建的数组,栈空间释放后可能会导致无法访问,而且栈空间重新分配时数组会遭到破坏,因此编译器在外部变量引用局部数组或者发现返回局部数组时会给出警告,但使用外部变量引用”abc”这样的局部字符串或者返回”abc”是不会给出警告的,因为临时字符串本无论在写哪里都作为常量保存在静态域中,它不会随着栈或堆的创建和释放而增长或消亡,即无论函数运行多少次都不会重复创建,运行结束后也不会销毁。

域控制符和变量生命周期

虽然C是面向过程的语言,但变量的生命周期和面向对象语言大致设计相同,域控制符虽然没有面向对象语言多,但不可缺少。C中的变量有4个域:程序块,函数内部,文件内部,文件外部(全局)。缺省情况下:

  • 在{}中编写的代码称为程序块,其中定义的变量只能在程序块中可见
  • 在函数内部定义的变量只能在函数内部可见
  • 文件中定义的变量和函数默认是全局的,在函数内部、文件内和文件外都可以访问。

缺省情况在某些时候是不够用的,例如一个项目被分成多个文件,由于默认情况下变量和函数是全局的,当项目由多个人编写或导入第三方库时就会发生重名冲突,此时可以通过static域控制符将变量和函数设置为文件中可见,即便发生重名也不会报错,例如:
static int a; //变量a仅文件中可见
//函数fun()仅文件中可见
static void fun()(
{
puts(“fun”);
}
变量的生存周期只有两种:临时变量和静态变量,临时变量是函数中使用的局部变量,它保存在栈中,在函数运行结束后被销毁;静态变量保存在内存的静态区,在程序运行过程中一直存在。有一种方法可以使函数中的临时变量变成静态变量,那就是在函数中使用static声明变量,此时该变量变成静态变量保存于静态区,只会在函数第一次运行时初始化,在函数运行完后不销毁该变量且继续保留函数运行结束时的值,例如:
void fun()
{
static int s=1;
s++;
}
静态变量s只被赋值一次,否则就失去了保留状态的能力,虽然这种形式的变量看起来很强大,但效果和在函数外声明的全局变量没有什么不同,只是有利于封装,因为变量s只在函数中可见。文件中的变量和函数本身就是静态的,static并不影响其生命周期,只影响可见性。换句话说,对于生命周期来说函数内的static变量和函数外的变量在程序运行时一直存在,对于作用域来说函数内的static变量在函数中可见,函数外的static在文件中可见。一些高级语言将全局变量和文件内部变量称为global和intel,函数内部静态变量称为static,意思更加明确,局部变量其实也有域控制符auto,只不过默认可以省略。还有一个域控制符是register,作用是将变量定义在cpu的寄存器中,因为寄存器运行速度远远大于内存,对于频繁使用的变量能够节省读写时间,但现在的编译器都很智能,它会自动将频繁使用的变量放入寄存器中,因此register几乎闲置不用。

变量和函数声明

编译器在编译时是按从上到下的顺序进行编译的,当发现使用了未定义的变量或函数就会报错,如果变量和函数已经定义还未来得及编译,应该提前告诉编译器,否则就会报错,对于变量来说需要使用:
extern int a;
关键字extern用于来提前声明变量a,称为引用式声明,编译器读到这一行首先在文件中查找变量a的定义,如果找不到再到文件外部查找,还找不到就会报错。对于函数来说只需要书写函数的名称和参数,例如:
void fun(int a, int b);
函数声明没有函数主体,用分号结束,其实函数声明并不检查参数名称,它只检查参数类型,因此写成void fun(int aa, int bb);或void fun(int, int);都可以,为了使得代码更加清晰,通常我们按照原型书写,也方便复制。使用时可以在函数中提前声明也可以在函数外提前声明,显然在函数外提前声明要节省代码,因此大多时候我们在文件顶部使用#include指令导入第三方库的头文件。.h文件是一个集中包含库函数声明的文件,#include指令将.h中的内容原封不动导入进来,使得库函数随处可用。对于同一个文件来说,如果能够按照定义的顺序来使用变量和函数,就可以不使用提前声明,因此将全局变量放到文件顶部定义,将局部变量放在函数顶部定义,并将main()函数放到底部是一个节省代码的习惯。

需要说明的是从C99开始,允许在循环中声明变量,例如:for(int i=0;i<10;i++),如果在循环内部声明变量i,那么i只会被赋值一次,对于while循环也是这样,例如:
do
{
int i=0;//只会在初始化时赋值
i++;
}while(i<10)

函数类型

对于一个函数void fun(int a,int b),它的类型为void (int, int),函数名和参数名并不重要,编译器只关心参数类型和返回值类型。这使得指针(void *p)(int,int)可以指向类型相同函数名和参数名不同的任何函数。然而虽然void fun(int a,int b)和void fun(int a, float b)是不同类型的函数,但C并不支持函数重载,如果两个函数的名称相同编译器会报重复定义的错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值