1.关于函数的前置声明(Forward Declaration):
前置声明告诉编译器函数定义在别处,并说明返回类型及参数(有些标准允许不声明参数)
如果函数定义在使用后,必须加前置声明;如果函数定义在使用前,可以不加前置声明
但后1种不是C语言的标准风格;main()通常只提供程序的框架,最好放在所有函数定义的前面
此外,通常把函数定义放在另1个文件中,这时需要include该文件
2.关于getchar()和putchar():
因为只处理字符,这2个函数比scanf()和printf()更简洁
并且通常这2个函数不是真正的函数,而是预处理宏
注意:
①putchar(<c>)中的变量c只能是字符或字符变量,不能是字符串,如'\n'合法但"\n"不合法
②实际上,putchar()/getchar()是将char当作int来处理,如getchar()返回的实际上是int类
型,故getchar()的返回值和putchar()的参数也可以是int类型变量或值
//实例:
#include <stdio.h>
#define SPACE ' '
int main(void) {
char ch=getchar();
while (ch!='\n') {
if (ch==SPACE) {
putchar(ch);
} else {
putchar(ch+1);//输出在ASCII码中的下1个字符
}
ch=getchar();
}
putchar(ch);
return 0;
}
//结果:
//asak(输入)
//btbl(输出)
3.ANSI C函数原型
(1)旧标准的问题:
在ANSI C之前,声明函数的方案有缺陷,因为只需要声明函数的类型而无需声明参数
因此,如果调用函数时参数个数或类型不匹配,编译器无法察觉,如下述程序:
#include <stdio.h>
int imax();//旧式函数声明
int main(void) {
printf("the maximum of %d and %d is %d\n",3,5,imax(3));
printf("the maximum of %d and %d is %d\n",3,5,imax(3.0,5.0));
return 0;
}
int imax(int n,int m) {
return (n>m?n:m);
}
//结果:在Xcode 4.6中运行(来自C Primer Plus(第6版)中文版 中国工信出版集团,人民邮电出版社)
the maximum of 3 and 5 is 1606416656
the maximum of 3 and 5 is 3886
//分析(注意,不同系统的结果可能不同):
主调函数把它的参数存储在称为栈(Stack)的临时存储区,被调函数从栈中读取这些参数.在本例
中,这2个过程并未相互协调,主调函数根据函数调用中的实际参数决定传递的类型.而被调函数根
据形式参数读取值,因此,主调函数调用imax(3)时把1个整数放在栈中.当imax()执行时,它从栈中读取2个整数.其中读取的第2个值是当时恰好在栈中的其他值
主调函数调用imax(3.0,5.0)时,把2个double类型的值放在栈中(f1oat类型作为参数传递时会被
升级为double类型).在本系统中,2个double类型的值就是2个64位的值,所以共有128位的数据被
放在栈中.在我们的系统中,每个int类型变量占用32位.当imax()从栈中读取2个int类型的值时,
只读取了前64位.这64位的数据对应2个整数,其中较大的是3886
(2)ANSI C的解决方案:
针对旧标准的问题,ANSI C要求在函数声明时也要声明参数,即使用函数原型(Function Prototype)进行声明
如果函数不接受参数,应使用void,如:
int f(void);
否则在某些标准中不合法
一些函数接受的参数数量和类型是不固定的,对此,ANSI C允许使用部分原型,如:
int printf(const char *,...);
4.不同作用域中的变量互相独立:
在C语言中,不同作用域(各个局部和全局)中的变量互不干扰,相互独立
//实例:
#include <stdio.h>
void f(void);
int main(void) {
int p=12;
printf("%d,%d\n",p,&p);//结果:12,6487580
f();
printf("%d,%d\n",p,&p);//结果:12,6487580
return 0;
}
void f(void) {
int p=33;
printf("%d,%d\n",p,&p);//结果:33,6487516
}
5.函数名代表函数的内存地址:
#include <stdio.h>
void f(void) {
printf("AAA");
}
int main(void) {
printf("%p",f);
return 0;
}
//结果:
0000000000401530
6.内联函数:
参见:https://blog.csdn.net/liu17234050/article/details/104179972
通常函数调用有一定的资源开销,因为函数的调用过程包括建立调用/传递参数/跳转到函数代码/返回.使用宏使代码内联可以避免这些开销.C99还
提供另1种方法:"内联函数"(Inline Function).在C99/11标准中对内联函数的叙述是:把函数变成内联函数意味着尽可能快地调用该函数,其具
体效果由实现定义.因此,如果把函数变成内联函数,编译器可能会用内联代码替换函数调用,或(并)执行一些其他优化,但也可能不起作用
标准规定具有内部链接的函数可以成为内联函数,还规定了内联函数的定义与调用该函数的代码必须在同1个文件中.因此,最简单的方法是使用函数
说明符inline和存储类别说明符static:
//注意:static/extern等不是函数说明符,C99时inline是唯一的函数说明符
inline static int f(void) {
int i,num=1;
while (i=0;i<10;i++) {
num*=i;
}
return num;
}
编译器可能会用函数体中的代码替换函数调用.由于没有给内联函数预留单独的代码块,无法获得内联函数的地址(实际上可以,不过这样做之后,编
译器会生成1个非内联函数).另外,内联函数无法在调试器中显示
内联函数应该较为短小,因为对较长的函数来说,调用函数所花费的时间远少于执行函数所花费的时间
编译器优化内联函数需要知道该函数的内容,这意味着内联函数的定义和调用必须在同1个文件中.如果程序有多个文件都需要使用某个内联函数,最
简单的方法是将内联函数的定义放入头文件,然后在使用该函数的文件中包含该头文件.因为内联函数具有内部链接,所以在多个文件中定义同名函
数不会产生什么问题
与C++不同,C还允许混合使用内联函数定义和外部函数(具有外部链接的函数)定义:
//file1.c中:
inline static double square(double x) {//静态内部链接
return x*x;
}
int main(void) {
double q=square(1.3);
...
}
//file2.c中:
double square(double x) {//外部链接
return (int)(x*x);
}
void spam(double v) {
double kv=square(v);
...
}
...
//file3.c中:
inline double square(double x) {//见下图
return (int)(x*x+0.5);
}
void masp(double w) {
double kw=square(w);
...
}
...
7._Noreturn函数:
C11新增了函数说明符_Noreturn,表明调用完成后不返回主调函数(注意和返回void的区别).exit()是_Noreturn函数的1个例子
_Noreturn的目的是告诉用户和编译器这个这个特殊的函数不会把控制返回给主调函数,告诉用户以免滥用该函数,告诉编译器以优化代码