C语言进阶--自定义类型详解

news2024/12/26 11:29:11

目录

一.结构体

1.1.结构的声明

1.2.结构的自引用

1.3.结构体变量的定义和初始化

1.4.结构成员的访问

1.5.结构体内存对齐

1.6.修改默认对齐数

1.7.offsetof宏

1.8.结构体传参

1.9.位段

二.枚举

2.1.枚举的定义

2.2.枚举的使用

2.3.枚举的优点

三.联合(共用体) 

3.1.联合的定义

3.2.联合的特点

3.3.联合大小的计算


一.结构体

结构是一些值的集合,这些值称为它的成员,一个结构中的各个成员可能具有不同的类型。

前期要点回顾:C语言深度解析--结构体

1.1.结构的声明

在声明结构时,必须列出它包含的所有成员。这个列表包括每个成员的类型和名字。

struct tag //struct:关键字,tag:类型名
{
	member-list;//成员列表
 
}variable-list;//变量列表

需要注意的是:类型名,成员列表和变量列表不能全部省略,它们至少要出现两个。

案例一:

struct Stu//Stu结构体类型名
{
	char name[20];
	int age;
	double score;
}s1;//结构体变量

//创建全局变量
struct Stu s2;

int main()
{	
	//创建局部变量
	struct Stu s3;
	return 0;
}

这里定义了一个结构体类型struct Stu,再使用类型创建3个变量s1,s2和s3 (两种方法创建结构体变量)。

案例二:

struct
{
	int a;
	char b;
	float c;
}x;
//这个声明创建了一个名叫x的变量,它包括三个成员:一个整数,一个字符和一个浮点数

struct
{
	int a;
	char b;
	float c;
}a[20],*p;
//这个声明创建了a和p。a是一个数组,它包含了20个结构。p是一个指针,它指向这个类型的结构

上述案例称为匿名结构体,因为没有类型名tag,所以后面不能再定义相关类型的数据。

这两个声明被编译器当作两个两种截然不同的类型,即使它们的成员列表完全相同。因此,变量a和p的类型与x的类型不同,所以下面这条语句是非法的

p=&x;

标签tag可以很好地解决匿名结构体带来的问题。标签tag字段允许为成员列表提供一个名字,这样它就可以在后续的声明中使用。标签允许多个声明使用同一个成员列表,并且创建同一种类型的结构。

1.2.结构的自引用

在一个结构内部包含一个类型为该结构本身的成员是否合法呢?

struct Node
{
	int data;
	struct Node b;
};

这种类型的自引用是非法的,因为成员b是另一个完整的结构,其内部还将包含它自己的成员b。这第二个成员又是另一个完整的结构,它还将包括它自己的成员b。这样重复下去永无止境。这有点像永远不会终止的递归程序。但下面这个声明确实合法的,你能看出其中的区别吗?

struct Node
{
	int data;
	struct Node* b;
};

这个声明和前面那个声明的区别在于b现在是一个指针而不是结构。编译器在结构的长度确定之前就已经知道指针的长度,所以这种类型的自引用是合法的。

如果你觉得一个结构内部包含一个指向该结构本身的指针有些奇怪,请记住它事实上所指向的是同一种类型的不同结构。

声明结构时可以使用的另一种良好技巧是用typedef创建一种新的类型。

结构体重新命名

typedef struct Node
{
	int data;
	struct Node* next;
}Node;//将struct Node重命名为Node

匿名结构体重新命名,但不可以自引用

typedef struct
{
	int data;
	Node* next;//err
}Node;

这个声明的目的是为这个结构创建类型名Node。但是它失败了。类型名直到声明的末尾才定义,所以在结构声明的内部它尚未定义。解决方案是定义一个结构标签来声明next。

typedef struct S
{
	int data;
	struct S* next;
}Node;

结构体指针重命名 

typedef struct Node
{
	int data;
	struct Node* next;
}Node,*pNode;
//pNode等价于struct Node*

1.3.结构体变量的定义和初始化

普通结构体的初始化

和数组一样,结构变量也可以在声明的同时进行初始化,初始化式中的值必须按照结构成员的顺序进行显示。

struct Book
{
	char name[20];
	float price;
	char id[12];
}s1 = { "C语言",55.5,"PGC001" };

struct Book s2 = { "Linux",99.9,"DG001" };

int main()
{
	struct Book s2 = { "数据结构",66.6,"HG001" };

	return 0;
}

嵌套结构体的初始化

//结构体嵌套初始化
struct Node
{
	struct Book b;
	struct Node* next;
};

int main()
{
	struct Node n = { {"汤神Java",66.8,"TG001"},NULL };

	return 0;
}

1.4.结构成员的访问

结构成员的直接访问

结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数,左操作数就是结构变量的名字,右操作数就是需要访问的成员的名字。这个表达式的结果就是指定的成员。

结构成员的间接访问

如果你拥有一个指向结构的指针,你该如何访问这个结构的成员呢?C语言提供了一个更为方便的操作符来完成这项工作--(->)操作符(也称为箭头操作符)。和点操作符一样,箭头操作符接受两个操作数,但左操作数必须是一个指向结构的指针。

struct Book
{
	char name[20];
	float price;
	char id[20];
};

int main()
{
	//初始化
	struct Book b = { "C语言", 29.9, "1122" };

	//使用.
	printf("%s %f %s\n", b.name, b.price, b.id);

	//使用->
	struct Book* ps = &b;
	printf("%s %f %s\n", ps->name, ps->price, ps->id);

	return 0;
}

1.5.结构体内存对齐

我们在计算一个结构体所占空间大小时,本质考察的是结构体的内存对齐规则。那结构体中的成员变量是如何对齐的呢?

结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处;
  2. 其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。VS中默认对齐数是8; Linux没有默认对齐数,它是按照自身大小来对齐的;
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

案例一:

struct S1
{
	char c1;//1
	int i;//4
	char c2;//1
};

分析:

案例二:

struct S2
{
	char c1;//1
	char c2;//1
	int i;//4
};

分析:

案例三:

struct S3
{
	double d;//8
	char c;//1
	int i;//4
};

分析:

案例四:

struct S3
{
	double d;//8
	char c;//1
	int i;//4
};

struct S4
{
	char c1;//1
	struct S3 s3;//16
	double d;//8
};

分析:

为什么要内存对齐?

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常;
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?                                             答案:让占用空间小的成员尽量集中在一起。

案例:

struct S1 
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	struct S1 s1 = { 0 };
	struct S2 s2 = { 0 };
	printf("%d\n", sizeof(s1));//12
	printf("%d\n", sizeof(s2));//8
	return 0;
}

分析:s1和s2的类型成员一模一样,但是s1和s2的大小不同。发现让占用空间小的成员尽量集中在一起有助于节省空间。

1.6.修改默认对齐数

之前我们见过了#pragma这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。

案例:

#pragma pack(4)//设置默认对齐数为4
struct S
{
	char c;
	double d;
};

int main()
{
	struct S s;
	printf("%d\n",sizeof(s));//12

	return 0;
}

分析:

结论:结构体在对齐方式不合适的时候,那么我们可以自己调整默认对齐数。

1.7.offsetof宏

offsetof,是一个宏函数,用于计算结构体中某变量相对于起始地址的位置的偏移量。                       

#include<stddef.h>
size_t offsetof(structName, memberName);

案例:

struct S3
{
	double d;//8
	char c;//1
	int i;//4
};

int main()
{
	
	struct S3 s3;

	printf("%d\n", sizeof(s3));//16

	printf("%u\n", offsetof(struct S3, d));//0
	printf("%u\n", offsetof(struct S3, c));//8
	printf("%u\n", offsetof(struct S3, i));//12

	return 0;
}

1.8.结构体传参

struct S
{
	int data[10];
	int num;
};

//结构体传参
void print1(struct S ss)
{
	printf("%d\n", ss.num);
}

//结构体地址传参
void print2(struct S* pss)
{
	printf("%d\n", pss->num);
}

int main()
{
	struct S s = { {1,2,3,4}, 1000 };

	print1(s);  //传结构体
	print2(&s); //传结构体地址

	return 0;
}

上面的print1和print2函数哪个好些?                                                                                                          答案是:print2好些。原因:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。所以我们在使用结构体传参时,最好传递结构体的地址。

1.9.位段

位段的声明和结构类似,但它的成员是一个或多个位的字段这些不同长度的字段实际上存储于一个或多个整型变量中。位段的声明和任何普通的结构成员声明相同,但有两个例外。首先,位段成员必须声明为int,signed int或unsigned int类型。其次,在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。

注意:用signed或unsigned整数显示地声明位段是个好主意。如果把位段声明为int类型,它究竟被解释为有符号数还是无符号数是由编译器决定的。

例如:

struct AA
{
	int _a;
	int _b;
	int _c;
	int _d;
};

struct A //A就是一个位段类型
{
	int _a : 2;//_a这个成员占2个bite位:00 01 10 11,则表示的取值:0 1 2 3
	int _b : 5;//_b这个成员占5个bite位
	int _c : 10;//_c这个成员占10个bite位
	int _d : 30;//_d这个成员占30个bite位
};

int main()
{
	printf("%d\n", sizeof(struct A));//8
	printf("%d\n", sizeof(struct AA));//16

	return 0;
}

位段的内存分配

  1. 位段的成员可以是 int,unsigned int,signed int 或者是char(属于整形家族)类型;
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的;
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

案例分析:

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = {0};
	printf("%d\n",sizeof(struct S));//3

	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;

	return 0;
}

分析:

我们可以看到结构体中的每个成员变量都是char类型的,而位段的空间是按照4个字节(int)或者1个字节(char)的方式来开辟的,所以编译器会先为其分配一个字节(8bite)的空间。

那数据是如何存放的呢?我们不妨设位段的数据是从右向左存放的。首先,变量a占据了3bite,变量b占据4bite,此时开辟一个字节(char)的空间,可以同时放下变量a和b。这时,第一个字节只剩下1bite的空间,而变量c需要占5bite的空间,此时第一个字节的空间显然是不够的。这时我们需要开辟第二个字节(char)的空间。

那变量c是在使用完第一个字节剩下的1bite之后再使用第二个字节的空间,还是直接使用第二个字节的空间呢?我们不妨假设变量c直接舍弃第一个字节剩余的空间,然后在第二个字节上自右向左占据5bite的空间。此时的第二字节只剩下3bite的空间,而变量b又需要4bite的空间,显然空间是不够的。所以我们再开辟第三个字节(char)的空间,然后自右向左占据4bite的空间来存放变量d。此时的结构体刚好需要3字节的空间,和程序运行结果完全相同。

那位段的的空间开辟是否真如上面所猜想的那样?接下来我们通过调试来进一步分析:

a的值为10,对应的二进制数为00001010,而其对应的位段为3bite,所以截断010。将它自右向左放到第一个字节空间的最右面;                                                                                                                 b的值为12,对应的二进制数为00001100,而其对应的位段为4bite,所以截断1100。将它自右向左紧邻着变量b存放;                                                                                                                                   c的值为3,对应的二进制数为00000011,而其对应的位段为5bite,所以截断00011。将它自右向左放到第二个字节空间的最右面;                                                                                                           d的值为4,对应的二进制数为00000100,而其对应的位段为4bite,所以截断0100。将它自右向左放到第三个字节空间的最右面。

三个字节空间存放的内容分别如下:                                                                                                            

  1. 01100010 对应的16进制数为0x62
  2. 00000011 对应的16进制数为: 0x03; 
  3. 00000100 对应的16进制数为:0x04

通过调试可以发现,结构体变量s在内存中确实是如我们所预想的那样存放的。

位段的跨平台问题:

  1. int位段被当成有符号数还是无符号数是不确定的;
  2. 位段中最大位的数目不能确定;(16位机器最大16,32位机器最大32,写成27则在16位机
    器会出问题。)
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义;
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

总结:跟结构体相比,位段可以达到同样的效果,但是可以很好节省空间,但是有跨平台的问题存在。

位段的应用:应用于IP数据报的格式等。

二.枚举

枚举,也就是列举,即把所有可能的取值一一列举出来。

2.1.枚举的定义

enum tag
{
  values1,
  values2,
  values3
} variable-list;

//enum:枚举关键字
//tag:枚举标签
//enum tag:枚举类型
//values1:枚举常量
//variable-list:枚举变量

举例:

enum Sex
{
	MALE,
	FEMALE,
	SECRET
};

enum Day
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

enum Color
{
	Red=5,
	Green,
	Blue
};

以上定义的 enum Sex, enum Day, enum Color 都是枚举类型。{ }中的内容是枚举类型的可能取值,也叫枚举常量

对于定义在枚举内部的常量,是存在初始值的,默认从0开始,依次递增1。例如:

enum Color
{
	RED,
	GREEN,
	BLUE
};

int main()
{
	printf("%d\n", RED);//0
	printf("%d\n", GREEN);//1
	printf("%d\n", BLUE);//2

	return 0;
}

当然,在定义的时候也可以赋初值,且枚举常量的值也会随着修改的值依次递增1,例如:

enum Color
{
	RED=5,
	GREEN,
	BLUE
};

int main()
{
	printf("%d\n", RED);//5
	printf("%d\n", GREEN);//6
	printf("%d\n", BLUE);//7

	return 0;
}

2.2.枚举的使用

那枚举常量的值在后期是否可以随意修改呢?答案是否定的

案例一:

enum Color
{
	RED = 5,
	GREEN,
	BLUE
};

int main()
{
	RED = 8;

	return 0;
}

原因如下:

案例二:

enum Color
{
	RED = 5,
	GREEN,
	BLUE
};

int main()
{
	enum Color d = 1;//err

	enum Color e = BLUE;//ok

	return 0;
}

分析:

1是整型,而d是enum Color类型,二者类型并不匹配。在.c文件下执行代码并没有报错,而在.cpp文件下中却报错。所以只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

2.3.枚举的优点

  1. 增加代码的可读性和可维护性;
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨;
  3. 防止命名污染(封装);
  4. 便于调试(而#define定义的常量在调试时不能看到标识符);
  5. 使用方便,一次可定义多个常量。

三.联合(共用体) 

联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列成员,特征是这些成员共用同一块空间 ,所以联合体也叫共用体。当你想在不同的时刻把不同的东西存储于同一个位置,就可以使用联合。

3.1.联合的定义

union tag
{
  member1;
  member2;
}variable-list;

//union:联合体关键字
//tag:联合标签名
//union tag:联合类型
//{}:成员列表
//variable-list:变量

举例:

union Un
{
	char c;
	int i;
};

int main()
{
	union Un u;

	return 0;
}

注意:联合变量可以被初始化,但这个初始值必须是联合第1个成员的类型,而且它必须位于一对花括号里面。

3.2.联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

案例一:

union Un
{
	char c;
	int i;
};

int main()
{
	union Un u;
	printf("%d\n",sizeof(u));//4

	printf("%p\n",&u);
	printf("%p\n",&(u.c));
	printf("%p\n",&(u.i));

	return 0;
}

运行结果:

我们可以发现:

由于sizeof(u)的值为4,那么联合变量的大小则为最大成员的大小。而最大成员为i,对应的类型为int,所以值为4。

由于&u,&(u.c)和&(u.i)的值均为0059FE98,那么联合的成员是共用同一块内存空间的。

案例二:

union Un
{
	char c;
	int i;
};

int main()
{
	union Un u;
	u.c = 0x55;//55 cc cc cc
	u.i = 0;//00 00 00 00

	return 0;
}

运行结果:

union Un u = { 0 };对应的内存:

u.c = 0x55;对应的内存:

 

u.i = 0;对应的内存:

 

可知,联合的成员是共用同一块内存空间的。

面试题:判断当前计算机的大小端存储

大端模式和小端模式:

方案一:强制类型转换

int main()
{
	int i = 1;
	//01 00 00 00

	if (1 == *(char*)&i)//强制转化为char*类型,解引用得到一个字节
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

方案二:联合的方式

int check_sys()
{
	union Un
	{
		char c;
		int i;
	}u;

	u.i = 1;//01 00 00 00

	return u.c;//c是i的第一个字节,01
}

int main()
{
	if (1 == check_sys())
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

3.3.联合大小的计算

规定:

  1. 联合的大小至少是最大成员的大小;
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

案例一:

union Un
{
	char arr[5];//5
	int i;//4
};

int main()
{
	printf("%d\n", sizeof(union Un));//8

	return 0;
}

分析:

当成员是数组时,我们看对齐数看的是数组成员的类型。字符数组c中的每一个元素在内存中所占大小为1个字节,VS默认对齐数为8,取两者最小值,即为1;
成员i在内存中所占大小为4个字节,VS默认对齐数为8,取两者最小值,即为4;
因为联合中已经存放了5个字节,而我们要对齐到最大对齐数4的整数倍,即为8,因此最后计算出来的联合体的大小就为8。

案例二:

union Un
{
	short arr[7];//14 2
	int i;//4 4
};

int main()
{
	printf("%d\n", sizeof(union Un));//16

	return 0;
}

分析:

当成员是数组时,我们看对齐数看的时数组成员的类型。字符数组arr中的每一个元素在内存中所占大小为2个字节,VS默认对齐数为8,取两者最小值,即为2;
成员i在内存中所占大小为4个字节,VS默认对齐数为8,取两者最小值,即为4;                            因为联合中已经存放了14个字节,而我们要对齐到最大对齐数4的整数倍,即为16,因此最后计算出来的联合体的大小就为16。

总结:

在一个成员长度不同的联合里,分配给联合的内存数量取决于它的最长成员的长度。这样,联合的长度总是足以容纳它最大的成员。如果这些成员的长度相差悬殊,当存储长度较短的成员时,浪费的空间是相当可观的。在这种情况下,更好的方法是在联合中存储指向不同成员的指针而不是直接存储成员本身。所有指针的长度都是相同的,这样就解决了内存浪费的问题。当它决定需要使用哪个成员时,就分配正确数量的内存来存储它。

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

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

相关文章

ODrive电路设计中的接地环路

对于要进行通信的电气设备,大多数时候它们需要公共接地连接。最佳实践是将接地连接回一个点,称为“星形接地”。如果有多个接地路径,则会形成“接地环路”。接地环路和导线电感可能会导致 ODrive 等大电流电子设备出现问题。作为可能出错的示例,请查看下图。 问题: 问题在…

【计算机网络】数据链路层--点对点协议PPP

1.概念 2.构成 3.封装成帧 - 帧格式 4.透明传输 4.1字节填充法&#xff08;面向字节的异步链路&#xff09; 4.2.比特填充法&#xff08;面向比特的同步链路&#xff09; 5.差错检测 6.工作状态 7.小结

使用Vite 搭建高可用的服务端渲染SSR工程

在非常早期的 Web 开发中&#xff0c;大家还在使用 JSP 这种古老的模板语法来编写前端的页面&#xff0c;然后直接将 JSP 文件放到服务端&#xff0c;在服务端填入数据并渲染出完整的页面内容&#xff0c;可以说那个时代的做法是天然的服务端渲染。但随着 AJAX 技术的成熟以及各…

Typescript中的interface,type和class的相同点和不同点

感觉他们很像是不是&#xff1f; 他们确实有一些相同点&#xff1a; 相同点&#xff1a; 它们都可以用来描述对象的形状&#xff0c;即属性和方法。它们都可以被继承或实现&#xff0c;形成新的类型或类。它们都可以使用泛型参数&#xff0c;增加类型的灵活性和复用性。 不同…

jenkins shell脚本问题

问题描述&#xff1a; mac电脑配置了jenkins,同样的脚本&#xff0c;mac 电脑终端执行没有问题&#xff0c;复制到jenkins时&#xff0c;jenkins shell命令识别不了 -n指令。 解决方案&#xff1a; jenkins 系统配置中&#xff0c;找到shell 模块&#xff0c;配置上本地的路…

继骨传导耳机之后,新发布开放式耳机又成断货王!2年3代爆款,南卡怎么吸引年轻人?

今年618后&#xff0c;南卡的开放式耳机OE Pro成了新一代“断货王”&#xff0c;火爆程度直逼南卡的骨传导耳机Pro系列。 仔细想想&#xff0c;南卡已做出了3代爆款&#xff1a;骨传导Pro系列、骨传导Noe系列&#xff0c;南卡开放式OE系列&#xff0c;并且每一代都带动了该系列…

四、Docker镜像详情

学习参考&#xff1a;尚硅谷Docker实战教程、Docker官网、其他优秀博客(参考过的在文章最后列出) 目录 前言一、Docker镜像1.1 概念1.2 UnionFS&#xff08;联合文件系统&#xff09;1.3 Docker镜像加载原理1.4 重点理解 二、docker commit 命令2.1 是什么&#xff1f;2.2 命令…

分布式调用与高并发处理 Zookeeper分布式协调服务

一、Zookeeper概述 1.1 集中式和分布式 单机架构 一个系统业务量很小的时候所有的代码都放在一个项目中就好了&#xff0c;然后这个项目部署在一台服务器上&#xff0c;整个项目所有的服务都由这台服务器提供。 缺点&#xff1a; 服务性能存在瓶颈&#xff0c;用户增长的时候…

LENOVO联想笔记本电脑 拯救者Y520-15IKBN(80Y5)原装Win10系统文件,恢复出厂OEM系统

lenovo联想笔记本电脑&#xff0c;拯救者Y520-15IKBN(1050、1050Ti) (80Y5)出厂状态Windows10系统&#xff0c;原装OEM系统镜像 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;IS…

Python基础学习注意事项

1.Python中 小数字符串不可以转成int&#xff0c;即int("98.9")会报错&#xff01; 数字字符串串才可以转对应的int、float 2.float数据计算的时候精度会丢失&#xff01;解决办法&#xff1a;&#xff08;from decimal import Decimal&#xff08;可以计算准确&am…

npm启动,node.js版本过高

“dev_t”: “set NODE_OPTIONS”–openssl-legacy-provider" & npm run dev\n"

easyConnect 报本地环境异常错误

一、检查任务管理器中发现ecagent.exe进程是禁用状态。如图&#xff1a; 二、在异常客户端上&#xff0c;找到easyconnect的安装目录&#xff08;默认路径&#xff1a;C:\Program Files (x86)\Sangfor\SSL\ECAgent&#xff09;&#xff0c;找到ecagent.exe应用程序尝试手动执行…

【2023 可信数据库发展大会】拓数派受邀参与,CTO 郭罡将在大会发表演讲

2023年7月4日~5日&#xff0c;由中国信息通信研究院、中国通信标准化协会指导&#xff0c;中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;主办的2023可信数据库发展会将于北京国际会议中心隆重召开。 大会以“自主创新引领”为主题&#xff0…

深入学习单例设计模式

目录 一.单例模式的定义 二.单例模式的实现方式 1.懒汉模式&#xff1a; 2.饿汉模式 3.静态内部类方式 4.反射模式 5.枚举方式 6.序列化方式 三.单例模式的应用 一.单例模式的定义 保证一个类只有一个实例&#xff0c;并且提供一个全局访问点 使用的场景&#xff1a;…

【table中部分tr的折叠与展开】

示例功能&#xff1a; 1. 点击“作品”按钮&#xff0c;会显示author的作品信息 2. 再次点击“作品”按钮&#xff0c;会收起author的作品信息 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name&quo…

IDEA远程Debug调试工具(Remote)的使用

我们在开发的过程中&#xff0c;经常会遇到这样的情况&#xff1a;代码在本地测试得好好的&#xff0c;但部署上线后测试结果就不一样了&#xff0c;这时就需要去服务器上查看日志进行分析从而定位问题&#xff0c;但这样还是会比较麻烦&#xff0c;如果能够Debug调试&#xff…

CSS实现进度条和订单进度条---竖向

之前做了一个横向订单进度条&#xff0c;手机访问显示很难兼容样式&#xff0c;下面做一个竖向的&#xff0c;再结合情况微调一下&#xff0c;方便去兼容手机。 1.直接放页面 代码如下&#xff08;示例&#xff09;&#xff1a; <!DOCTYPE html> <html xmlns:th"…

【Java高级语法】(二十一)数组操作类:解析Arrays类中的全部操作方法,解锁Java数组操作技巧~

Java高级语法详解之数组操作类 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 Arrays类常用方法3.2 使用技巧 4️⃣ 应用场景&#x1f33e; 总结 前言&#xff1a;在学习本文之前&#xff0c;应该先学习并清楚了解Java基础部分的数组相关的概念和知识。 若还不具备学习条件&#xf…

途乐证券|人工智能概念再度下挫 海天瑞声、寒武纪等跌超10%

人工智能概念26日盘中大幅回调&#xff0c;截至发稿&#xff0c;当虹科技跌近18%&#xff0c;美亚柏科、昆仑万维跌约13%&#xff0c;博睿数据、光云科技、海天瑞声、寒武纪等跌超10%&#xff0c;焦点科技跌停&#xff0c;云从科技、朗玛信息、三六零等跌超9%。 香港途乐证券有…

力扣125:判断字符串是否是回文字符串

题目描述&#xff1a; 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个回文串。 字母和数字都属于字母数字字符。 给定一个字符串s&#xff0c;如果它是回文串&#xff0c;返回true&#xff1…