c和指针--函数

函数定义

返回值类型 函数名 形参类型 函数体 是函数组成的四个基本要素
开发一段程序是一个周密执行的过程,对于一些和必须要执行的函数/过程,可以先用存根占位置

function()
{
	// NULL 空函数体, 只起到一个存根作用
}

K&R C定义一个函数 显然比现在复杂得多

int
add(a, b)
int a;
int b;
{
    return a + b;
}

函数默认返回值是 int,所以对于没有返回值的函数应该使用 void 声明其返回类型;
从表达式内部调用一个过程类型的函数是一个非常严重的错误,这会使用一个不可预测的值;(编译器会识别到这个错误)

函数声明

当编译器遇到一段函数调用代码,如果他没有实现留存函数声明信息,他会假定函数返回值类型是 int,同时默认函数传参类型和数量都是正常的;
注:K&R C的函数定义只能让编译器意识到返回值类型而不能注意到参数类型

向编译器传递函数定义信息的方法有两种
1、在函数调用前已经出现了函数的定义
2、使用函数原型向编译器传递函数信息

将函数原型放置在一个源文件中,如果其他源文件需要这个函数原型就 #include 这个文件;这样做有几个好处
1、函数声明具备文件作用域,原型一份拷贝可以作用在整个源文件,相比函数调用前单独写一份函数原型要容易得多;
2、函数原型只需要书写一次(如果修改只需要修改一次),避免原型多次书写出现失误的情况;
3、如果函数原型被 include 函数定义,那么编译器可以清楚的看到函数原型和函数定义的匹配

int *find_int(int key, int array[], int len);
注意后面这个分号,这是区分函数原型和函数定义的标志,这个函数原型中包含了形参的名字,事实上不包含形参名只包含形参类型也是完全可以的,但会让人不明所以;

int *func();
看起来这句代码既可以解释为旧式函数声明,也可以解释为无参函数原型;—实际上,他只能是旧式函数声明,为了方便兼容前面的标准,一个没有参数的函数原型应该写成这个样子
int *func(void)

#include <stdio.h>

int main () { 
    float c = 0;
    c = add(1.14, 2.0);
    printf("c = %d\n", c);
    return 0;
}  

float add(float a, float b)
{
    return a + b;
}

由于函数缺省认定,导致编译器默认的函数原型与函数定义对不上,报错信息如下
/data/173219383989534032.c: At top level:
/data/173219383989534032.c:13:7: error: conflicting types for ‘add’; have ‘float(float,  float)13 | float add(float a, float b)
      |       ^~~
/data/173219383989534032.c:7:9: note: previous implicit declaration of ‘add’ with type ‘int()7 |     c = add(1.14, 2.0);
      |         ^~~

值类型不是值的内在本质,而是取决于他被使用的方式,如果编译器认定这个值是 int ,并产生整数指令来操作这个值,如果这个值实际上是个非整,其结果通常是不正确的;

假设 func 实际上返回的是 3.14 --float,但如果这个值被当作 int 使用,那么他的值就是 1078523331; 这个例子说明返回值不是整型的函数具有原型是非常重要的;

函数参数

值传递和地址传递
声明数组参数时不指定它的长度是合法的,函数不为数组元素分配内存,一个函数可以访问任意长度的数组,同时,函数没有办法判断数组的长度,如果函数需要这个值,必须作为参数显式传递;

#include <stdio.h>

int longll(int a[])
{
    printf("a.length2 = %d \n", sizeof a);
    return 0;
}

int main()
{
    int a[] = {1, 2, 3, 4, 5};
    printf("a.length = %d \n", sizeof a);
    longll(a);
    return 0;
}

运行结果
a.length = 20 
a.length2 = 8 

ADT和黑盒

ADT–抽象数据类型:抽象数据类型的基本想法 模块具有功能说明和接口说明,前者说明模块执行的任务,后者定义模块的使用;用户不应知道模块实现的细节只能访问特定的接口;
黑盒设计–限制函数和数据的作用域;

static 限制模块的访问
static 修饰局部变量,不改变变量作用域,但是该变量全局只进行一次内存分配,其值再次调用时维持上一次调用的值。
修饰全局变量/函数,被修饰变量只能被该模块成员访问;

递归

使用递归来执行一个流程,必须要关注到递归带来的好处能否抵得上他的代价

可变参数列表

#include <stdarg.h>

int add(int num, ...)
{
	va_list var_arg;
	int count = 0, sum = 0;
	va_start(var_arg, num);

	for (; count < num; count++) {
		sum += va_arg(var_arg, int);
	}
	va_end(var_arg);
	return sum > 0 ? sum : 0;
}

步骤如下
#include <stdarg.h> 头文件引入
int add(int, …) 函数声明中含有必备的省略号
va_list 声明可变参数变量读取参数的值
va_start 第一个参数 va_list 第二个指定的确定参数名,执行后va_list 指向可变参数部分的第一个参数,va_arg(va_list, int) 按指定类型读取参数内容 返回对应参数的值
va_end 表示可变参数拆解完成

限制
1、参数列表中至少有一个命名参数
2、可变参数宏无法判断实际的参数数量
3、宏无法判断每个参数的类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值