C语言 函数(function)
C语言所有的标识符 --- 先定义后使用main函数是整个程序的入口函数定义的位置可以在main之前,也可以在main之后main之前,直接使用main之后,在使用前,要做一下函数声明//函数声明:函数头复制+分号。
语法格式
返回值类型 函数名(形式参数) //函数头 --- head
{
//声明部分
//语句部分
函数体 //body
return 表达式; //返回函数处理的结果
}
函数头:
----返回值类型 -------> int / short / long...
//返回值类型和实际返回结果类型不一致,以定义的返回值类型为准 (即实际返回值会被转换为定义的类型)
//如果不需要返回值----返回值类型定义为void(对应的return后不能有值)
----函数名 ------> 标识符,体现函数功能的名字(见名知意)
----形式参数(形参) ------> 本质是变量,作用是用来接收 实际的参数(即实参)(数据的入口)
形参语法:类型1 变量名1,类型2 变量名2 //每个形参都必须指明类型
//当函数不需要传入参数时,形参部分写void
注意:形参 与 实参 ------ 类型匹配,个数相同,占用不同的内存空间
函数体:
----函数对应功能的具体代码
函数传参
1)值传递:

2)地址传递——被调修改主调
• 原理:将实参的内存地址传递给形参(形参为指针变量),被调函数通过指针(地址)直接访问和修改实参的内存空间。
• 特点:被调函数对指针指向的内存的修改会直接影响主调函数的实参。
• 适用场景:需要通过函数修改主调函数中变量的值(如交换两个变量),或传递大型数据(避免复制副本的开销)。

void * //空类型指针 --- 万能指针(表示可以接受任意类型的大小)
注意:如果要形参为void *类型,要强制转换为使用的数据类型后才能传参
void * //解引用
解引用 void * 指针的问题
在 C/C++ 中,当你尝试直接解引用一个 void * 指针时,会出现 "dereferencing 'void *' pointer" 错误。这是因为:
原因
-
void *是一种通用指针类型,可以指向任何数据类型 -
编译器不知道
void *指向的数据的具体类型和大小 -
直接解引用会导致编译器无法确定如何解释内存中的数据
解决方法
你需要先将 void * 指针转换为具体类型的指针后才能解引用:
函数嵌套调用
语法:函数名(参数)
过程(递归--->倒序):函数调用时当前执行函数的相关信息存储在栈中,调用函数执行结束回到前一个执行函数,栈中存储的函数相关信息被恢复,函数继续执行。

递归解决问题
---- > 是函数
1.递推关系:n 和 n-1
2.递归结束条件
返回值类型 递归函数(形参)
{
if(是否达到结束条件)
{
//是 ---> 回归
return 表达式;
}else
{
//假 ---> 递推
return 问题 n 和 n-1 的递推结果;
}
}
汉诺塔问题
思路:
由下图,起始柱为A,辅助柱为B,目标柱为C ---- 若要将7个圆盘由A-->C,可先将前六个盘子由A-->B,再把圆盘7由A-->C,最后将辅助柱上的由B-->C,循环执行直至只剩一个圆盘;
可得到以下规律:前 n-1 // A(起始)-->B(辅助)
第n个 // A(起始)-->C(目标)
n-1 // B(辅助)-->C(目标)

代码示例
//汉诺塔问题
#include <stdio.h>
void move(char p1, char p2)
{
printf("%c--->%c\n",p1,p2);
}
// 起始A 辅助B 目标C
void hanoi(int n,char p1, char p2, char p3)
{
if(n == 1)
{
move(p1,p3);
}else
{
hanoi(n-1,p1,p3,p2);
move(p1,p3);
hanoi(n-1,p2,p1,p3);
}
}
int main(void)
{
int n = 0;
printf("Input a num:");
scanf("%d",&n);
hanoi(n,'A','B','C');
return 0;
}
原则
单一原则,高内聚低耦合
函数定义的位置
C语言所有的标识符 --- 先定义后使用
main函数是整个程序的入口函数
定义的位置可以在main之前,也可以在main之后
main之前,直接使用
main之后,在使用前,要做一下函数声明 //函数声明:函数头复制+分号
C语言程序运行的五个区
1.栈(stack)
-------> 先进后出(FILO)--- 特点
栈底固定,栈顶进出数据 --- 入栈 / 出栈
栈是内存中的一块空间,Linux系统中默认大小 8M,空间大小可以修改
局部变量 ---- 自动申请,自动释放
2.堆
空间非常大
空间的使用手动申请,手动释放
3.全局区 / 静态区
存放全局变量,静态变量 //默认初始化为0
静态区特点:只初始化一次(程序编译好静态变量固定),且值具有继承性;
不能用变量初始化,只能用常量初始化 //int a = 0; static int b = a;
#include <stdio.h>
void test(void);
int main(void)
{
while(1)
{
test();
sleep(1); //睡眠函数,时间位1s
}
return 0;
}
#if 0
void test(void)
{
int i = 0; 随着循环调用 i 每次都会重新初始化为0
i++;
printf("i = %d ",i);
}
/*
程序运行结果:i = 1 i = 1 i = 1 i = 1 ...
*/
#endif
#if 1
void test(void)
{
static int i = 0; //随着循环调用 i 只会初始化一次,第二次循环直接从i++开始执行
i++;
printf("i = %d ",i);
}
/*
程序运行结果:i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 ...
*/
#endif
4.字符常量区
存放"hello"这样的字符型常量
5.代码区
机器指令
数组作为函数参数
int型一维数组
int a[10] = {0};
a[i] //a数组中某个元素作函数实参,直接传入,与普通变量传参没区别
a //整个数组做函数实参,需要传入数组名和数组长度
整个数组做参数传入方法的解释:
从类型角度
printArray(int a[], int len) //实际书写形式
printArray(int *a, int len) //编译器理解 ---> int *a //指针类型 ----> 需要的是一个地址
//从值的角度 ---> 数组名代表 首元素的地址
//64位平台,指针大小都是8字节
代码示例:
//实现:一维数组的输入
#include <stdio.h>
void printfArray(int a[], int len)
{
int i = 0;
for(i=0; i<len; i++)
{
// printf("a[%d] = %d\n",i,a[i]);
printf("%d ",a[i]);
}
putchar('\n');
}
int main(void)
{
int a[] = {10,5,3,4,8,7,1};
int len = sizeof(a) / sizeof(a[0]);
printfArray(a,len);
return 0;
}
int型二维数组
示例:int a[3][4];
int row = sizeof(a) / sizeof(a[0]);
printArray(a,row) //实参传入数组名和数组行数
printArray(int a[][4], int row) //形参传入数组形式和数组行数
本质:printArray(int[4] a[], int len) //实际理解二维数组,len长的大小为4个int型的一维数组
printArray(int[4] *a, int len) //编译器处理理解
代码示例:
//实现:打印二维数组值 / 输入二维数组值
#include <stdio.h>
void printArray(int a[][4], int row)
{
int i = 0;
int j = 0;
for(i=0; i<row; i++)
{
for(j=0; j<4; j++)
{
printf("%-2d ",a[i][j]);
}
putchar('\n');
}
}
void scanfArray(int a[][4], int row)
{
int i = 0;
int j = 0;
for(i=0; i<row; i++)
{
for(j=0; j<4; j++)
{
scanf("%d",&a[i][j]); //二维数组输入函数要加取地址符(&)
}
}
}
int main(void)
{
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14};
int row = sizeof(a) / sizeof(a[0]);
scanfArray(a,row);
printArray(a,row);
return 0;
}
char型一维数组
示例:char s[] = "hello";
printStr(char s[]) //实际书写形式
printStr(char *s) //编译器处理成指针(和int型一维数组同)
注:char型一维数组做函数参数,实参只需要传入数组名( printStr(s) ),形参只需要数组形式
//因为字符型数组自带结束标志'\0',操作数组时,以'\0'为标志。
代码示例:
//打印字符数组
#include <stdio.h>
void Puts(char s[]) //标注字符输入函数puts
{
int i = 0;
while(s[i] != '\0')
{
printf("%c",s[i]);
i++;
}
putchar('\n');
}
int main(void)
{
char s[20] = {0};
printf("Input arrar-s:");
gets(s);
Puts(s);
return 0;
}
char型二维数组
----> 存放多个字符串
示例:char s[][10] = {"hello","china"};
int row = sizeof(s) / sizeof(s[0]);
printStr(s,row) //实参传入数组名和数组行数
printStr(char s[][10], int row); //形参传入数组形式和行数
代码示例:
/*
实现:二维字符型数组输入输出,三种排序(选择 / 冒泡 / 插入),二分查找(前提:有序的字符串)
*/
#include <stdio.h>
#include <string.h>
void printStr(char s[][20], int row)
{
int i = 0;
for(i=0; i<row; ++i)
{
printf("%s ",s[i]);
}
putchar('\n');
}
void scanStr(char s[][20], int row)
{
int i = 0;
for(i=0; i<row; ++i)
{
scanf("%s",s[i]); //与int型数据不同,字符型数组使用标注输入scanf函数不用加取地址符&
}
}
void optionSort(char s[][20], int row)
{
int i = 0;
int j = 0;
char tmp[20] = {0};
for(i=0; i<row-1; i++)
{
for(j=i+1; j<row; j++)
{
if(strcmp(s[i],s[j]) > 0)
{
strcpy(tmp,s[j]);
strcpy(s[j],s[i]);
strcpy(s[i],tmp);
}
}
}
}
void bubbleSort(char s[][20], int row)
{
int i = 0;
int j = 0;
char tmp[20] = {0};
for(i=0; i<row-1; i++)
{
for(j=0; j<row-1-i; j++)
{
if(strcmp(s[j],s[j+1]) > 0)
{
strcpy(tmp,s[j+1]);
strcpy(s[j+1],s[j]);
strcpy(s[j],tmp);
}
}
}
}
void insertSort(char s[][20], int row)
{
int i = 0;
int j = 0;
char tmp[20] = {0};
for(i=0; i<row; i++)
{
strcpy(tmp,s[i]);
j = i;
while(j>0 && strcmp(s[j-1],tmp) > 0)
{
strcpy(s[j],s[j-1]);
j--;
}
strcpy(s[j],tmp);
}
}
int binaryResearch(char s[][20], int row, char dest[])
{
int ret = -1;
int begin = 0;
int mid = 0;
int end = row-1;
while(begin <= end)
{
mid = (begin+end) / 2;
if(strcmp(dest,s[mid]) < 0)
{
end = mid-1;
}else if(strcmp(dest,s[mid]) > 0)
{
begin = mid+1;
}
else
{
ret = mid;
break;
}
}
return ret;
}
int main(void)
{
char s[][20] = {"hello","china","world","am","consistent","persive"};
int row = sizeof(s) / sizeof(s[0]);
// printf("%d\n",row);
char dest[20] = {0};
#if 0
printf("Input dest[] to research:");
gets(dest);
#endif
// scanStr(s,row);
// optionSort(s,row);
// bubbleSort(s,row);
insertSort(s,row);
printStr(s,row);
#if 0
if(binaryResearch(s,row,dest) == -1)
{
printf("Don't find\n");
}else
{
printf("Find\n");
}
#endif
// printf("%d\n",binaryResearch(s,row,dest));
return 0;
}
标识符的作用域和可见性
作用域 //标识符发挥作用的范围
全局作用域:不在任何一个{ }范围内(在函数外面) -----> 全局变量
//文件作用域
局部作用域:只要是在{ }括起来的范围内,就是一个局部作用域 ----> 局部变量
可见性 //当程序运行到某个代码,此时哪些标识符可见
规则:先定义后使用;
在同一作用域内,不能有同名标识符;
在不同的作用域,同名标识符相互之间没有影响;
如果是不同作用域且作用域之间存在嵌套关系,则内层的作用域的同名标识符,会屏蔽外层的作用域的同名标识符(就近使用原则)。
变量的生命周期
//变量,从开始存在到销毁位一个生命周期
1.局部作用域范围内的生命周期:局部变量从代码执行到对应的定义开始存在,到所在的作用域结束
2.全局 / 静态作用域范围内的生命周期:全局变量 / 静态变量从程序运行开始,到程序运行结束(全程序的生命周期 [./a.out] )
3.变量生命周期的改变:通过添加存储类别关键字改变
存储类别关键字:
auto //空间开在栈上(栈区程序运行到才会被使用)----> auto int a;
static //修饰全局变量,空间开在静态区(全局区),延长变量生命周期至全程序
----> static int a;
//static-->限制作用域 -- 限定所修饰的变量,只能作用于本文件,且其他文件无法访问
//好处:可以避免重名,保护数据
//修饰函数,作用:限定文件使用
register //寄存器,告诉编译器把变量放到寄存器上
//建议性关键字(不一定会被采纳)
//不能取地址
extern //外部关键字,起到声明作用 ---> extern int a;
//告诉编译器用到的变量存在但不在本文件,去别的文件寻找
//如果extern修饰的变量被static修饰,那么extern也无法调用
存储体系结构:硬盘、内存、cache 高速缓存(16M 1T / 32M 1T / 62M 1T)、CPU(一级缓存 / 二级缓存 / 高速缓存)、寄存器(ARM处理器 64位)
----> 硬盘 --- 内存 --- 缓存 --- cache --- 寄存器 --- CPU
//容量 大 --> 小
//速度 快 -->大
更多推荐


所有评论(0)