语法格式

返回值类型  函数名(形式参数)        //函数头  ---  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" 错误。这是因为:

原因

  1. void * 是一种通用指针类型,可以指向任何数据类型

  2. 编译器不知道 void * 指向的数据的具体类型和大小

  3. 直接解引用会导致编译器无法确定如何解释内存中的数据

解决方法

你需要先将 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

        //容量  大 --> 小

        //速度  快 -->大

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐