C语言的函数在理解和使用中主要从以下几个方面来思考:
什么是函数?
函数的入口参数
函数的返回值
函数具备三要素:
1、函数名 (地址)
2、输入参数
3、返回值
在定义函数时,必须将三要素告诉编译器;
命令:man 3 printf;可以查看关于printf的一些相关信息;
因为函数的名称本身就是一个地址,所以我们在函数调用的时候完全可以使用指针去调用,只不过我们在定义指针的时候一定要注意要跟函数的入口参数和返回值的类型保持一致;
调用者:一般为主函数;函数名(要传递的数据(实参))
被调者:函数的具体实现;
函数的返回值 函数名 (接收的数据(形参))
传递的过程:实参 传递给 形参
传递的形式:拷贝(按位逐一赋值)
地址传递:调用者让子函数修改自己空间里的值
上面函数的功能相信大家都很熟悉,就是实现数值的交换,可以看到子函数的入口参数是两个指针,这样主函数在调用的时候只要输入两个地址,子函数就可以直接改变两个地址所表示的内容了。
连续空间的传递:连续的空间我们常见的有两个,1、数组,2、结构体
在结构体参数的传递中指针的传递方式是比较省内存的,我们优先考虑的应该就是指针的方式,当然在数组的传递过程中我们采用的也是指针的方式。
字符空间与非字符空间的区别:结束的标志不同
字符空间的结束标志:存在0x00(1B)
非字符空间的结束标志:可以存在0x00但不是结束的标志
命令:man strlen 可以查看库中关于strlen相关的内容,所以man 库函数函数名,可以查看相关函数在库函数中的内容;
一般我们在对字符空间数据进行处理时采用的方式是逐一处理,框架是:
while(P[i])
{ //数据的处理; i++}
非字符空间:结束的标志不是0x00,是数量(B)
void * :数据空间的标识符
上面的文章我们也说过memcpy(),我们可以看到它的入口参数就是用void修饰的,为什么会用void去修饰一个指针呢?原因很简单因为调用者读出内存的方式可能是int、char、struct;由于读取内存的方式不同而我们的子函数又要满足所有的调用者,所以形参的调用方式就要用void 来修饰了。
int fun(void *buf,int len)
{
int i;
unsigned char *tmp = (unsigned char *)buf;
for(i=0;i<len;i++)
{
对tmp[i]进行处理
}
}
以上就是关于void *的用法
如果形参的修饰符是void我们就认为是对空间内存的修改,如果是char、int等我们认为是对一个值的修改。
返回值:提供启下功能的一种表现形式,我们在来看一下函数的表达语法
函数的表达的基本语法:返回类型 函数名称(输入列表)
{ return 返回值};
返回值的类型:
1、基本数据类型
2、指针类型(连续空间类型)
int fun(**p):如果函数是这样的入口参数,那么我们认为调用这样一个子函数是希望它帮我们修改一下地址;
int *fun();返回的是一个连续空间;在这种函数的写法上,我们特别要注意的是函数返回的地址所指向的空间是合法的【不是局部变量】;那么我们在子函数中我们可以把指针变量定义为静态变量(static),也可以运用malloc函数直接开辟空间(在堆区域);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *fun(void)
{
//static char buf[]="hello world";
char *s = (char *)malloc(100);
strcpy(s,"hello wprld");
return s;
}
int main()
{
char *p;
p = fun();
printf("the p is %s \n",p);
free(p); //释放malloc开辟的内存空间
return 0;
}
以上的代码就是上面两种情况的举例;