一、函数名的意义
1.c语言是一门面向过程的语言:所谓的过程就是动词,动作。
功能块=动词1+动词2+……+动词
2.功能块:就是一堆动词(动作)的组合,动作通过函数来实现。
3.函数的功能:承上启下
承上启下,空间和空间的传递
a.看 看一下 具有数据类型的空间 传递这个空间的变量名 拷贝值
看,上层把我的空间传递给了下层,根本不用担心下层把我的空间改了
b.改 如果让子函数修改上层空间
a1.上层空间,必须把这个空间的首地址交给子函数
a2.子函数拿到这个地址后,必须使用 *addr=
c.具有连续数据类型的空间,指针只标记了首地址,没有标记结束标志
子函数就能够拿着这个指针,不受访问的限制了,这个肯定不是空间传递的方法
空间: 首地址,结束标志[个数]
- 值传递
void t01(int b){
b=20;
printf("Inside t01, b = %d\n", b); // 打印t01函数内部的b的值
}
void fun1(){
int a=10;
printf("Inside fun1, a = %d\n", a); // 打印fun1函数内部的a的值
t01(a);
}
- 地址传递
// 修改t01函数,使其接收一个整数的指针
void t01(int *b){
*b = 20; // 通过解引用指针来修改b指向的值
}
void fun1(){
int a = 10;
printf("Inside fun1, before calling t01, a = %d\n", a); // 打印a的初始值
t01(&a); // 将a的地址传递给t01函数
printf("Inside fun1, after calling t01, a = %d\n", a); // 打印a被修改后的值
}
4.如何实现承上启下的功能?封装和调用
5.函数名的意义:标识一段空间的首地址
6.标识符:
-
在C语言中,标识符(Identifier)是用来标识变量名、函数名、数组名、宏名等的符号。 它们是编程语言中用于表示数据对象和函数的名称。 以下是关于C语言中标识符的一些规则和特点: 1.字母开头:标识符必须以字母(A-Z或a-z)或下划线(_)开头。 2.后续字符:在第一个字符之后,标识符可以包含字母、数字(0-9)和下划线。 3.大小写敏感:C语言是大小写敏感的, 这意味着Variable和variable被视为两个不同的标识符。 4.保留字:不能使用C语言的保留字作为标识符, 例如int、if、return等。 5.长度限制:标识符的长度通常有限制,虽然这个限制由编译器决定, 但标准C语言规定前31个字符是保证有效的。 6.可见性:标识符的可见性取决于它们的作用域, 例如全局变量名在整个程序中都是可见的,而局部变量名只在定义它们的函数或代码块中可见。 7.命名约定:虽然不是强制的,但通常有命名约定, 比如驼峰命名法(camelCase)和下划线命名法(snake_case),这些约定有助于提高代码的可读性。
7.函数指针和指针函数:
- 函数指针:存储函数地址的容器
- 指针函数:返回地址的函数
-
//函数指针 int (*func)(int *,...) //指针函数 int *func(int xxx,...)
8.函数要考虑的两个问题:输入和返回
返回值 函数(输入值)
9.数组要考虑的两个问题:大小和多大的步长来访问,类比一下
10.标签:标识一段空间的首地址,就是常量指针
11.数组名和函数名,就是常量指针
12.()是运算符
13.仿函数:C语言中可以通过函数指针来实现类似仿函数的功能。函数指针可以指向不同的函数,从而在运行时根据需要调用不同的函数,这在某种程度上模拟了仿函数的行为。
14.把函数和括号看成一个整体,再向右或向左看
15.存printf
//printf是常量指针,本质上地址,可以被调用
#include<stdio.h>
void test01(){
printf("printf1 =%p\n",printf);
int (*my_show)(const char*,...)=(int (*)(const char*,...))(printf);
my_show("printf2 =%p\n",printf);
my_show("printf3 =%s\n","hello world");
}
int main(){
test01();
}
16.数据区和代码区:基本数据类型负责数据区,函数负责代码区
17.强制类型转换:把函数声明的函数名删掉就行
int (*my_show)(const char*,...)=(int (*)(const char*,...))(printf);
int a=int ;
18.函数名:本质上是地址。
- 直接写函数名
#include<stdio.h>
void test01(){
printf("printf1 =%p\n",printf);
int (*my_show)(const char*,...)=(int (*)(const char*,...))(printf);
my_show("printf2 =%p\n",printf);
my_show("printf3 =%s\n","hello world");
}
int main(){
test01();
}
- 直接写函数地址
#include<stdio.h>
void test01(){
printf("printf1 =%p\n",0x00403AA0);//0x00403AA0是printf的地址
int (*my_show)(const char*,...)=(int (*)(const char*,...))(0x00403AA0);
my_show("printf2 =%p\n",0x00403AA0);
my_show("printf3 =%s\n","hello world");
}
int main(){
test01();
}
19.推荐写法:typedef
typedef void(*xxx)(int); //把一个变量变为这个类型的别名
void(*xxx)(int);//定义一个叫xxx的函数指针变量,这个指针按照输入参数为int
返回值为void的函数行为来调用
typedef void(*xxx)(int);//定义一个叫xxx的函数指针类型的别名,这个指针按照输入参数为int
返回值为void的函数行为来调用
typedef int len_t;
xxx signal(int sig,xxx func);
二、函数的输入参数意义
1.一个函数就是一个独立空间,空间之间通过拷贝传递信息
2.核心就是拷贝
3.只读和可写区分开
连续空间为了避免二义性,只读和可写区分开
const int *key,int num 非字符的连续空间 只读
int *key,int num 非字符的连续空间 可写 可被修改
const char* 字符的连续空间 只读
char* 字符的连续空间 可写 可被修改
4.strcpy和buf
三、函数返回值注意事项
1.返回值的本质:拷贝
2.面试必考的错误:
void test()
{
int a;
return &a;//经典错误,a的空间还给系统了,现在&a取到的不是a的地址
}
3.函数的返回值一旦是指针,那么这个指针指向的空间绝对不是局部区域。(重要结论要记住)
4.设计接口或使用别人的接口,这个指针一定指向:
a.数据区 static 静态空间
b.堆区 malloc 动态空间 堆空间
四、多个.c的编程原理
1.工程=
A: 接口1 接口2
B: 调用接口1 调用接口2
每个接口单独编译 最后链接汇总成一个独立的可执行文件
2.如果调用不正确,会报错“undefined reference”未定义符号
3.为了方便调用,IDE(核心是工程管理器) 工程管理器(如cmake),负责:哪些文件编译,哪些不编。如果编译了,那么以什么形式编译。