函数
1.函数声明、定义、调用
#include包含头文件,#必须有,< >包含真正头文件,头文件只服务函数。
预编译时要先进行头文件的展开——函数声明
预编译时要进行宏定义中宏的展开和替换。
头文件有默认路径,有函数声明(.h)
<>指定路径。
函数套函数构成一个项目
在C语言中,函数的定义顺序是有讲究的:默认情况下,只有后面定义的函数才可以调用前面定义过的函数,举个栗子
int sum(int a, int b) {
return a + b;
}
int main()
{
int c = sum(1, 4);
return 0;
}
如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明
举个栗子:
int sum(int a, int b);
int main()
{
int c = sum(1, 4);
return 0;
}
int sum(int a, int b) {
return a + b;
}
函数的声明格式
返回值类型 函数名 (参数1, 参数2, ...)
只要你在main函数前面声明过一个函数,main函数就知道这个函数的存在,就可以调用这个函数。而且只要知道函数名、函数的返回值、函数接收多少个参数、每个参数是什么类型的,就能够调用这个函数了,因此,声明函数的时候可以省略参数名称。比如上面的sum函数声明可以写成这样:
int sum(int, int);
2.main函数
在C语言中,main()函数有三种形式。main函数作为整个程序的入口,返回值给了他的父进程。
int main(int arge,char *argv[ ])
main函数的参数: argc代表main函数参数个数,char *argv[ ]:[ ]优先级很高,argv[ ]代表数组
char argv[ ]:argv[ ]里边所有元素是char 类型,char *argv[ ]指针数组;
main函数的返回值返给了操作系统
进程相互独立,父进程下的子进程中的参数可以从父进程继承,只需要写一定的代码即可传参,特定的参数传入子进程中,返回到父进程,看是否执行了。
多个进程可以用_exit(0)结束进程
void表示类型的一个关键字,空类型(暂时我们不知道该给他什么类型)
void类型占一个字节,但是需要特定类型时,可以进行强制类型转换
隐式转换:
1.低精度向高精度转换,完全没有错误;高精度向低精度转换,数据丢失
2.小范围默认转化为大范围(指的是最大数值的那个类型)
注意:c语言是强类型语言,两个操作数之间运算必须类型一致。
2.1无参
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
2.2双参
习惯上第一个参数是整型argc,保存了外部调用命令的参数个数,第二个参数是指针数组或二级指针argv,以字符串形式保存了与argc对应的参数
#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;
}
2.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;
}
3.形参与实参
函数的参数分为形参和实参两种,形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,实参是在调用时传递该函数的参数
函数的形参和实参具有以下特点:
形参只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。
实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值等办法使实参获得确定值。
在参数传递时,实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。
函数的返回值
函数的值只能通过return语句返回主调函数。return语句的一般形式为:
return 表达式 或者为: return (表达式);
函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数返回类型为准,自动进行类型转换。
4.递归函数和迭代函数
4.1递归函数
计算阶乘
int jiecheng(int a)
{
if (a <= 0)
{
printf("参数错误.\n");
return -1;
}
//回归条件
if (a == 1)
{
return 1;
}
return a * jiecheng(a-1);
}
求fibo数某一项的值
#include <stdio.h>
int fibo(int num)
{
if (num > 0)
{
if (1 == num)
{
return 1;
}
if (2 == num)
{
return 1;
}
return fibo(num-1) + fibo(num-2);
}
}
int main(void)
{
int a = 0;
while (1)
{
printf("输入0表示推出.\n");
scanf("%d", &a);
if (0 == a)
{
break;
}
printf("%d项的fibo值:%d.\n", a, fibo(a));
}
return 0;
}
4.2迭代函数
计算从1开始到某个数据内所有的和
#include <stdio.h>
int sum(int a)
{
int i = 1, sum = 0;
if (a > 0)
{
for (i=1; i<=a; i++)
{
//迭加
//i值每变化一次就更新sum值
sum += i;
}
}
//最终的结果,中间环节不需要
return sum;
}
int main(void)
{
int a = 100;
printf("%d内的和:%d.\n", a, sum(a));
int i = 1, sum;
for (i=1; i<10; i++)
{
printf("sum = %d", sum+=i);
sleep(1);
}
return 0;
}
5.代参宏和普通函数
1.能完成相类似的功能
2.函数会检查你的参数列表和返回值类型,而宏定义没有类型,也就谈不上检查
3.宏定义只是替换,没有调用开销,函数有开销
调用函数即进栈出栈问题,开销就是进栈出栈
你的函数短小,但是还想利用检查参数的机制,要提高我的效率?
内联函数,处理这些问题,调用开销。
数学库调用时:gcc x.c -lm即数学库链接的时候需要 -lm
%p 打印地址
6.补充内容
行缓冲:无/n则将行写满再显示出来(默认所有都是一行)
举个栗子
unsigned i = 0;
for(i = 8; i >= 0; i--)
printf("0x%x.\n", a);
计算机使用补码存储数据,正数的补码是原码本身,负数的补码是反码加一
运算时:正数位移后补零,负数位移后补一
unsigned 无符号整形,i = -1时,系统读取它为i = -1的原码,即i = 1000 0000 0000 0000 0000 0000 0000 0001,其中的1000中的1代表数字,不代表符号了,所以死循环.
三个零:0,’0’,’\0’的区别
0是数字零
‘\0’是一个char字符,功能是标志一个字符串的结束,对应的数值是0
‘0’是一个char字符,对应的数值是48