[C/C++]指针详讲-让你不在害怕指针

  •  个人主页:北·海
  •  🎐CSDN新晋作者
  •  🎉欢迎 👍点赞✍评论⭐收藏
  • ✨收录专栏:C/C++
  • 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗

目录

前言

 一.&与*的作用与结合方向

二.指针和数组的关系

1.利用指针访问数组里的值

2.利用指针将数组作为实参传递给函数

 三.字符指针与字符数组

四.指针在内存分配中的应用

1.用C语言中的malloc在堆区分配动态内存

1.用C++中的new在堆区分配动态内存

五.使用指针为什么能提高程序的性能

六.指针与const搭配的四种情况

七.引用是实现原理

八.多级指针

九. 指针和函数的关系

1.通过指针使函数返回多个值

2.函数指针

3.函数的返回值类型为指针类型

十.指针和结构体的关系

 


前言

指针在编程语言中扮演着重要的角色,特别是在C语言中。指针提供了直接访问和操作内存地址的能力,使得编程更加灵活、高效,并且可以处理各种复杂的数据结构和算法。

以下是指针的几个重要方面和作用:

  1. 内存管理:指针允许程序直接与内存交互。通过使用指针,可以动态地分配和释放内存,避免了静态内存分配所带来的限制。这在处理动态数据结构(如链表、树、图等)和大规模数据中非常有用。

  2. 传递参数和引用:通过指针,可以在函数之间传递参数和引用。传递指针作为函数参数,可以避免在函数调用时产生副本,节省了内存和时间开销。此外,指针还可以用于函数返回多个值或修改调用者的变量。

  3. 动态数据结构:动态数据结构如链表、树、堆等通常需要使用指针进行内存分配和组织。指针可以在运行时创建、删除、连接和重新组织数据结构,使得数据结构的管理更加灵活和高效。

  4. 数组和字符串操作:在C语言中,数组实际上是通过指针来访问和操作的。指针使得数组可以直接访问和修改其元素,还可以进行指针算术操作,实现数组的遍历和操作。字符串在C语言中本质上是以空字符结尾的字符数组,指针的使用是对字符串进行处理的关键。

  5. 性能优化:指针的使用可以提高程序的性能。通过使用指针来访问和操作数据,可以减少副本的生成和数据的复制,提高程序的执行效率。


 一.&与*的作用与结合方向

作用: &在等号的右边为取地址符号,在等号的左边为引用符号

         *可以用做为乘号,也可用作为对指针变量解引的符号,取该地址存放的数据

结合方向:

“&”和“*”都是右结合的。假设有变量 x = 1,则*&x 的含义是,先获取变量 x 的地址,再获取地址中的内容。因为“&”和“*”互为逆运算,所以 x = *&x。

例如:输入 x、y 两个整数,然后将其中的值大的赋值给 x,小的赋值给 y。即:假设输入 x = 8,y = 9。就将 9 赋值给 x,8 赋值给 y。

void main(){
	//声明两个普通变量
	int x, y;
	//声明两个指针变量
	int *px, *py;
	//声明一个临时变量,用于交换
	int t;
	//输入两个值,赋值给 x、y
	scanf("%d", &x);
	scanf("%d", &y);
	//给指针变量 px、py 赋初值(关联变量 x、y)
	px = &x;
	py = &y;
	//利用指针来对比 x、y 的值,如果 x 的值比 y 的值小,就交换
	if(*px < *py){
		//交换步骤,其中*px == x、*py == y
		t = *px;
		*px = *py;
		*py = t;
	}
	printf("x =  %d, y = %d", *px, *py);
}

二.指针和数组的关系

1.利用指针访问数组里的值

	//利用指针访问数组里面的值
	int nums[] = {1,2,3,4};
	//方法一:下标法
	cout << "第一个数 :" << nums[0] << endl;
	//方法二:指针法
	cout << "第一个数 :" << *(nums + 0) << endl;//数组名就是数组元素的首地址

2.利用指针将数组作为实参传递给函数

void Test1(int nums[],int n) {
	for (int i = 0; i < n; i++) {
		cout << nums[i] << " ";
	}
}

int main() {
	//利用指针访问数组里面的值
	int nums[] = {1,2,3,4};
	int* p = nums;
	//利用指针将数组作为实参传递给函数
	Test1(nums, 4);//数组名就是数组元素的首地址
	//方法二
	Test1(p, 4);//p指向数组,再将指向数组的指针传给被调函数
}

补充对指针加一 : 例如p++,不会将地址的值加一,而是将数组下移一位

 三.字符指针与字符数组

C语言中,没有字符串类型的变量,通常用字符数组或者字符指针存放字符串

1.字符数组的声明

	char str[] = "C++";
	char str1[] = { 'C','+','+' ,'\0'};
   字符数组的声明方式,在使用第二种声明方式的时候,必须在最后加上\0,此符号代表在这个地方该数组就结束了,如果不加该符号,会访问出界的

  字符数组的访问

	char str[] = "C++";
	char str1[] = { 'C','+','+' ,'\0'};
	printf("%s\n", str1);//%s格式化,表示直接输出整个数组
	printf("%c\n", str[0]);//利用数组法取str第一个字符
	printf("%c\n", *(str1 + 1));//利用指针法取str1的第2个字符

由上面可以看出,可以通过%c一个字符一个字符的输出,那么也可也利用循环输出每个字符

	for (int i = 0; i < strlen(str); i++) {
		printf("%c", str[i]);
	}

还可以用过strlen(str)或者sizeof(str)/sizeof(char)来获取该数组的长度

2.字符指针的声明

char *word = "have a good night";

  字符指针的访问

	const char* word = "have a good night";
	printf("%s\n", word);

	printf("%c\n", word[0]);

	for (int i = 0; i < strlen(word); i++) {
		printf("%c", word[i]);
	}

    对字符指针地址的加减

	const char* word = "have a good night";
	word++;
	printf("%s\n", word);//此时就会将指针下移到第一个a的地方,从a的地方进行输出,
    输出为:ave a good night
	word--;
	printf("%s\n", word);//输出为:have a good night

	const char str[] = "C++";
	str++;//报错,在字符数组中不能进行加减操作,表达式必须是可修改的左值
	printf("%s", str);

四.指针在内存分配中的应用

1.用C语言中的malloc在堆区分配动态内存

1.利用malloc创建一维数组

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	int n;
	int* p = nullptr;
	cout << "请输入要存放的数据个数:";
	cin >> n;
	//在堆区分配内存
	p = (int *)malloc(n * sizeof(int));
	//输入
	for (int i = 0; i < n; i++) {
		cin >> *(p+i);
	}
	//输出
	for (int i = 0; i < n; i++) {
		cout << *(p+i) << " ";
	}
	free(p);
}

用malloc进行分配,注意malloc的返回值类型位void*类型,需要进行强制类型转换,由于在堆区分配的动态内存,所以在使用完之后,为了防止内存泄漏,需要用free进行释放

2.利用malloc进行开辟二位数组,与释放二位数组

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	int row = 0 ,col = 0 ;
	int** p = nullptr;
	cout << "依此输入行和列:" << endl;
	cin >> row>>col;
	//在堆区分配内存
	p = (int **)malloc(row * sizeof(int*));
	for (int i = 0; i < row; i++) {
		*(p + i) = (int*)malloc(col * sizeof(int));
	}
	//输入
	for (int i = 0; i < row * col; i++) {
		cin >> p[i / col][i % col];
	}

	//输出
	for (int i = 0; i < row * col; i++) {
		cout << p[i / col][i % col] << " ";
	}
	//释放动态开辟的二维数组
	for (int i = 0; i < row; i++) {
		free(p[i]);
	}
	free(p);
}

原理是,先创建一个指针数组,然后在利用改一级指针去创建动态int类型数组,在输入时候,用到了一个小技巧,用一层循环给二维数组赋值,在使用二维数组时候,应该先将每一层一级指针指向的内存块释放掉,然后再去释放该释放二级指针本身指向的内存块

1.用C++中的new在堆区分配动态内存

用到C++中的new分配内存,用delete释放内存,这里的new和delete只适用于c++中,malloc与free适用于c/c++,直接上例子

1.使用new进行分配一个动态的一维数组

int main() {
	int n;
	cout << "输入数组的大小:";
	cin >> n;
	int* p = new int[n];
	//输入
	for (int i = 0; i < n; i++) {
		cin >> *(p + i);
	}
	//输出
	for (int i = 0; i < n; i++) {
		cout << *(p + i) << " ";
	}

	delete[]p;
}

2.创建二维数组

int main() {
    int rows = 3;
    int cols = 4;

    // 创建动态二维数组
    int** array = new int*[rows];  // 创建一级指针数组

    for (int i = 0; i < rows; i++) {
        array[i] = new int[cols];  // 创建二级指针数组
    }

    // 使用动态二维数组
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            array[i][j] = i * cols + j;  // 给数组元素赋值
        }
    }

    // 打印动态二维数组
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            std::cout << array[i][j] << " ";
        }
        std::cout << std::endl;
    }

    // 释放动态二维数组的内存
    for (int i = 0; i < rows; i++) {
        delete[] array[i];  // 释放二级指针数组
    }
    delete[] array;  // 释放一级指针数组

    return 0;
}

和malloc的原理都是一样的,起始可以将一维数组创建的长一点,利用p[i / col][i % col];完全可以将一位数组当二维数组用,二维数组的存储方式也是按行存储的,地址都是连续的

在使用delete时候,数组的释放格式为 delete []p;,单个变量的释放格式为 delete p;

五.使用指针为什么能提高程序的性能

  • 直接访问内存:指针允许直接访问和操作内存中的数据。相比于通过变量的拷贝进行操作,直接访问内存可以减少数据的复制和移动,从而提高程序的执行效率。

#include <iostream>
using namespace std;
void Test1(int *p) {
	(*p)++;
}
void Test2(int m) {
	m++;
}
int main() {
	int num1 = 10,num2=10;
	Test1(&num1);
	cout << "Test1 :" << num1 << endl;//输出结果11
	Test2(num2);
	cout << "Test2 :" << num2 << endl;//输出结果10
}

Test1通过直接访问内存,避免了数据的拷贝和移动,提高了程序的执行效率。

Test2函数通过传递值的拷贝来操作变量。当我们调用该函数后,原始变量的值保持不变

  • 减少内存和时间开销:通过传递指针作为函数参数,可以避免在函数调用时产生变量的副本,从而减少内存的使用和传输的时间开销。特别是在处理大型数据结构或大量数据时,使用指针可以显著减少内存和时间的消耗。

  • 动态内存分配:指针使得动态内存分配成为可能,即在程序运行时根据需要分配和释放内存。相比于静态内存分配,动态内存分配可以更灵活地管理内存,避免内存浪费和限制,并且减少了程序启动时的内存占用。

六.指针与const搭配的四种情况

  • const 写在int之前,则限定不能通过*指针去改变该指针指向的值,但是可以指向别的指针
  • const 写在int之后,则限定可以通过*指针去改变该指针指向的值,但是不能指向别的指针
  • 两个const一个写在int前,一个写在变量名前,限制指针不能指向别的指针,并且不允许修改指针指向的值
  • 总结 : 看const离类型(int)近,还是理变量名近,离谁近,就修饰谁,谁就不能变
#include<iostream>
using namespace std;

int main() {
	int wife = 30;
	int girl = 18;

	//第一种 : 渣男型,普通指针可以随意更改指向与指向地址的值
	int* zha_nan = &wife;
	cout << *zha_nan << endl;

	zha_nan = &girl;
	cout << *zha_nan << endl;

	//第二种 : 直男型,以自我为中心,可以改变指向,但是不可以改变指向的地址的值

	const int* zhi_nan = &wife;
	//*zhi_nan = 25;//报错,不能改变值
	zhi_nan = &girl;
	cout << *zhi_nan << endl;

	//第三种 : 暖男型,专一,不可以改变指向,但是可以改变指向的地址的值

	int* const nuan_nan = &wife;
	//nuan_nan = &girl;//报错,不能改变指向
	*nuan_nan = 25;
	cout << *nuan_nan << endl;

	//第四种 : 超级暖男型,超级专一,不能改变指向,也不能改变指向地址的值

	const int* const _super_nuan_nan = &wife;
	//*_super_nuan_nan = 25;//报错,不能改变指向地址的值
	//super_nuan_nan = &girl;//报错,不能改变指向

	//总结 : const理谁近就修饰谁,理(int)近,则修饰该指针的值不能改变,修饰变量,
	//	    则该指针不能在指向别的变量了
}

七.引用是实现原理

引用的底层也是用指针进行实现的

	int a = 0;
	int b = 2;
	int& c = a;
	c = b;//c是a的别名,将b的值赋值给c,就相当于将b的值也赋值给了a
	cout << c << endl;//2
	cout << a << endl;//2
	//引用变量只能初始化一次,很像 int* const c = a;
    //该指针也只能进行一次初始化,就可以猜测引用的底层也是用该类型指针实现的

在使用引用的时候,编辑器会将引用类型转换为int * const 类型

例子:

void swap1(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}
void swap2(int* const a,int* const b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}
int main() {

	int a = 1;
	int b = 2;
	
	swap1(a, b);//引用实现
	cout << "a = " << a << " b = " << b << endl;//a = 2,b =1
	swap2(&a, &b);
	cout << "a = " << a << " b = " << b << endl;//a = 2,b =1
}

对照swap1和swap2,在形参方面,引用变量会被改为int * const类型,在实参方面会将传入整数改为传入地址

八.多级指针

举例二级指针,懂了引用变量的话,那么对二级指针就可以有个优化了,提出指针引用,代码如下:

void home(int * p) {
	int b = 12;
	p = &b;
}
int main() {
	int a = 10;
	int* p = &a;
	//让p存放b的地址
	home(p);
	cout << *p << endl;//10
}

可以看出以上代码是无法改变p的指向的,改变值需要用一级指针,改变一级指针的指向需要用到二级指针,一次类推,这里直接就不写二级指针了,直接用一级指针的引用代替二级指针

void home(int *& p) {
	int b = 12;
	p = &b;
}
int main() {
	int a = 10;
	int* p = &a;
	//让p存放b的地址
	home(p);
	cout << *p << endl;//12
}

由此可以看出,利用指针引用可以将p的指向改变,这也的引用可以增强代码的可读性,与简洁性

九. 指针和函数的关系

1.通过指针使函数返回多个值

比如给你了一个已经初始化的数组,需要定义一个函数,用于返回这个数组的最大值与次大值,这个时候,为了增强代码的可读性,尽量返回一个数组,因为当返回的数据多了,也只能用返回数组实现了,代码如下:

void _max(int *nums,int n,int* exterm) {
	exterm[0] = nums[0];
	exterm[1] = nums[0];

	for (int i = 0; i < n; i++) {
		//大于次大的
		if (nums[i] > exterm[1]) {

			if (nums[i] > exterm[0]) {
				//大于最大的
				exterm[1] = exterm[0];
				exterm[0] = nums[i];
			}

			if (nums[i] < exterm[0]) {
				//小于最大的
				exterm[1] = nums[i];
			}
		}
	}
}

int main() {
	//求该数组中的最大值与次大值
	int nums[] = { 2,6,4,9,5 };
	int exterm[2] = { 0 };
	_max(nums, 5, exterm);

	cout << exterm[0] << " " << exterm[1] << endl;//9,6
}

2.函数指针

函数指针是指向函数的指针变量。它可以存储函数的地址,并允许我们通过该指针调用相应的函数。函数指针在C和C++中都有广泛的应用,可以用于回调函数、函数参数以及实现函数的动态调用等场景。

要理解函数指针,首先需要了解函数的定义和函数指针的声明以及函数指针的使用方式。

1.函数定义:
返回类型 函数名(参数列表) {
    // 函数体
}

2.函数指针的声明:
返回类型 (*指针变量名)(参数列表);

指针变量名:函数指针的名称。
*:用于指明该变量是一个指针。
返回类型:函数指针指向的函数的返回类型。
参数列表:函数指针指向的函数的参数列表。

3.将函数指针指向函数
指针变量名 = 函数名;

4.通过函数指针调用函数:
(*指针变量名)(参数列表);//方法一
(指针变量名)(参数列表);//方法二

举例:

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 声明函数指针
    int (*p)(int, int);

    // 将函数指针指向add函数
    p = add;

    // 通过函数指针调用add函数
    int result = (*p)(10, 5);
    std::cout << "Add: " << result << std::endl;

    // 将函数指针指向subtract函数
    p = subtract;

    // 通过函数指针调用subtract函数
    result = (*p)(10, 5);
    std::cout << "Subtract: " << result << std::endl;

    return 0;
}

3.函数的返回值类型为指针类型

int* creat() {
	return new int(1);//创建一个int类型的变量初始化为1,动态分配的数组不能进行初始化	
}

int main() {

	//函数的返回值类型为指针变量类型,例子,
    //在creat函数里面动态的分配内存,将指向该段内存的指针返回
	int* p = creat();
	cout << "*p :" << *p << endl;//*p : 1

    delete p;
}

十.指针和结构体的关系

利用指针访问结构体里面的值

struct Test
	{
		int a;
		int b;
		int c;
	};
	struct Test ss = { 2,3,4 };
	//声明了结构对象ss,并把ss 的成员初始化为2,3 和4。
	struct Test* ptr = &ss;
	//声明了一个指向结构对象ss 的指针。它的类型是
	//Test *,它指向的类型是Test 。
	printf("%d", ptr->a);
	printf("%d", ptr->b);
	printf("%d", ptr->c);

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring cloud、gradle、父子项目、微服务框架搭建---spring secuity oauth2、mysql 授权(九)

文章目录 一、二、授权服务2.1 初始化表结构2.2 引入依赖2.3 自定义 用户详情类 UserDetailsService2.4 授权配置 AuthorizationServerConfiguration2.5 Web安全配置 WebSecurityConfiguration2.6 默认生成接口 三、资源服务3.1 引入依赖3.2 资源服务 ResourceServerConfig 四、…

本地镜像管理

查看 用户可以通过docker images命令查看本地所有镜像&#xff0c;如下&#xff1a; 这里一共有五个参数&#xff0c;含义分别如下&#xff1a; REPOSITORY 仓库名称&#xff0c;仓库一般用来存放同一类型的镜像。仓库的名称由其创建者指定。如果没有指定则为<none>。…

爬虫逆向实战(二十七)--某某招标投标网站招标公告

一、数据接口分析 主页地址&#xff1a;某网站 1、抓包 通过抓包可以发现数据接口是page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现&#xff0c;请求参数是一整个密文 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 通…

Python之动态规划

序言 最近在学习python语言&#xff0c;语言有通用性&#xff0c;此文记录复习动态规划并练习python语言。 动态规划&#xff08;Dynamic Programming&#xff09; 动态规划是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家…

【DevOps视频笔记】8. Jenkins 配置

一、Jenkins 入门配置 1. 工具 / 插件 介绍 二、插件和工具配置 1. 配置 JDK 和 Maven Stage 1&#xff1a;将服务器中 JDK 和 Maven 映射到 jenkins 容器中 Stage 2&#xff1a;jenkins 全局配置中 -- 指定JAVA_HOME目录 Stage 3&#xff1a;jenkins 全局配置中 -- 指定…

Vue 项目性能优化 — 实践指南

前言 Vue 框架通过数据双向绑定和虚拟 DOM 技术&#xff0c;帮我们处理了前端开发中最脏最累的 DOM 操作部分&#xff0c; 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM&#xff1b;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题&#xff0c;所…

深度图相关评测网站

文章目录 1 单目/Stereo相关测评网站介绍12 单目/Stereo相关测评网站介绍23 单目/Stereo相关测评网站介绍3 1 单目/Stereo相关测评网站介绍1 https://vision.middlebury.edu/stereo/eval3/ 2 单目/Stereo相关测评网站介绍2 http://www.cvlibs.net/datasets/kitti/eval_stereo…

maven推包The environment variable JAVA_HOME is not correctly set

解决办法&#xff1a; 打开idea查看jdk安装位置 1.在/etc下面创建&#xff08;如果存在就是更新&#xff09;launchd.conf。里面添加一行&#xff1a; setenv JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_351.jdk/Contents/Home #JAVA_HOME后面是我的java安装路径…

vscode GDB 调试linux内核 head.S

遇到的问题 此前参考如下文章 https://zhuanlan.zhihu.com/p/510289859 已经完成了在ubuntu 虚拟机用vscode 调试linux 内核。但是美中不足的是&#xff0c;断点最早只能加在__primary_switched() 函数。无法停在更早的断点上&#xff0c;比如ENTRY(stext) 位置。参考《奔跑吧…

数据库的备份与分类以及日志

目录 1、数据库的概念 1.1、数据备份的重要性 1.2、造成数据丢失的原因 1.3、 数据库备份的分类 1.3.1、从物理与逻辑的角度&#xff0c; 1.3.2、原理图 1.3.3.1 完全备份&#xff1a; 1.3.2.2 差异备份 1.2.3.3、 增量备份 1.3.3、 备份方式比较 1.4、常见的备份方…

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

arm版Linux下安装es集群

背景&#xff1a;由于生产上网络没通&#xff0c;没办法&#xff0c;只能自己安装一个es集群的测试环境了&#xff0c;我的电脑是Mac M2&#xff0c;安装的Linux是centos7&#xff0c;也是arm版的。 第一步&#xff1a;查看自己Linux系统的版本 命令&#xff1a;uname -a 例如…

Midjourney学习(二)参数的基础

prompt的组成 prompt 可以由三部分组成&#xff0c; 第一部分是垫图部分&#xff0c;也就是一张网络图片 第二部分是文本描述内容 第三部分则是参数 参数列表 --aspect <value> 或者 --ar <value> 控制画面的比例&#xff0c;横竖比例 --version <value> -…

批量身份证图片转Excel,核验真伪,保留头像,只需一款软件

你是否曾经遇到过需要将大量员工的身份证图片转化为Excel表格的情况&#xff1f;这种情况可能会让你感到无从下手。但是&#xff0c;现在有了金鸣表格文字识别电脑客户端&#xff0c;一切都将变得轻松便捷。 首先&#xff0c;你只需要前往金鸣识别官网下载并安装金鸣表格文字识…

电缆厂 3D 可视化管控系统 | 图扑数字孪生

近年来&#xff0c;我国各类器材制造业已经开始向数字化生产转型&#xff0c;使得生产流程变得更加精准高效。通过应用智能设备、物联网和大数据分析等技术&#xff0c;企业可以更好地监控生产线上的运行和质量情况&#xff0c;及时发现和解决问题&#xff0c;从而提高生产效率…

vue项目静态文件资源下载

业务场景&#xff1a;页面有一个导入功能&#xff0c;需要一个模板文件供下载&#xff0c;文件放在本地。 对于 Vue 3 Vite 项目&#xff0c;使用 require 方法来导入模块是不被支持的。require 是 CommonJS 规范中用于模块导入的方法&#xff0c;在 Webpack 等构建工具中常用…

编绎和优化,脚本代码小米加步枪赶超英法美

编程达人&#xff1a;冰冻牡蛎 测试&#xff0c;总结》》 今有空&#xff0c;继续看了一下竹笋大师几天前提出的“使用for循环查找10亿内可被7整除的数的个数”的题目&#xff08;相关文件&#xff1a;群文件 10亿以内多少个数字可以整除7.7z &#xff09; 1. 论输出的exe大小…

matlab使用教程(27)—微分代数方程(DAE)求解

1.什么是微分代数方程&#xff1f; 微分代数方程是一类微分方程&#xff0c;其中一个或多个因变量导数未出现在方程中。方程中出现的未包含其导数的变量称为代数变量&#xff0c;代数变量的存在意味着您不能将这些方程记为显式形式 y ′ f t , y 。相反&#xff0c;您可以…

Excel:通过Lookup函数提取指定文本关键词

函数公式&#xff1a;LOOKUP(9^9,FIND($G 2 : 2: 2:G 6 , C 2 ) , 6,C2), 6,C2),G 2 : 2: 2:G$6) 公式解释&#xff1a; lookup第一参数为9^9&#xff1a;代表的是一个极大值的数据&#xff0c;查询位置里面最接近这一个值的数据&#xff1b;lookup第二参数用find函数代替&am…

springboot第37集:kafka,mqtt,Netty,nginx,CentOS,Webpack

image.png binzookeeper-server-start.shconfigzookeeper.properties.png image.png image.png 消费 image.png image.png image.png image.png image.png image.png image.png image.png image.png Netty的优点有很多&#xff1a; API使用简单&#xff0c;学习成本低。功能强大…