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

news2024/11/15 21:19:27
  •  个人主页:北·海
  •  🎐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);

 

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

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

相关文章

【Vuex状态管理】Vuex的基本使用;核心概念State、Getters、Mutations、Actions、Modules的基本使用

目录 1_应用状态管理1.1_状态管理1.2_复杂的状态管理1.3_Vuex的状态管理 2_Vuex的基本使用2.1_安装2.2_创建Store2.3_组件中使用store 3_核心概念State3.1_单一状态树3.2_组件获取状态3.3_在setup中使用mapState 4_核心概念Getters4.1_getters的基本使用4.2_getters第二个参数4…

c++ boost::json

Boost社区12月11日发布了1.75版本&#xff0c;在之前&#xff0c;​​Boost使用Boost.PropertyTree解析​​JSON​​​&#xff0c;​​XML​​​&#xff0c;​​INI​​​和​​INFO​​​格式的文件。但是由于成文较早及需要兼容其他的数据格式&#xff0c;相比较于其他的​…

怎么自学嵌入式?

嵌入式开发&#xff0c;简直就是一个无穷无尽的知识海洋&#xff01;你永远都学不够&#xff01;当然啦&#xff0c;这并不意味着你得花个三五年才能掌握。如果你只是想找嵌入式开发的工作&#xff0c;其实只需花三五个月有针对性地学习&#xff0c;就可以完全hold住。本文就给…

MybatisPlus核心功能

文章目录 一、前言二、核心功能2.1、条件构造器2.1.1、基础查询条件2.1.2、复杂查询条件2.1.3、动态查询条件2.1.4、查询结果排序2.1.5、执行查询 2.2、主键策略2.2.1、自增主键策略2.2.2、UUID 主键策略2.2.3、雪花算法主键策略2.2.4、自定义 ID 生成策略 三、总结 一、前言 …

学习node之——如何在项目中使用MySQL、前后端的身份认证

上一篇文章只写了一丢丢&#xff0c;这篇才是正片&#xff0c;look look look 一、使用mysql模块操作数据库 1、查询数据 这里连接数据库的用户和密码都是我们在安装mysql时配置的密码。每个人的users表格里面数据不同&#xff0c;结果也会不一样哟&#xff01; // 导入mys…

使用Spring Boot和Kafka实现消息发送和订阅

文章目录 一&#xff0c;新建Spring Boot1&#xff0c;Maven配置2&#xff0c;无法识别为SpringBoot项目3&#xff0c;无效的源发行版4&#xff0c;无法访问SpringApplication5&#xff0c;运行直接Finish6&#xff0c;服务运行成功 二&#xff0c;安装启动Kafka1&#xff0c;下…

从零开始学习 Java:简单易懂的入门指南之查找算法及排序算法(二十)

查找算法及排序算法 常见的七种查找算法&#xff1a;1. 基本查找2. 二分查找3. 插值查找4. 斐波那契查找5. 分块查找6. 哈希查找7. 树表查找 四种排序算法&#xff1a;1. 冒泡排序1.1 算法步骤1.2 动图演示1.3 代码示例 2. 选择排序2.1 算法步骤2.2 动图演示 3. 插入排序3.1 算…

基于ETLCloud的自定义规则调用第三方jar包实现繁体中文转为简体中文

背景 前面曾体验过通过零代码、可视化、拖拉拽的方式快速完成了从 MySQL 到 ClickHouse 的数据迁移&#xff0c;但是在实际生产环境&#xff0c;我们在迁移到目标库之前还需要做一些过滤和转换工作&#xff1b;比如&#xff0c;在诗词数据迁移后&#xff0c;发现原来 MySQL 中…

部署问题集合(二十二)Linux设置定时任务,并设置系统时间

前言 因为项目中经常用到定时任务&#xff0c;特此总结记录一下 步骤 大部分虚拟机创建后就自带定时服务&#xff0c;直接用命令就好编辑定时任务&#xff1a;crontab -e&#xff0c;在该文件下添加如下内容开机自启&#xff1a;reboot /home/autoRun.sh定时执行&#xff1a…

基于java+springboot+vue的交流互动系统-lw

​ 系统介绍&#xff1a; 随着现在网络的快速发展&#xff0c;网上管理系统也逐渐快速发展起来&#xff0c;网上管理模式很快融入到了许多企业的之中&#xff0c;随之就产生了“交流互动系统”&#xff0c;这样就让交流互动系统更加方便简单。 对于本交流互动系统的设计来说&a…

字节一面:你能讲一下跨域吗

前言 最近博主在字节面试中遇到这样一个面试题&#xff0c;这个问题也是前端面试的高频问题&#xff0c;作为一名前端开发工程师&#xff0c;我们日常开发中与后端联调时一定会遇到跨域的问题&#xff0c;只有处理好了跨域才能够与后端交互完成需求&#xff0c;所以深入学习跨域…

STM32F103驱动oled显示屏

STM32F103驱动oled显示屏 一.了解oled显示屏二.IIC协议驱动oled显示屏2.1 oled.c2.2 oled.h 三.效果展示 一.了解oled显示屏 oled显示屏和其他显示屏类似&#xff0c;不过他只有0.96英寸&#xff0c;屏幕较小&#xff0c;但是使用起来比较方便。有二种驱动方式&#xff0c;分别…

Python学习笔记——从面试题出发学习Python

Python学习笔记——从面试题出发学习Python Python学习笔记——从面试题出发学习Python1. 可变数据类型与不可变数据类型&#xff0c;深拷贝与浅拷贝&#xff0c;函数参数的传递机制1.1 变量与对象1.2 可变数据类型与不可变数据类型1.3 深拷贝与浅拷贝1.4 函数参数的传递机制1.…

SIP对讲求助终端,带功放输出

SV-7011TP SIP对讲求助终端&#xff0c;带功放输出 一、描述 网络对讲终端SV-7011TP&#xff0c;SV-7011TP能处理tcp/ip网络音频流&#xff0c;并驱动扬声器进行播音的终端&#xff0c;主要用于公共数字广播&#xff0c;媒体教学&#xff0c;报警等需要数字音频的领域。 SV-…

LinearAlgebraMIT_12_Graph

x.1 用Incidence matrix关联矩阵表示图 矩阵将图的关系数学表达了出来&#xff0c;如下&#xff0c; x.2 图的性质 如果一个数据结构是图&#xff0c;则意味着其组成关联矩阵的向量组是线性相关的&#xff0c;如果数据结构是树则线性无关。 通过对图的了解&#xff0c;我们可…

激活函数总结(二十四):激活函数补充(SquaredReLU、ModReLU)

激活函数总结&#xff08;二十四&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 SquaredReLU激活函数2.2 ModReLU激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、ELU、SELU、GELU、Soft…

【iOS】折叠cell

文章目录 前言一、实现效果二、折叠cell的实现原理三、实现折叠cell的高度变化四、实现选中点击的单元格总结 前言 在暑假的3GShare中用到了折叠cell控件&#xff0c;特此总结博客记录 一、实现效果 二、折叠cell的实现原理 首先我们需要知道ScrollView的是TableView的父类&a…

c++11 标准模板(STL)(std::basic_ostringstream)(四)

定义于头文件 <sstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_ostringstream;(C11 前)template< class CharT, class Traits std::char_traits<CharT>, class Allocator std::allo…

Anaconda Prompt输入jupyter lab无反应

问题&#xff1a;Anaconda Prompt界面输入指令无反应 原因&#xff1a;公司电脑勒索病毒防御工具阻止了进程 解决&#xff1a;找到黑名单恢复进程

Modahub魔搭社区:星环向量数据库Transwarp Hippo团队的详细介绍

目录 团队概况 团队特色 团队实践 未来展望 团队概况 星环向量数据库团队,这是一个在星环内部颇具特色的团队。我们的特色在于,我们不仅专注于数据库领域,而且还涵盖了数据应用以及人工智能领域。这种跨领域的合作让我们能够提供更全面、更深入的服务。 我们团队的一个重…