函数
函数的定义
函数就是将一段经常使用的代码封装起来,减少重复代码,在一个较大的程序,一般分为若干个程
序块,每个模块实现特定的功能。
函数的定义形式如下:
返回值类型 函数名 (参数列表)
{
函数体语句
return表达式
}
返回值类型 :一个函数可以返回一个值。
在函数定义中函数名:给函数起个名称
参数列表:使用该函数时,传入的数据
函数体语句:花括号内的代码,函数内需要执行的语句
return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据给调用方
函数的作用:将一段经常使用的代码封装起来,减少重复代码,一个较大的程序,一般分为若干个
程序块,每个模块实现特定的功能。
如下一个函数进行演示
int add(int nNumberA, int nNumberB)
{
int nTemp = nNumberA + nNumberB;
return nTemp;
}
该函数实现了两个数的相加
函数的调用
功能:使用定义好的函数
语法: 函数名(参数)
此时调用上述演示的函数
int nRes = add(1,3)
函数参数列表中int nNumberA, int nNumberB为形式参数,简称形参,目的是告诉使用函数的人,这个参数,想要接受什么类型的值
此时调用函数中传参为1,3是实际参数,简称实参
实际参数的值才是真正参与运算的值
nRes接受函数的返回值
值传递
所谓值传递,就是函数调用时实参将数值传入给形参
值传递时,如果形参发生改变,并不会影响实参
如下一个程序进行演示
void swap(int num1, int num2)
{
printf( "交换前:);
printf( "num1 = %d",num1); 打印10
printf( "num2 = %d",num2); 打印20
int temp = num1;
num1 = num2;
num2 = temp;
printf("交换后:);
printf("num1 = %d",num1); 打印20
printf("num2 = %d",num2); 打印10
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
printf("mian中的 a = %d",a); 打印10
printf("mian中的 b = %d",b); 打印20
system("pause");
return 0;
}
由上程序可知:值传递时,形参是修饰不了实参的
函数常见样式
常见的函数样式有4种
1.无参无返
2.有参无返
3.无参有返
4.有参有返
如下一个程序进行说明演示:
1.无参无返
void test01() 没有参数
{
void a = 10; 无类型不可以创建变量,原因无法分配内存
printf( "this is test01"); 无返回
}
2.有参无返
void test02(int a) 有参数
{
printf("this is test02");
printf("a = %d",a); 无返回
}
3.无参有返
int test03() 无参数
{
printf( "this is test03 ");
return 10; 有返回
}
4.有参有返
int test04(int a, int b) 有参数
{
printf("this is test04 ");
int sum = a + b;
return sum; 有返回
}
函数中内存
void func_1(int par)
{
par = 3;
std::cout<<"The parameter in the function is: "<<par<<std::endl;
}
这个时候里面的par是一个临时变量。在运行到这个函数的时候程序会开出一片新内存来存储 par,然后函数里面修改的值都是修改的 par 而不是传进来的变量
当我们直接传参数的时候会发生从传入的参数到函数内参数的深拷贝。(就是把它的数据完全拷贝到另一个内存空间上面)因此修改 par 修改的是 par 单独的内存空间,所以不会影响 a 的值。
函数的声明
作用:告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
正常情况,函数应该在主函数上方,因为程序是自上而下识别运行的。
如果函数定义在主函数的下方时,将代码块以外的东西,全部再写一遍到主函数前面,并加上分号,即为声明
如下一个函数进行演示说明
声明可以多次,定义只能一次
声明,即代码块以外的东西
int max(int a, int b);
int max(int a, int b);
定义,即代码块
int max(int a, int b)
{
return a > b ? a : b; 若a>b则返回a否则返回b
}
函数的分文件编写
作用:让代码结构更加清晰
函数分文件编写一般有4个方式:
1.创建后缀名为.h的头文件
2.创建后缀名为.cpp的源文件
3.在头文件中写函数的声明
4.在源文件中写函数的定义
如下一个程序进行演示:
swap.h文件:
#include<iostream>
using namespace std; C++中知识点:命名空间
void swap(int a, int b);实现两个数字交换的函数声明
swap.cpp文件:
#include "swap.h" 包含个人创建的头文件
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl; C++的输出方式
}
main函数文件:
#include "swap.h"
int main()
{
int a = 100;
int b = 200;
swap(a, b);
system("pause");
return 0;
}
变参函数
需要包含头文件 <stdarg.h>
int test(int optcount, ...) (int optcount 指传参总数)
{
va_list ap;(参数列表)
va_start(ap, optcount);(获取参数内容)(参数列表,参数的总数)
int nCount = 0;
for (int i = 0; i < optcount; i++)
{
nCount += va_arg(ap, int ) {va_arg(ap,int)(取参数)(参数列表,参数类型 参数 )}
}
va_end(ap)(结束标志)
}
int main()
{
int nRes = test(5,1,2,3,4,5);
printf("%d\r\n",nRes);
return 0 ;
}
递归函数
void printNumber(int nNumber)
{
printf("%d\n",nNumber);
if (nNumber < 50)
{
printfNumber(++nNumber); 递归
}
数组
所谓数组,就是一个连续的内存位置组成的集合,里面存放了相同类型的数据元素
数组分为一维数组,多维数组,其每一个元素内存大小都是本身类型的数据宽度
一维数组
以下是一维数组的定义方式:
int Arr1[5]
int Arr2[5] = {0,1,2,3,4}; (数组实际开始的下标从0开始 如Arr1[0])
int Arr3[5] = {[3] = 10};(指定第三个元素为10)
int Arr4[] = {0,1,2,3}; []中不添加数字,系统根据数组中元素的多少自动分配内存
int Arr5[] = {1,[5] = 1};
如下讲解数组在内存中具体表现:
int Arr1[5] = {0,1,2,3,4};
内存地址 0x012FFBE0
数据内存由十六进制表示,小端序排列,一个int类型元素占四个字节
00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
注意:
1.一维数组名称用于整个数组在内存中的长度以及获取数组在内存中的首地址
如arr[5],表示五个元素,arr表示数组内存首地址
2.数组名是常量,不可以赋值
如 arr1 = 100 编译错误
3.直接打印数组名,可以查看数组所占内存的首地址
如printf("%d",(int)arr); 获取数组首地址
4.对数组名进行sizeof,可以获取整个数组占内存空间的大小
如 printf("%d",sizeof(arr));
5.当数组名传入到函数作为参数时,被退化为指向首元素的指针
大端序 小端序
大端序 12 34 56 78(人正常看的情况)
小端序 78 56 34 12
数组越界
int nRes = Arr1[5]; 访问越界,无法访问任何东西
Arr1[5] = 11; 定义越界 如果Arr1[5]的位置有重要的东西,则会覆盖内容
多维数组
二维数组定义的四种方式:
1.数据类型 数组名[ 行数 ][ 列数 ]
举例:int arr[2][3];
2.数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
举例:int arr2[2][3] ={{1,2,3},{4,5,6}};
3.数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
举例:int arr3[2][3] = { 1,2,3,4,5,6 };
4.数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};
举例:int arr4[][3] = { 1,2,3,4,5,6 };
如下是一个具体的数组的讲解:
int nArr[3][4] = { {0,1,2,3,},{4,5,6,7},{8,9,10,11} };
[3]表示有3个一维数组,[4]表示每个一维数组有4 个元素
以此类推 int nArr[3][4][5]
[3]表示有三个二维数组
[4],表示每个二维数组都有四个一维数组
[5]表示每个一维数组有五个元素
注意:
1.在定义二维数组时,如果初始化了数据,可以省略行数
2.二维数组的数组名同一维数组一致用途
数组遍历
for (size_t i = 0; i < 5; i++)
{
Arr1[i] = i + 100;
}
for (size_t i = 0; i <5; i++)
{
printf("%d\r\n",Arr1[i]);
}
数组异或加密
char szBuffer[5] = {'q', 'x', 'w', 'w', 0};
每个字符内存后都有0 ,数组后有0与系统自带的0组成结束符
char szBuf[5] = {0};
for (sizt_t i = 0; i < 5; i++)
{
szBuf[i] = szBuffer[1] ^ 0x55;
}
szBuf[4] = '\0';
异或加密
for (sizt_t i = 0; i < 5; i++)
{
szBuf[i] = szBuffer[1] ^ 0x55;
}
szBuf[4] = '\0';
异或还原
二维数组
int nArr[3][4] = { {0,1,2,3},{4,5,6,7},{8,9,10,11} };
可通过一维数组的元素推断二维数组的个数
无法通过二维数组的个数来推断一维数组的元素个数
字符串操作
相关操作包含头文件string.h
char szStr[] = {'r', 'k', 'v', 'i', 'r', 0};
char szStrB[] = "rkvir"
char * szStrC[] = ="rkvir"
输入字符串时如
char szStrD[50] = {0};
scanf_s{"%s",szStrD,sizeof(sztrD)}
在调用该函数时,必须提供一个数字以表明最多读取多少位字符,一般用sizeof(调用的字符串)。
char szStrD[100] = {0}; (需要用{0},使100字节初始化。否则只有char szStrD[100],拼接字符串时时从这申请的一百个空间之后开始拼接的。不赋值0,很可能是乱码或者cc是被占用了)
计算字符串长度
int nRes = strlen(szStrC)
拼接字符串
strcat(szStrD, szStrB)
后者拼接到前者,前者字符串要留一定的空间给后者
拷贝字符串
strcpy(szStrD,szStrB);
将后者拷贝覆盖到前者,前者字符串要留足够的空间给后者
判断两个字符串大小
strcmp(szStrD,szStrB);
判断两个字符串大小。
若相等,则返回零;若前者小于后者,则返回负数一般是-1;若前者大于后者,则返回正数一般是1。
初始化字符串
memset(szStrD,int ch,size_t n);
参数依次是:数组,要修改成的内容,修改内存长度
指针和内存管理
指针一般指向一块内存
指针有多种指针,如char类型,int类型等等
指针中的描述符: * & ->
char * szBuffer = "rkvir"
int ArrA[5] = { 0, 1, 2, 3, 4};
int * p =ArrA; 指针指向数组首地址 不需要取地址
p是地址 *p是值
(此时若 p++)
printf("%d",*p); (将*p所指向地址的位置的东西取出来打印)
打印结果 0 实际是ArrA[0]的值
(打印结果为1)地址加1得到的新地址,新地址是ArrA[1]的地址,将他的值取出来
指针的运算和普通的运算不同:
相当于 地址 + 数据类型的宽度
如 120(地址指向) + 1(实际加的是int的字节宽度4)= 124
*p 则是将地址124的值取出来
int nNumber = 100;
int * pp = &nNumber 指针指到变量 需要取地址
printf("%d",*pp)
打印结果为100
若指针指向超过内存范围,则会获取该类型数据的乱码
函数指针
指针也可以指向函数
函数的本质就是指向一个内存地址,在该内存上执行指令
int pfnTest(int a, int b)
{
return a + b;
}
typedef int*(TestType) (int a, int b);
typedef(取别名) int*(被指向函数类型)(TestType)(新名字) (int a, int b)(与被指向函数列表一样)
新名字可以作为一个数据类型去定义一个函数
TestType tfn = pfnTest
TestType tfn (新名字+变量名) 变成了函数指针,此时赋值pfnTest(被指向的函数的函数名)
作用便是tfn作为指针指向了这个函数
内存的申请
申请内存需要包括头文件malloc.h
申请空间时要初始化 如 char szStrA[] = {0}
char * szBufferr = malloc(sizeof(char)* 100); 指针指向内存首地址
malloc(申请类型宽度*申请个数)
memset(szBuffer,0xCC,sizeof(char) * 100) 内存设置功能
在指针都指向的内存区域szBuffer首地址,将若干数据定义0xCC,定义100个字节
strcpy(szBuffer,"rkvir") 拷贝字符串,拷贝完成以后以00结尾
内存地址szBuffer 拷贝的内容rkvir
如果想要拷贝的内容有00需要利用memcpy
memcpy(szBuffer,"rkvir",10) 拷贝十个字节,超过rkvir内容范围的数据随机拷贝赋值
free(szBuffer); 释放szBuffer未使用内存
传递数组和指针
int addr(int * arr, int nCount)
{
int nTemp = 0;
for(size_t i = 0; i <nCount; i++)
{
nTmep += arr[i];
}
return nTemp;
}
int main()
{
int nArr[] = {1, 2, 3, 4, 5};
int nRes = addr(nArr,5);
printf("%d\n",nRes); 结果15
return 0;
}
若想返回多个同类型值
int addr(int * arr, int nCount,int *nRes)
{
int nTemp = 0;
for(size_t i = 0; i <nCount; i++)
{
nTmep += arr[i]
}
*nRes = nTemp
return nTemp;
}
int main()
{
int nArr[] = {1, 2, 3, 4, 5};
int nRes = 0;
addr(nArr, 5, &nRes);
printf("%d\n",nRes); 结果15
return 0;
}
作业
01.计算一个一维数组的总和( int nArr[10] = {12,158,14,12,16,8,152,12,3,55};)
02.计算一个二位数组中每一个一位数组的平均值( int nArr[3][3] = {{12,15,12},{15,33,99},{88,77,44}};)
03.找出一个一维数组中最大的值(int nArr[10] = {12,158,14,12,16,8,152,12,3,55};)
04.使用指针获取该数组中最大的数(int nArr[5] = {12,15,33,555,1};)
05.计算(char szStr[] = "this is a c string";)字符串的长度,并且输出
06.将下列两个字符串进行拼接,并且输出
char szStrA[] = "rkvir";
char szStrB[] = "Linux C";
char szStraC[100] = {}
07.输出提示并且接收两个字符串,对比是否一致,并且输出结果
08.输出提示并且接收用户名和密码,拼接成一个字符串输出
09.实现加减乘除函数
10.用递归实现(0 + 1 + 2 ... 100)
11.使用指针编写字符串处理库
11.1字符串长度计算函数strlen
11.2字符串对比函数strcmp() 长度且内容一致返回ture反之返回false
11.3字符串拷贝函数strcpy
11.4字符串拼接函数strcat
11.5字符串切割函数strslic
11.6字符串子串搜索strstr