栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言提高学习笔记

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言提高学习笔记

C语言提高 1.数据类型

每个数据必须指定类型,由编译器创建的,为了更好的管理内存

  1. 基本类型
    1. 整型
    2. 字符型
    3. 浮点型
      1. 单精度
      2. 双精度
  2. 构造类型
    1. 数组类型
    2. 结构体
    3. 联合体
    4. 枚举
  3. 指针类型
2. typedef使用
  • 起别名 - 简化struct关键字

  • 区分数据类型

  • 提高代码移植性

3. void使用

不可以利用void创建变量 无法给无类型变量分配内存

用途:

  • 限定函数返回值,函数参数

  • void * 万能指针 可以不通过强制类型转换就转成其他类型指针

4. sizeof用法

本质:不是一个函数,是一个操作符

返回值类型 unsigned int无符号整型

用途:可以统计数组长度

数组当参数传递后使用当指针使用,存放第一个数组元素地址

5. 变量的修改方式
  • 直接修改

  • 间接修改:指针

6. 内存分区
  1. 运行前

    1. 代码区:只读,共享
    2. 数据区:存放数据:全局变量 、静态变量、常量
      1. 已初始化数据区 data:已初始化全局变量、静态变量(全局和局部)、常量数据。
      2. 未初始化数据区 bss :未初始化的全局变量和静态变量。
  2. 运行后、

    1. 栈 符合先进后出数据结构,编译器自动管理分配和释放,有限容量

      不要返回局部变量的地址,局部变量在函数执行之后就被释放了,释放的内存就没有权限取操作了,如果操作结果未知

    2. 堆 容量远远大于栈,不是无限。手动开辟 malloc 手动释放 free

      主调函数没有分配内存,被调函数需要用更高级的指针去修饰低级指针,进行分配内存

7. static和extern区别
  • static静态变量:在程序运行前分配内存,程序运行结束生命周期结束,在本文件内都可以使用静态变量,作用域有限制
  • extern外部变量:表明变量在其他文件中,编译器在全局变量会隐式默认加
8. 常量
  • const修饰的变量:分为全局和局部变量,
    • 全局变量。直接修改编译器报错,间接修改语法通过,运行失败。原因:收到常量区保护。
    • 局部变量。直接修改报错。间接修改成功,称为伪常量。不可以用来初始化数组。
  • 字符串常量:char *p="helloworld",内容相同,地址相同。由编译器决定。不可以修改字符串常量。ANSI并没有制定出字符串是否可以修改的标准,根据编译器不同,可能最终结果也是不同的
8. 函数调用流程
  • 宏函数
    • 宏函数需要加小括号修饰,保证运算完整性
    • 通常将频繁使用,短小的函数写成宏函数
    • 一定程度上比普通函数效率高,省去普通函数入栈、出栈的时间(以空间换时间)
9. 函数的调用流程

局部变量、函数形参、函数返回地址. 入栈 和 出栈

调用惯例:vs默认cdecl

主调函数和被调函数必须要有一致约定,才能正确的调用函数,这个约定我们称为调用惯例

调用惯例 包含内容: 出栈方、参数传递顺序、函数名称修饰

C/C++下默认调用惯例: cdecl 从右到左 ,主调函数管理出栈 变量名前加下划线修饰

//变量传递分析
char * func()
{
	char * p =  malloc(10); //堆区数据,只要没有释放,都可以使用
	int c = 10;//在func中可以使用,test01和main都不可以使用
	return p;
}

void test01()
{
	int b = 10; // 在test01 、func 可以使用,在main中不可以用

	func();
}

int main(){

	int a = 10; //在main 、test01 、 func中都可以使用
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
10. 栈的生长方向和内存存放方向

栈生长方向

栈底 — 高地址

栈顶 — 低地址

内存存放方向

高位字节数据 — 高地址

低位字节数据 — 低地址

小端对齐方式

void test01()
{
	int a = 10;  //栈底  高地址
	int b = 10;
	int c = 10;
	int d = 10;  //栈顶  低地址


	printf("%dn", &a);
	printf("%dn", &b);
	printf("%dn", &c);
	printf("%dn", &d);

}


//2、内存存放方向
void test02()
{
	int a = 0x11223344;

	char * p = &a;

	printf("%xn", *p);    //44  低位字节数据  低地址
	printf("%xn", *(p+1)); //33  高位字节数据  高地址
}

int main(){


	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}
11. 空指针和野指针
  • 空指针

    不能向NULL或者非法内存拷贝数据

  • 野指针

    指针变量未初始化

    指针释放后未置空

    指针操作超越变量作用域

  • 空指针可以重复释放、野指针不可以重复释放

//1、不能向NULL或者非法内存拷贝数据
void test01()
{
	//char *p = NULL;
	给p指向的内存区域拷贝内容
	//strcpy(p, "1111"); //err
 
	//char *q = 0x1122;
	给q指向的内存区域拷贝内容
	//strcpy(q, "2222"); //err		

}


//指针操作超越变量作用域
int * doWork()
{
	int a = 10;
	int * p = &a;
	return p;
}

//2、野指针出现情况
void test02()
{
	//2.1 指针变量未初始化
	



	//2.2 指针释放后未置空
	char * str  = malloc(100);
	free(str);
	//记住释放后 置空,防止野指针出现
	//str = NULL;

	//free(str);
	//2.3 空指针可以重复释放、野指针不可以重复释放

	//2.4 指针操作超越变量作用域
	int * p = doWork();
	printf("%dn", *p);
	printf("%dn", *p);
}

12. 指针的步长
  • +1之后跳跃的字节数

  • 解引用 解出的字节数

  • 自定义结构体做步长练习

通过 offsetof( 结构体名称, 属性) 找到属性对应的偏移量

offsetof 引入头文件 #include

//1、指针的步长代表 指针+1之后跳跃的字节数
void test01()
{
	char * p = NULL;
	printf("%dn", p);
	printf("%dn", p+1);


	double * p2 = NULL;
	printf("%dn", p2);
	printf("%dn", p2 + 1);

}

//2、解引用的时候,解出的字节数量
void test02()
{
	char buf[1024] = { 0 };

	int a = 1000;

	memcpy(buf + 1, &a, sizeof(int));

	char * p = buf;
	printf("%dn", *(int *)(p+1));

}

//步长练习,自定义数据类型练习
struct Person
{
	char a;     // 0 ~ 3
	int b;      // 4 ~ 7
	char buf[64]; // 8 ~ 71
	int d;     // 72 ~ 75
};

void test03()
{
	struct Person p = { 'a', 10, "hello world", 20 };
	
	printf("d属性的偏移量: %dn", offsetof(struct Person, d));

	printf("d属性的值为:%dn", *(int *)((char *)&p + offsetof(struct Person, d)));
}
13. 指针的间接赋值
  • 三大条件

    • 一个普通变量+指针变量( 实参+形参)

    • 建立关系

    • 通过* 操作内存

利用Qt实现 操作地址 修改内存

14. 指针做函数参数的输入输出特性
  • 输入特性

    在主调函数中分配内存,被调函数使用

    分配在栈上和堆区

  • 输出特性

    在被调函数中分配内存,主调函数使用

15.字符串强化训练
  • 字符串结束标志

  • sizeof 和 strlen

  1. 拷贝字符串 利用三种方式

    利用[]

    利用指针

    while (*dest++ = *src++){}

  2. 翻转字符串

    利用[ ]

    利用指针

  3. sprintf使用

    格式化字符串

    sprintf(目标字符串,格式化内容,占位参数…)

    返回值 有效字符串长度

16. calloc 和 realloc
  • calloc 和malloc 都是在堆区分配内存
  1. 与malloc不同的是,calloc会将空间初始化为0

  2. calloc(个数,大小)colloc(10,sizeof(int))=malloc(sizeof(int)*10),成功返回空间起始地址,失败发挥null

  • realloc 重新分配内存
  1. 如果重新分配的内存比原来大,那么不会初始化新空间为0
  2. 先看后续空间,如果足够,那么直接扩展
  3. 如果后续空闲空间不足,那么申请足够大的空间,将原有数据拷贝到新空间下,释放掉原有空间,将新空间的首地址返回
  4. 如果重新分配的内存比原来小,那么释放后序空间,只有权限操作申请空间
17. scanf()使用
%*s或%*d跳过数据
%[width]s读指定宽度的数据
%[a-z]匹配a到z中任意字符(尽可能多的匹配)
%[aBc]匹配a、B、c中一员,贪婪性
%[^a]匹配非a的任意字符,贪婪性
%[^a-z]表示读取除a-z以外的所有字符
18.查找子串

实现mystrstr 自己查找子串功能mystrstr

int myStrstr(char *str, char * substr)
{
	int n = 0;
	while (*str != '') 
	{
		if (*str != *substr) 
		{
			n++;
			str++;
			substr++;
		}
		char *tmpStr = str;
		char *tmpSubStr = substr;
		while (*tmpSubStr != '') 
		{
			if (*tmpStr != *tmpSubStr)
			{
				n++;
				str++;
				break;
			}
			tmpStr++;
			tmpSubStr++;

		}
		if (*tmpSubStr == '')
		{
			return n;
		}
	}
	return -1;
}

void test01()
{
	char * str = "abdnfcdefgdfasdfaf";

	int ret = myStrstr(str, "dnf");

	if (ret != -1)
	{
		printf("找到了子串,位置为:%dn", ret);
	}
	else
	{
		printf("未找到子串n");
	}

}
19.指针的易错点
  • 越界
  • 指针叠加会不断改变指针指向
  • 返回局部变量地址
  • 同一块内存释放多次(不可以释放野指针)
20.二级指针做函数参数的输入输出特性
  • 二级指针做函数参数的输入特性

    • 创建在堆区
    • 创建在栈区
  • 二级指针做函数参数的输出特性

    • 被调函数分配内存,主调函数使用
21. const 修饰形参,防止误操作 22. 二级指针文件操作练习
#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 

int  getGetFileLines(FILE *fp)
{
	if (fp == NULL)
	{
		return -1;
	}
	char buf[1024] = { 0 };
	int lines = 0;
	while (fgets(buf,1024,fp)!=NULL)
	{
		lines++;
	}
	fseek(fp, 0, SEEK_SET);
	return lines;
}

void readFileData(FILE *fp, int len, char **pArray)
{
	if (fp == NULL)
	{
		return;
	}
	if (len <= 0)
	{
		return;
	}
	if (pArray ==NULL)
	{
		return;
	}
	char buf[1024] = { 0 };
	int i = 0;
	while (fgets(buf, 1024, fp) != NULL)
	{
		int curLen = strlen(buf) + 1;
		char * curStr = malloc(curLen);
		strcpy(curStr, buf);
		pArray[i++] = curStr;
		memset(buf, 0, 1024);
	}
}
void showFileData(char ** pArray, int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%s", pArray[i]);
	}
}

void test01()
{
	FILE *fp = fopen("./test.txt", "r+");
	if (fp == NULL)
	{
		printf("文件打开失败");
		return;
	}
	int len = getGetFileLines(fp);
	printf("%dn", len);
	char **pArray = malloc(sizeof(char *)* 5);
	readFileData(fp, len, pArray);
	showFileData(pArray, len);

	//释放堆区内容
	for (int i = 0; i < len; i++)
	{
		if (pArray[i] != NULL)
		{
			free(pArray[i]);
			pArray[i] = NULL;
		}
	}
	free(pArray);
	pArray = NULL;
	//关闭文件
	fclose(fp);
}

23. 位运算

按位取反 ~ 0变1 1 变0

按位与 & 全1为1 一0为0

按位或 | 全0为0 一1为1

按位异或 ^ 相同为0 不同为1

24. 位移运算

左移运算 << X 乘以2 ^ X

右移运算 >> X 除以 2 ^X

有些机器用0填充高位

有些机器用1填充高位

如果是无符号,都是用0填充

25. 一维数组名
  • 除了两种特殊情况外,都是指向数组第一个元素的指针

    1. sizeof 统计数组长度int arr[5]={1,2,3,4,5} sizeof(arr)为20
    2. 对数组名取地址, &arr数组指针,步长整个数组长度

    指针步长:offset(struct Person,d) 头文件

数组名是指针常量,指针的指向不可以修改的,而指针指向的值可以改

传参数时候,int arr[] 可读性更高int arr[] 等价于 int * arr

数组索引下标可以为负数

int * const a指针常量 :a是常量,指针的常量,它是不可改变地址的指针,但是可以对它所指向的内容进行修改。

const int * a 常量指针 :*a是常量,指向常量的指针,指针所指向的地址的内容是不可修改的。

26. 数组指针的定义方式
  1. 先定义出数组类型,再通过类型定义数组指针变量

    int arr[5] = {1,2,3,4,5};
    typedef int(ARRARY_TYPE)[5];//ARRARY_TYPE
    ARRARY_TYPE * arrP = &arr;
    //*arrP = arr = 数组名
    

    代表存放5个int类型元素的数组 的数组类型

  2. 先定义数组指针类型,再通过类型定义数组指针变量

    typedef int(*ARRARY_TYPE)[5];
    ARRARY_TYPE  arrP = &arr;
    
  3. 直接定义数组指针变量

    int(* p )[5] = &arr; // *p 等于数组名

27. 二维数组名
  • 二维数组名 除了两种特殊情况外,是指向第一个一维数组的 数组指针
  1. sizeof 统计二维数组大小
  2. 对数组名称取地址 int(*p)[3][3] = &arr
  • 二维数组做函数参数

    • void printArray(int (*array)[3], int row, int col)
    • void printArray(int array[][3], int row ,int col)
    • void printArray(int array[3][3], int row ,int col) 可读性比较高
  • 数组指针 和 指针数组?

    • 数组指针: 指向数组的指针
    • 指针数组: 由指针组成数组
28. 指针数组排序
void sortArray(char **pArray,int len)
{
	int min=0;
	int i = 0, j =0;
	for (i = 0; i < len; i++)
	{
		min = i;
		for (j = i + 1; j < len; j++)
		{
			if (strcmp(pArray[j],pArray[min]) == -1)
			{
				min = j;
			}
		}
		if (i != min)
		{
			char * tmp="";
			
			tmp = pArray[i];
			pArray[i] = pArray[min];
			pArray[min] = tmp;
		}
	}	
}

29. 结构体基本概念
  • 加typedef 可以给结构体起别名

  • 不加typedef ,可以直接创建一个结构体变量

  • 结构体声明 可以是匿名

  • 在栈上创建和在堆区创建结构体

  • 在栈上和堆区创建结构体变量数组

  • 结构体深浅拷贝

    • 系统提供的赋值操作是 浅拷贝 – 简单值拷贝,逐字节拷贝
    • 如果结构体中有属性 创建在堆区,就会出现问题,在释放期间,一段内存重复释放,一段内存泄露
    • 解决方案:自己手动去做赋值操作,提供深拷贝
30. 结构体嵌套一级指针练习
#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 

struct Person
{
	char *name;
	int age;
};

struct Person ** allocateSpace()
{
	struct Person ** tmp = malloc(sizeof(struct Person *) * 3);
	for (int i = 0; i < 3; i++)
	{
		tmp[i] = malloc(sizeof(struct Person));
		tmp[i]->name = malloc(1024);
		//strcpy(tmp[i]->name,"")
		sprintf(tmp[i]->name, "name[%d]", i + 100);
		tmp[i]->age = i + 100;
	}
	return tmp;
}

void printPerson(struct Person ** pArray, int len )
{
	for (int i = 0; i < len; i++)
	{
		printf("%s : %d", pArray[i]->name, pArray[i]->age);
		printf("n");
	}
}

void freeSpace(struct Person ** pArray, int len)
{
	for (int i = 0; i < len; i++)
	{
		if (pArray[i]->name != NULL)
		{
			free(pArray[i]->name);
			pArray[i]->name = NULL;
		}
	}

	for (int i = 0; i < len; i++)
	{
		if (pArray[i] != NULL)
		{
			free(pArray[i]);
			pArray[i] = NULL;
		}
	}

		if (pArray != NULL)
		{
			free(pArray);
			pArray = NULL;
		}

}

void test01()
{
	struct Person ** pArray = NULL;

	pArray = allocateSpace();


	//打印数组
	printPerson(pArray, 3);

	//释放内存
	freeSpace(pArray,3);
	printPerson(pArray, 3);
	pArray = NULL;
}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
	
}
31. 内存对齐

查看对齐模数 #pragma pack(*show*)

默认对齐模数 8

  • 自定义数据类型 对齐规则

    第一个属性开始 从0开始偏移

    第二个属性开始 要放在 该类型的大小 与 对齐模数比 取小的值 的整数倍

    所有属性都计算完后,再整体做二次偏移,将整体计算的结果 要放在 结构体最大类型 与对齐模数比 取小的值的 整数倍上

  • 结构体嵌套结构体

    结构体嵌套结构体时候,子结构体放在该结构体中最大类型 和对齐模数比 的整数倍上即可

32. 文件读写回顾
  • 按照字符读写

    写 fputc

    读 fgetc

    while ( (ch = *fgetc*(f_read)) != *EOF* ) 判断是否到文件尾

  • 按行读写

    写 fputs

    读 fgets

  • 按块读写

    写 fwrite

    参数1 数据地址 参数2 块大小 参数3 块个数 参数4 文件指针

    读 fread

  • 格式化读写

    写 fprintf

    读 fscanf

  • 随机位置读写

    fseek( 文件指针, 偏移, 起始位置 )

    SEEK_SET 从头开始

    SEEK_END 从尾开始

    SEEK_CUR 从当前位置

    rewind 将文件光标置首

    error宏 利用perror打印错误提示信息

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include

//按照字符读写文件:fgetc(), fputc()

void test01()
{
	//写文件

	FILE * f_write = fopen("./test01.txt", "w+");

	if (f_write == NULL)
	{
		return;
	}

	char buf[] = "this is first test";
	for (int i = 0; i < strlen(buf);i++)
	{
		fputc(buf[i], f_write);
	}

	fclose(f_write);

	//读文件
	FILE * f_read = fopen("./test01.txt", "r");
	if (f_read == NULL)
	{
		return;
	}
	char ch;
	while ( (ch = fgetc(f_read)) != EOF  )  // EOF  End of File
	{
		printf("%c", ch);
	}
	fclose(f_read);
}


//按照行读写文件:fputs(), fgets()
void test02()
{
	//写文件
	FILE * f_write = fopen("./test02.txt", "w");
	if (f_write == NULL)
	{
		return;
	}
	char * buf[] =
	{
		"锄禾日当午n",
		"汗滴禾下土n",
		"谁知盘中餐n",
		"粒粒皆辛苦n",
	};

	for (int i = 0; i < 4;i++)
	{
		fputs(buf[i], f_write);
	}

	fclose(f_write);

	//读文件
	FILE * f_read = fopen("./test02.txt", "r");
	if (f_read == NULL)
	{
		return;
	}
	while ( !feof(f_read ))
	{
		char buf[1024] = { 0 };
		fgets(buf, 1024, f_read);
		printf("%s", buf);
	}

	fclose(f_read);
}

//按照块读写文件:fread(), fwirte()
struct Hero
{
	char name[64];
	int age;
};
void test03()
{
	//写文件  wb二进制方式
	FILE * f_write = fopen("./test03.txt", "wb");
	if (f_write == NULL)
	{
		return;
	}

	struct Hero heros[4] = 
	{
		{ "亚瑟" , 18 },
		{ "赵云", 28 },
		{ "妲己", 19 },
		{ "孙悟空", 99 },
	};

	for (int i = 0; i < 4;i++)
	{
		//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
		fwrite(&heros[i], sizeof(struct Hero), 1, f_write);
	}

	fclose(f_write);

	//读文件  
	FILE * f_read = fopen("./test03.txt", "rb");  // read binary
	if (f_read == NULL)
	{
		return;
	}

	struct Hero temp[4];
	//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
	fread(&temp, sizeof(struct Hero), 4, f_read);

	for (int i = 0; i < 4;i++)
	{
		printf("姓名:%s  年龄:%d n", temp[i].name, temp[i].age);
	}
	fclose(f_read);
}

//按照格式化读写文件:fprintf(), fscanf()
void test04()
{
	//写文件
	FILE * f_write = fopen("./test04.txt", "w");
	if (f_write == NULL)
	{
		return;
	}

	fprintf(f_write, "hello world %d年 %d月 %d日", 2018, 7, 5);

	//关闭文件
	fclose(f_write);


	//读文件
	FILE * f_read = fopen("./test04.txt", "r");
	if (f_read == NULL)
	{
		return;
	}

	char buf[1024] = { 0 };
	while (!feof(f_read))
	{
		fscanf(f_read, "%s", buf);
		printf("%sn", buf);
	}

	fclose(f_read);
}

//按照随机位置读写文件
void test05()
{
	FILE * f_write = fopen("./test05.txt", "wb");
	if (f_write == NULL)
	{
		return;
	}

	struct Hero heros[4] =
	{
		{ "亚瑟", 18 },
		{ "赵云", 28 },
		{ "妲己", 19 },
		{ "孙悟空", 99 },
	};

	for (int i = 0; i < 4; i++)
	{
		//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
		fwrite(&heros[i], sizeof(struct Hero), 1, f_write);
	}

	fclose(f_write);


	//读取妲己数据
	FILE * f_read = fopen("./test05.txt", "rb");
	if (f_read == NULL)
	{
		//error 宏 
		
		//printf("文件打开失败n");
		perror("文件打开失败");
		return;
	}

	//创建临时结构体
	struct Hero temp;

	//改变文件光标位置
	fseek(f_read, sizeof(struct Hero) *2, SEEK_SET);

	fseek(f_read, -(long)sizeof(struct Hero) * 2, SEEK_END);

	rewind(f_read); //将文件光标置首

	fread(&temp, sizeof(struct Hero), 1, f_read);

	printf("姓名: %s 年龄: %dn", temp.name, temp.age);

	fclose(f_read);

}


int main(){
	//test01();
	//test02();
	//test03();
	//test04();
	test05();

	system("pause");
	return EXIT_SUCCESS;
}
33. 函数指针
  • 函数指针

    函数名本质就是一个函数指针

    可以利用函数指针 调用函数

  • 函数指针定义方式

    1、先定义出函数类型,再通过类型定义函数指针

    typedef void(FUNC_TYPE)(int, char);

    2、定义出函数指针类型,通过类型定义函数指针变量

    typedef void( * FUNC_TYPE2)(int, char);

​ 3、直接定义函数指针变量

​ void(*pFunc3)(int, char) = func;

  • 函数指针和指针函数

​ 函数指针 指向了函数的指针

​ 指针函数 函数返回值是指针的函数

  • 函数指针数组

    void(*pArray[3])();

  • 函数指针做函数参数(回调函数)

    利用回调函数实现打印任意类型数据

    提供能够打印任意类型数组函数

    利用回调函数 提供查找功能

34.链表反转练习 35.预处理指令
  • 头文件 #include

    <> “”区别

    <> 包含系统头

    “” 包含自定义头

    • 宏常量 大写 可以是常数

      不重视作用域

      没有数据类型

      不做语法检查

      有效范围在文件内

      利用 #undef 卸载宏

    • 宏函数

      将频繁、短小函数写成宏函数

      优点:以空间换时间

  • 条件编译

    #ifdef #else #endif 测试存在

    #ifndef #else #endif 测试不存在

    #if #else #endif 自定义条件编译

  • 特殊宏

    __FILE__ 宏所在文件路径

    __LINE__ 宏所在行

    __DATE__ 宏编译日期

    __TIME__ 宏编译时间

36.生成库配置
  • 静态库配置

    右键项目->属性 ->常规->配置类型 ->静态库

    生成项目 生成.lib文件

    将.lib和 .h交给用户

    测试

    静态库链接在编译期完成,在链接阶段复制到程序中,程序运行时与函数库再无瓜葛,移植方便,浪费空间

  • 动态库配置

    右键项目->属性 ->常规->配置类型 ->动态库 .dll

    生成项目 生成 .lib .dll

    静态库中生成的.lib和动态库生成的.lib是不同的,动态库中的.lib只会放变量的声明和 导出函数的声明,函数实现体放在.dll中

    库中声明 导出函数/外部函数 : __declspec(dllexport)int mySub(int a, int b);

    测试

    ​ 在测试中使用#pragma comment( lib,“./mydll.lib”)

    ​ 或者添加已有文件.dll .lib .h

37.递归函数

本质:函数自身调用自身

注意事项:递归函数必须有结束条件,函数有出口

void reversePrint(char * p)
{
	if (*p == '')
	{
		return;
	}

	reversePrint(p + 1);

	printf("%cn", *p);

}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/835906.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号