指针进阶(2)

news2025/1/11 2:13:51

Tips

1.  

2.  

3.  碰到地址就等价于指针变量,里面存放着该地址的指针变量

4.  数组指针是存放数组的地址,指向的是一个数组;函数指针存放的是函数的地址,指向的是一个函数。

5. 地址就是指针,地址就是指针

6. 数组指针,比如说int(*pa)[10];指针数组是一个数组,里面存放的都是指针,比如说:char* ch[5]; ,函数指针的话就是这样写的:函数返回类型(*pa)(参数数据类型)=函数地址

7.  

函数指针(存放函数地址/指向函数的指针

创建函数指针

1. 函数指针就是指向函数的指针。我们发现:原来函数也是有地址的,比如说只需要&add

2. 那么既然是一个地址,能不能把它存起来?当然。可以用函数指针存放函数地址。

创建格式为:  函数返回类型(*p)(参数数据类型)=&函数名/函数名

如int(*pf)(int, int)=&add。 

3. 函数指针与数组指针非常非常类似。但是有区别:对于数组而言,arr是数组首元素的地址,&arr是整个数组的地址。对于函数而言add与&add两者没有区别,都是函数的地址,两个一样

通过函数指针调用函数

1. 这时候还是得注意:指针的核心与灵魂所在就是解引用操作

2. 比如说我要调用add函数,首先通过指针变量解引用找到函数,然后调用函数的话,传递参数自然不可或缺我就:int sum = (*pf)(2, 3),发现sum就是5,说明调用成功。

3. 而事实上,根据指针变量的转换关系:指针变量的值=指针变量指向哪=指针变量里面存放的地址=指针变量的名字本身=指针变量本内存条里面存放的数据。那么当
int(*pf)(int, int)=add的时候,pf就是add。那么既然pf就是add,我pf(2.3)不就OK了

有趣代码1

1,首先,void( * ) ( )是一个函数指针的指针类型 

2.  把0这个整型强制类型转换成指针类型void( * )( ),于是:( void( * ) ( ) ) 0

3.  强制类型转换完之后,这时候已经变成一个函数指针了,在对它进行解引用调用函数并传递参数。于是有:(* ( void( * ) ( ) ) 0 ) ( ) ;

4.  

5. 关于强制类型转换的理解:
1. 其实这个是涉及到数据类型的知识。
2. 不同的数据类型确定了看待内存数据的视角与解读方式,但是内存里面的二进制补码是岿然不动的,总而言之,就是解读方式发生了变化。
3. 像这个代码里面,原先的话0是一个整型,在属于它的32位二进制内存空间里面每一位都是0。而现在我把它的类型转换为函数指针类型,那么我就认为你们存放的是地址,欸里面是0,这就说明函数指针指向0,会去调用0地址处的函数。 

有趣代码2 

1. 首先,signal是一个函数名称

2. 函数的两个参数类型为int, void( * )( int )。于是: signal ( int , void ( * ) ( int ) ) 

3. 然后signal函数的返回类型是void ( * ) ( int )。于是:

void ( * signal ( int , void ( * ) ( int ) ) ) ( int ); 

4.  

利用typedef简化有趣代码2

注意:对于数组指针啊,函数指针啊,这些指针的指针类型要死的恶心如 

int( * )[10], void( * )(int ,char )等等
这时候其充当typedef简化对象,充当函数定义返回类型,充当数组定义时元素类型都需要形式上发生微调,东西都移到括号里面*的右边

函数指针数组

1. 有很多指针数组,如字符指针数组(存放的是字符指针),如char* arr[10];也有整型指针数组(存放的是整型指针),如int* arr[10]。那么函数指针数组的话,就是用来存放函数指针的。那么该如何写出一个函数指针数组呢?反正就按我的那张表来:如,int(*arr[10])(int, int)。再次强调一下:如果变量与*结合在一起,那么表示是一个指针;如果说与[ ]结合在一起,那么表示是一个数组。然后指针类型的话,一般来说潜意识就能知道。如果碰到有点复杂的,那么就去创建一个指针变量那么走一遍,把表达式去掉变量名就是了,比如说函数指针创建是int(*p)(int, int),那么这个p的类型就是int(*)(int, int)。 

2. 注意:函数指针数组数组里面放的不是函数,数组里面放的是函数的指针也就是地址。

函数指针数组的用途:转移表

写一个计算器整数加、减、乘、除

代码1:

#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
void menu(void)
{
	printf("********************************\n");
	printf("***** 1.add      2. sub   ******\n");
	printf("***** 3.mul      4. div   ******\n");
	printf("***** 0.exit              ******\n");
	printf("********************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
	return 0;
}

上面这个太繁琐,用函数指针数组来实现简洁多了

代码2:

#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
void menu(void)
{
	printf("********************************\n");
	printf("***** 1.add      2. sub   ******\n");
	printf("***** 3.mul      4. div   ******\n");
	printf("***** 0.exit              ******\n");
	printf("********************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int(*arr[5])(int, int) = { 0,add,sub,mul,div };
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出成功\n");
			break;
		}
		else if ((input >= 1) && (input <= 4))
		{
			printf("请输入两数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", (*(arr + input))(x, y));
		}
		else
		{
			printf("输入错误\n");
		}
	} while (input);
	return 0;
}

3. 其实这个函数指针数组有一种跳转的效果。你给我一个下标,我通过这个下标找到数组里面的某个函数指针然后去调用这个函数。因此在有一些叫法里面:这种函数指针数组叫做“转移表”

4. 函数指针数组里面的每一个函数指针它的指针类型必须是一模一样。(因为数组的每个元素类型必须一样),这也就说明函数指针数组里面的每一个函数指针所指向的函数的参数数据类型与个数与返回类型必须一样

指向函数指针数组的指针

1. 我们之前已经知道,int(*p)(int,int)就是函数指针,而int(*p[10])(int, int)就是函数指针数组。之前比如说:int arr[10],那我就可以int (*pA)[10]=&arr。那我现在int(*pf[10])(int, int)就是函数指针数组可不可以&pf呢?当然可以。int(*(*ppf)[10])(int, int)=&pf。这边必须要再次强调一下:如果变量名与*结合,那么就表示是一个指针;如果与[ ]结合,那么就表示是一个数组,至于到底与谁先结合,这就是根据优先级来的。

2.  

3. 

4.  这个本质上就是一个数组指针。因为是指向数组的嘛。

5.  再来回顾一下指针变量的创建。1. *p结合表示这个p一个指针变量。2. 再加上其指向内存空间里面数据的类型

6. 与此同时还要知道,数组的类型是构造类型,如int [10];数组名的类型与&arr的类型都是指针类型,这类型的话一般潜意识就能知道,复杂搞不懂的话创建走一遭去掉p就是。 

回调函数 (函数也是有地址的,地址就是指针)

回调函数不需要像一般的函数这样去定义/声明,好像类似于函数指针里面已经定义好了 

 

 

注: 函数名与函数都是一模一样的,代表函数地址,地址就是指针 

1. 回调函数又是一个比较有意思的东西,我们之前学过函数指针与函数指针数组,说到底这两个东西的核心在于函数指针。因此函数指针非常非常非常非常关键。而函数指针非常重要的应用就是

用函数指针来实现回调函数。 

2.  回调函数就是一个通过函数指针调用的函数。首先,你把函数指针\地址当成一个参数传递给另外一个函数 -> 然后当这个参数指针被用来调用其指向的函数,我们就说这个是回调函数。回调函数不是由本函数的实现方直接调用。而是在特定的事件或条件发生时由另外的一方调用,由于对该事件和条件进行响应。说白了,就是用函数指针调用其指向的函数。

例子1(用回调函数改造上面的计算器)

#include <stdio.h>
void menu()
{
	printf("*******************************\n");
	printf("****** 1. add   2. sub    *****\n");
	printf("****** 3. mul   4. div    *****\n");
	printf("****** 0. exit            *****\n");
	printf("*******************************\n");
}
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
void mul(int x, int y)
{
	return x * y;
}
void div(int x, int y)
{
	return x / y;
}
void calc(int(*pf)(int,int))
{
	printf("请输入两个数字:");
	int x = 0;
	int y = 0;
	scanf("%d %d", &x, &y);
	int ret = pf(x, y);
	printf("%d\n", ret);
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

例子2(qsort函数)

1. 首先先得复习冒泡排序:

void bubble_sort(int* str, int sz)
{
	//趟数
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j <= (sz - (i + 2)); j++)
		{
			if (*(str + j) < *(str + j + 1))
			{
				int tmp = *(str + j);
				*(str + j) = *(str + j + 1);
				*(str + j + 1) = tmp;
			}
		}
	}
}
int main()
{
	int arr[10] = { 2,3,5,6,8,1,4,9,0,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	//打印结果
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

2. 但有缺陷与限制,只能排整数,因为参数在那里写死了。

3. C里面有qsort函数可以对任意元素类型的数组的元素排序

4.  其底层用快速排序而不是冒泡排序,但我们只会冒泡排序。

5. 

6. 看一下qsort的参数:

    1. void* base 表示待排序数组的起始地址

    2. size_t num 表示数组元素个数

    3. size_t width 表示数组一个元素占几个字节

    4. 比较函数 int (*cmp)(const void*, const void*)

7. 想想, 对冒泡排序函数而言:

void bubble_sort(int* str, int sz)
{
	//趟数
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j <= (sz - (i + 2)); j++)
		{
			if (*(str + j) < *(str + j + 1))
			{
				int tmp = *(str + j);
				*(str + j) = *(str + j + 1);
				*(str + j + 1) = tmp;
			}
		}
	}
}

 假设要升级实现不仅能够排整型数组,也可以排序浮点型数组,结构体数组等等,发现:

其实上面这个冒泡排序函数的 i 的循环是不用发生变化的,j 也是不用发生变化的,但是:这个if 语句里面的相邻数值元素的比较方式是要发生变化的 (比如说结构体之间,不能直接大于小于这么简单的去比)那么显然,里面相邻数值元素的交换方式也是要发生变化的

8.  既然要这么变化来变化去,那就这样,我把它抽离出来,这部分到外面来形成一个函数,你来写,写完后你把地址传过来,我用函数指针接受

 

9. 来好好看一下这个比较函数的函数指针参数的形式:

int (*cmp)(const void*, const void*)  第一个const void* 是两相邻元素的第一个元素起始地址,第二个const void* 是两相邻元素的第二个元素起始地址

然后是关于这个void* 的有关内容 

首先:如果   float*的指针 = int*的指针,编译器会报警告的,因为等号两边不兼容 

void*是一个指针类型,是无具体类型的指针, 好处在于是一个通用指针,兼容性极好,可以接受任何类型的指针;坏处在于不能直接解引用操作/++/--(需要强制转换类型)void就是一个瞎子,它不知道怎么去解读二进制补码

10, 接下来,讲讲这个比较函数的返回值int是怎么一回事呢?与strcmp一样,如果第一个数据>第二个数据,就返回大于0的数字;如果第一个数据<第二个数据,就返回小于0的数字,相等的话,就返回0 

用qsort函数实现整型数组的排序

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;
}
int main()
{
	int arr[10] = { 2,3,5,6,8,1,4,9,0,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//打印结果
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

用qsort函数实现结构体数组的排序1

struct Stu
{
	char name[20];
	int age;
};
int cmp_Stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{
	struct Stu arr[3] = { {"zhang",12}, {"li",14} ,{"shen",13} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_Stu_by_age);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s %d\n", (arr + i)->name, (arr + i)->age);
	}
	return 0;
}

用qsort函数实现结构体数组的排序2

#include <string.h>
struct Stu
{
	char name[20];
	int age;
};
int cmp_Stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e2)->name, ((struct Stu*)e1)->name);
}
int main()
{
	struct Stu arr[3] = { {"zhang",12}, {"li",14} ,{"shen",13} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_Stu_by_name);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s %d\n", (arr + i)->name, (arr + i)->age);
	}
	return 0;
}

strcmp比较两个字符串是对应位置字符一对一对比较下去,并且其返回类型恰好与qsort的比较函数的返回类型雷同

用冒泡排序的内核来实现qsort指针

void Swap(char* buf1,char* buf2,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void my_qsort(void* base, size_t sz, size_t width, int (*cmp)(const void*, const void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j <= (sz - (i + 2)); j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
			{
				Swap((char*)base + width * j, (char*)base + width * (j + 1), width);
			}
		}
	}
}
//
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int main()
{
	int arr[10] = { 2,3,5,6,8,1,4,9,0,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//打印结果
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 最后补充:

1. 计算机内存里面的基本单位都是字节,这里面都是二进制补码.

2. 一切都是二进制补码,这就使得数据类型显得尤为重要。

3. 把void*的指针变为char*的指针就可以实现以字节为基本单位的步长。

4.

5. 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146316.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 138. 复制带随机指针的链表(C++)

思路&#xff1a; 用哈希表实现&#xff0c;创建一个哈希表来对应原链表中的每一个节点&#xff0c;这样也可以将原链表中的所有结点的next和random关系映射到哈希表复制链表中。 原题链接&#xff1a;https://leetcode.cn/problems/copy-list-with-random-pointer/description…

1658. 将 x 减到 0 的最小操作数

解法一&#xff1a;双指针 首先&#xff0c;每次操作可以移除数组 nums 最左边或最右边的元素&#xff0c;那么相当于求出l和rl和rl和r使得[0,l][r,n−1][0, l][r,n-1][0,l][r,n−1]之间所有元素之和等于xxx,并且元素个数最少。我们可以通过双重循环枚举l和r变量l和r变量l和r变…

马哥架构第1周课程作业

马哥架构第1周课程作业一. 画图解释一次web请求的过程。涉及tcp/ip, dns, nginx&#xff0c;wsgi。二. 编译安装nginx, 详细解读常用参数。三. 基于nginx完成动静分离部署 lamp。php到后端php-fpm, static/ 在nginx本地。3.1 配置 nginx 实现反向代理的动静分离3.2 准备后端 ht…

equals和==的区别

目录 1.基本数据类型和引用数据类型的说明 2. 3.equals 1.基本数据类型和引用数据类型的说明 基本数据类型&#xff1a;byte&#xff0c;short&#xff0c;int&#xff0c;long&#xff0c;float&#xff0c;double&#xff0c;char&#xff0c;boolean。 对应的默认值&…

2-4进程管理-死锁

文章目录一.死锁的概念二.死锁的处理策略1.死锁预防&#xff1a;破坏必要条件&#xff0c;让死锁无法发生2.避免死锁&#xff1a;在动态分配资源的过程中&#xff0c;用一些算法防止系统进入不安全状态&#xff08;1&#xff09;银行家算法&#xff08;2&#xff09;系统安全状…

Java if else分支结构精讲

Java 支持两种选择语句&#xff1a;if 语句和 switch 语句。其中 if 语句使用布尔表达式或布尔值作为分支条件来进行分支控制&#xff0c;而 switch 语句则用于对多个整型值进行匹配&#xff0c;从而实现分支控制。这些语句允许你只有在程序运行时才能知道其状态的情况下&#…

2022:不恋过往,不畏将来

一、开篇 少年有山海&#xff0c;踏过皆繁华。岁月不居&#xff0c;时节如流&#xff0c;时间在指尖悄悄流逝&#xff0c;人生即将翻开新的一年的篇章。2022年&#xff0c;注定是一个不平凡的年份&#xff0c;这一年&#xff0c;我们从关心世界到关心国家&#xff0c;最后关心自…

2023年12306购票平台自动化购票二|解决车次查找与预定

目录 一、说明 1.1、背景 1.2、说明 二、步骤 2.1、点击去购票 2.2、在搜索框中输入车次信息 2.3、点击查找 2.4、出现车次信息&#xff0c;进行筛选&#xff0c;如果有票则点击计入预定车票界面 三、结果 四、小节 一、说明 1.1、背景 接上文&#xff0c;春运抢不到…

适用于 Windows 的 5 大 PDF 编辑器

“如何在 Windows 7/8/10/11 上编辑 PDF 文件&#xff1f;” “适用于 Windows 7/8/10/11的最佳 PDF 编辑器是什么&#xff1f;” 升级到 Windows 7/8/10/11 后&#xff0c;你会发现很多应用程序在新的 Windows 系统上无法运行&#xff0c;包括 PDF 编辑器。然而&#xff0c;一…

POJ 3070 Fibonacci

Time Limit: 1000MSMemory Limit: 65536KTotal Submissions: 30932Accepted: 20284 Description In the Fibonacci integer sequence, F0 0, F1 1, and Fn Fn − 1 Fn − 2 for n ≥ 2. For example, the first ten terms of the Fibonacci sequence are: 0, 1, 1, 2, 3,…

opencv源码之中值滤波medianBlur_SortNet解读

背景中值滤波&#xff0c;最大值滤波&#xff0c;最小值滤波属于排序滤波&#xff0c;常用于图像去噪处理。最大/小值滤波的处理比较好理解&#xff0c;就是逐个比较窗口内的每个数字&#xff0c;每次比较会根据所属任务保留最大值&#xff0c;或最小值。假设滑动窗口是3*3&…

固体物理分子模拟实验(二)MPI的安装

固体物理分子模拟实验&#xff08;二&#xff09;MPI的安装 文章目录固体物理分子模拟实验&#xff08;二&#xff09;MPI的安装前言一、MPI是什么&#xff1f;二、安装步骤&#xff08;Ubuntu22.04mpich-4.0.2&#xff09;1、下载mpich解压包2、安装前置组件3、文件配置&#…

【一文讲通】如何检测数据满足同分布

1 统计指标的方法1.1群体稳定性指标&#xff08;Population Stability Index&#xff0c;PSI&#xff09;群体稳定性指标&#xff08;Population Stability Index&#xff0c;PSI&#xff09;&#xff0c; 衡量未来的样本&#xff08;如测试集&#xff09;及训练样本评分的分布…

【Linux】基础开发工具使用 --- vim

目录 前言 vim的基本概念 具体操作 插入模式 命令模式下的指令 底行模式下的指令 vim的配置 前言 &#x1f367;了解了 Linux 的一些基本的指令之后若要在 Linux 上进行程序的编写&#xff0c;除了 nano 以外&#xff0c;我们还可以选择 vim 进行编写。而 vim 是 vi 升级…

OJ万题详解––孤独的照片(C++详解)

题目 题目描述 Farmer John 最近购入了 N 头新的奶牛()&#xff0c;每头奶牛的品种是更赛牛&#xff08;Guernsey&#xff09;或荷斯坦牛&#xff08;Holstein&#xff09;之一。 奶牛目前排成一排&#xff0c;Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。 然…

0107-JAVA和JDK的区别

前言 因为工作需要现在也不得不接触后端java语言&#xff0c;对于java和jdk一直存在疑惑&#xff0c;今天就详细总结一下 1.什么是java 人话就是java是一门后端脚本语言和PHP一样 2.什么是jdk JDK的全称是Java Development Kit&#xff0c;直译就是&#xff1a;Java开发工…

真实地址查询——DNS

通过浏览器解析 URL 并生成 HTTP 消息后&#xff0c;需要委托操作系统将消息发送给 Web 服务器。但在发送之前&#xff0c;还有一项工作需要完成&#xff0c;那就是查询服务器域名对应的 IP 地址&#xff0c;因为委托操作系统发送消息时&#xff0c;必须提供通信对象的 IP 地址…

java 基础 - 泛型

泛型 术语中文含义举例Parameterized type参数化的类型ListActual typeparameter实际类型参数StringGeneric type泛型类型ListFormal typeparameter形式类型参数 EUnbounded wildcard type无限制通配符类型List<?>Raw type原始类型ListBounded type parameter有限制类型…

Vue--》Vue3给数据共享增添的改变

目录 数据共享 父向子共享数据 子向父共享数据 父子组件间数据双向同步 兄弟组件共享数据 后代组件共享数据 使用Vue3的setup函数实现后代数据共享 数据共享 在项目开发中&#xff0c;组件之间的关系分为如下3种&#xff1a;父子关系、兄弟关系、后代关系。 父向子共享…

FPGA学习笔记(十二)IP核之FIFO的学习总结

系列文章目录 一、FPGA学习笔记&#xff08;一&#xff09;入门背景、软件及时钟约束 二、FPGA学习笔记&#xff08;二&#xff09;Verilog语法初步学习(语法篇1) 三、FPGA学习笔记&#xff08;三&#xff09; 流水灯入门FPGA设计流程 四、FPGA学习笔记&#xff08;四&…