【C语言】自定义类型:结构体、枚举、联合

简单不先于复杂,而是在复杂之后

在这里插入图片描述

文章目录

    • 1. 结构体的声明
      • 1.1 结构的基础知识
      • 1.2 结构的声明
      • 1.3 特殊的声明
      • 1.4 结构体的自引用
      • 1.5 结构体变量的定义和初始化
      • 1.6 结构体内存对齐
      • 1.7 修改默认对齐数
      • 1.8 结构体传参
    • 2. 位段
      • 2.1 什么是位段
      • 2.2 位段的内存分配
      • 2.3 位段的跨平台问题
      • 2.4 位段的应用
    • 3. 枚举
      • 3.1 枚举类型的定义
      • 3.2 枚举的优点
      • 3.3 枚举的使用
    • 4.联合(共用体)
      • 4.1 联合类型的定义
      • 4.2 联合的特点
      • 4.3 联合大小的计算
    • 5.练习:实现通讯录


1. 结构体的声明

1.1 结构的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.2 结构的声明

 struct tag
 {
     member-list;
 }variable-list

例如描述一个学生

struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
};//分号不能丢
struct Stu {
	//学生的相关属性
	char name[20];
	int age;
}s1,s2;
//s1,s2是根据结构体类型创建的结构体变量

在这里插入图片描述

1.3 特殊的声明

在声明结构的时候,可以不完全的声明。

省略掉结构体标签(tag)

比如:

//匿名结构体类型
//只能使用一次

struct
{
	//学生的相关属性
	char name[20];
	int age;
}s1;
struct
{
	int a;
	char b;
	float c;
}x;

struct
{
	int a;
	char b;
	float c;
}a[20], * p;

int main()
{
	p = &x;

	return 0;
}

在这里插入图片描述

警告:

编译器会把上面的两个声明当成完全不同的两个类型

所以是非法的

1.4 结构体的自引用

在这里插入图片描述
在这里插入图片描述

struct Node
{
	int data;//4 byte
	struct Node next;
};

int main()
{
	sizeof(struct Node);

	return 0;
}

这种写法是错误的。


正确的自引用方式:

在这里插入图片描述

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

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

在结构体中使用 Node* next 来定义一个指向 Node 结构体的指针,但在结构体定义之前并没有声明 Node 结构体。这会导致编译错误


typedef struct Node {
	int data;
	struct Node* test;
}Node;

	struct Node* n1;
	Node* n2;
    //以上创建结构体类型变量的方式是等价的

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

有了结构体类型,定义结构体变量就很简单了。

struct Point
{
	int x;
	int y;
}p1 = { 2,3 };                //声明类型的同时定义变量p1并初始化
struct Point p2;    //定义结构体变量p2


int main()
{
	//初始化:定义变量的同时赋初值

	struct Point p3 = { 3,4 };

	return 0;
}                                           

在这里插入图片描述

struct score
{
	int n;
	char ch;
};

struct Stu
{
	char name[20];
	int age;
	struct score s;
};

int main()
{
	struct Stu s1 = { "zhangsan",20, {100,'q'} };
	printf("%s %d %d %c\n", s1.name, s1.age, s1.s.n, s1.s.ch);

	return 0;
}

1.6 结构体内存对齐

现在我们已经基本掌握了结构体的基本使用了。

现在需要深入讨论一个问题:如何计算结构体的大小。

结构体内存对齐


在这里插入图片描述

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

int main()
{
	printf("%d\n", sizeof(struct s1));

	return 0;
}

在这里插入图片描述

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

int main()
{
	printf("%d\n", sizeof(struct S2));

	return 0;
}

我们发现,即使结构体成员相同,结构体所占用内存的大小也是不同的,结构体有内存的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)整数倍的地址处。

    对齐数 = 编译器默认的一个对齐数与该成员大小的较小值

    • vs中默认的值为8(gcc上没有默认对齐数)
  3. 结构体总大小为最大对齐数(每个成员变量都有一个自己的对齐数)的整数倍。

  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

为什么存在内存对齐?

大部分的参考资料是这样说的:

1. 平台原因(移植原因):

​ 不是所有的硬件平台都能访问任意地址上的任意数据的;

某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常.

2. 性能原因:

​ 数据结构(尤其是栈)应该尽可能地在自然边界上对齐.

​ 原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;

​ 而对齐的内存访问仅需要一次访问.

​ (32位机器上一次能读写 32bit 的数据)

总的来说:

结构体的内存对齐是拿空间时间的做法。
在这里插入图片描述

在这里插入图片描述

以上是s1所创建的变量中各成员变量在内存中的偏移量。

struct S3
{
	double d;
	char c;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S3));//16
	
	return 0;
}
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S4));//32
	
	return 0;
}

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?

让占用小的成员尽量集中在一起.

//例如:
struct S1
{
	char c1;
	int i;
	char c2;
};//12

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

//s1 和 s2 类型的成员一模一样,但是 s1 和 s2 所占空间大小不同

1.7 修改默认对齐数

我们使用#pragma这个预处理指令,可以改变我们的默认对齐数.(对于 vs 来说, 只有 vs 有默认对齐数的概念)

#include<stdio.h>
#pragma pack(4)//设置默认对齐数为4

struct S
{
	int i;
	double d;
};
#pragma pack(8)//取消设置的默认对齐数,还原为默认

int main()
{
	printf("%d\n", sizeof(struct S));
	//相较修改默认对齐数之前,结构体占用内存大小从 16 变成了 12

	return 0;
}
//取消默认对齐数
#include<stdio.h>
#pragma pack(1)
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()

int main()
{
	printf("%d\n", sizeof(struct S1));
	//相较修改默认对齐数之前,结构体占用内存大小从 12 变成了 6

	return 0;
}
#pragma once
//头文件中使用,功能是:防止头文件被多次引用

结论:

结构在对齐方式不合适的时候,我可以自己更改默认对齐数.

百度笔试题

写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明.

考察: offsetof 宏的实现

在之后的文章说明。

1.8 结构体传参

在这里插入图片描述

#include<stdio.h>

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


//结构体传参
void print1(struct S ss)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ss.data[i]);
	}
	printf("%d\n", ss.num);
}


//结构体地址传参
void print2(const struct S* ps)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("%d\n", ps->num);
}

int main()
{
	struct S s = { {1,2,3},100 };
	print1(s);//传值调用
	print2(&s);//传址调用

	return 0;
}

上面的 print1 和 print2 函数哪个好些?

首选 print2 函数

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

结论:

结构体传参的时候,要传结构体的地址。

2. 位段

接下来介绍结构体实现位段的能力。

2.1 什么是位段

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是 int、unsigned int 或 signed int
  2. 位段的成员名后边有一个冒号和数字.(位段的位是比特位的意思,冒号+数字的含义是:这个成员需要分配的比特位个数)

当有一些成员的取值范围在非常有限的情况下,所需要的内存空间也可以一定程度上减小。

也就是说:位段是可以用来节省空间的。

比如:

struct A
{
    //4byte - 32bit
	int _a : 2;
	int _b : 5;
	int _c : 10;
    //15
    //4byte - 32bit
	int _d : 30;
};

A 就是一个位段类型。

那位段 A 的大小是多少?

printf("%d\n", sizeof(struct A));//8byte - 64bit

2.2 位段的内存分配

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

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;

	return 0;
}
//空间内存是如何开辟的?

在这里插入图片描述

2.3 位段的跨平台问题

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

总结:

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

2.4 位段的应用

在这里插入图片描述

3. 枚举

枚举顾名思义就是一一列举。

把可能的取值一一列举。

比如在我们现实生活中:

一周的星期一到星期日是有限的7天,可以一一列举。

性别有:男、女和保密。也可以一一列举。

月份有有限的12个月,也可以一一列举。

3.1 枚举类型的定义

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

enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};

enum Color//颜色
{
	Red,
	GREEN,
	BLUE
};

以上定义的enum Dayenum Sexenum Color 都是枚举类型。

{}中的内容是枚举类型的可能取值,也叫枚举常量.

这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。

例如:

enum Color//颜色
{
   RED = 1,
   GREEN = 2,
   BLUE = 4
}

3.2 枚举的优点

为什么使用枚举?

我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

1.增加代码的可读性和可维护性

举个例子:文章最后写的通讯录,用起来有不方便的地方:在测试的时候需要来回翻找菜单来确定函数功能需要对应输入哪个数字来调用。下面是修改后的代码:

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};

void menu()
{
	printf("**********************************************\n");
	printf("***********  1. add         2. del.    *******\n");
	printf("***********  3. search      4. modify  *******\n");
	printf("***********  5. show        6. sort    *******\n");
	printf("***********  0. exit                   *******\n");
	printf("**********************************************\n");
}

int main()
{
	int input = 0;
	Contact con;//通讯录
	//初始化通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);	

	return 0;
}

2.和 #define 定义的标识符比较枚举有类型检查,更加严谨。

#define 定义的标识符只是个符号,没有类型,而枚举是一个类型

在这里插入图片描述

虽然 FRI 的值是 5 ,但是 FRI 是 枚举 Day 类型的,无法通过类型检查 。

3.防止了命名污染(封装)

4.便于调试

当我们写好 test.c 的时候 会经过预处理、编译、链接然后生成 .exe 文件

在这里插入图片描述

这样的代码在预处理过程中,M 就会替换成100,当我们调试 exe 文件的时候,就不能够观察到 M 这样的标识符了,因为已经被替换了,但是 enum 枚举常量可以在调试的时候进行观察,方便我们调试

5.使用方便,一次可以定义多个常量

3.3 枚举的使用

enum Color//颜色
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};

enum Color clr = GREEN;//只能拿枚举常量给枚举常量赋值,才不会出现类型的差异。
clr = 5;			   //ok??

4.联合(共用体)

4.1 联合类型的定义

联合也是一种特殊的自定义类型

这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

比如:

//联合类型的声明
union Un
{
	char c;
	int i;
};

//联合变量的定义
union Un un;
//计算联合变量的大小

int main()
{
	printf("%d\n", sizeof(un));//结果是4

	return 0;
}

4.2 联合的特点

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

在这里插入图片描述

union Un
{
	int a;
	char c;
};

struct St
{
	int a;
	char c;
};

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

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

	return 0;
}
#include<stdio.h>

union Un
{
	int i;
	char c;
};

int main()
{
	union Un un;

	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);//输出11223355


	return 0;
}

面试题:

判断当前计算机的大小端存储

#include<stdio.h>

int check_sys()
{
	int a = 1;
	return *(char*)&a;


}
int main()
{
	int a = 1;//0x 00 00 00 01

	//小端:低->高    01 00 00 00
	//大端:高->低    00 00 00 01
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

还有一种共用体的写法:

int check_sys()
{
	union Un
	{
		char c;
		int i;
	}u;
	u.i = 1;
     //返回1是小端,返回0是大端
    
	return u.c;
}

4.3 联合大小的计算

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

比如:

union Un1
{
	char c[5];
	int i;
};

union Un2
{
	short c[7];
	int i;
};


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

	return 0;
}

5.练习:实现通讯录

功能描述:

存放人的信息:

名字+年龄+性别+电话+地址

  1. 存放100个人的信息
  2. 增加联系人
  3. 删除指定联系人
  4. 查找联系人
  5. 修改联系人
  6. 排序
  7. 显示联系人

程序在vs2019环境下以多文件的形式编写:

test.c 测试功能

contact.c 通讯录的实现

contact.h 通讯录相关的声明

这个通讯录的版本是静态的版本:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void menu()
{
	printf("**********************************************\n");
	printf("***********  1. add         2. del.    *******\n");
	printf("***********  3. search      4. modify  *******\n");
	printf("***********  5. show        6. sort    *******\n");
	printf("***********  0. exit                   *******\n");
	printf("**********************************************\n");
}

int main()
{
	int input = 0;
	Contact con;//通讯录
	//初始化通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			SearchContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(&con);
			break;
		case 6:
			SortContact(&con);
			break;
		case 0:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);	

	return 0;
}

contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void InitContact(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->count == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pc->count].name);

	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->count].age));
	//其他成员是数组,数组名本身就是地址,age是整形变量,所以要取地址

	printf("请输入性别:>");
	scanf("%s", pc->data[pc->count].sex);

	printf("请输入电话:>");
	scanf("%s", pc->data[pc->count].tele);

	printf("请输入地址:>");
	scanf("%s", pc->data[pc->count].addr);

	pc->count++;
	printf("增加成功\n");
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址","");
	for (i = 0; i < pc->count; i++)
	{
		printf("%-20s\t%-3d\t%-5s\t%-12s\t%-30s\n", 
			pc->data[i].name, 
			pc->data[i].age, 
			pc->data[i].sex, 
			pc->data[i].tele, 
			pc->data[i].addr);
	}
}

void DelContact(Contact* pc)
{
	char name[MAX_NAME];
	assert(pc);
	if (pc->count == 0)
	{
		printf("通讯录为空,没有信息可以删除\n");
		return;
	}
	printf("请输入要删除人的名字:>\n");
	scanf("%s", name);

	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	//2.删除
	int i = 0;
	for (i = pos; i < pc->count-1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->count--;

	printf("删除成功\n");
}

static int FindByName(Contact* pc, char name[])
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->count; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];

	printf("请输入要查找人的名字:>\n");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
		printf("要查找的人不存在\n");
	else
		printf("%-20s\t%-3d\t%-5s\t%-12s\t%-30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);

}

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];

	printf("请输入要修改人的名字:>\n");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的人不存在\n");
	}
	else
	{
		printf("要修改人的信息已经查找到,接下来开始修改\n");

		printf("请输入名字:>");
		scanf("%s", pc->data[pos].name);

		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pos].age));

		printf("请输入性别:>");
		scanf("%s", pc->data[pos].sex);

		printf("请输入电话:>");
		scanf("%s", pc->data[pos].tele);

		printf("请输入地址:>");
		scanf("%s", pc->data[pos].addr);

		printf("修改成功\n");
	}

}

int cmp_peo_bt_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

//按照名字来排序
void SortContact(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_bt_name);

	printf("排序成功\n");
}

contact.h

#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

//类型的声明
//人的信息
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;


//通讯录
typedef struct Contact
{
	PeoInfo data[MAX];//存放人的信息
	int count;//记录当前通讯录中实际人的个数

}Contact;

//初始化通讯录
void InitContact(Contact* pc);

//增加联系人到通讯录
void AddContact(Contact* pc);

//打印通讯录
void ShowContact(const Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//查找指定联系人
void SearchContact(const Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//排序通讯录中的内容
//按照名字来排序
//按照年龄来排序
//.....
void SortContact(Contact* pc);

;
scanf(“%s”, pc->data[pos].tele);

	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);

	printf("修改成功\n");
}

}

int cmp_peo_bt_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

//按照名字来排序
void SortContact(Contact* pc)
{
assert(pc);
qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_bt_name);

printf("排序成功\n");

}


contact.h    

```c
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

//类型的声明
//人的信息
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;


//通讯录
typedef struct Contact
{
	PeoInfo data[MAX];//存放人的信息
	int count;//记录当前通讯录中实际人的个数

}Contact;

//初始化通讯录
void InitContact(Contact* pc);

//增加联系人到通讯录
void AddContact(Contact* pc);

//打印通讯录
void ShowContact(const Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//查找指定联系人
void SearchContact(const Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//排序通讯录中的内容
//按照名字来排序
//按照年龄来排序
//.....
void SortContact(Contact* pc);

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

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

相关文章

解决:java: 错误: 不支持发行版本 5 最有效方法

报错信息如图&#xff1a; 直接上终极方法&#xff1a; 修改配置文件 如图找到settings.xml文件 在标签中间插入如下代码&#xff08;jdk更改为自己电脑上的版本&#xff09; <profile><id>development</id><activation><jdk>11</jdk><…

深入Rust:探索所有权和借用机制

大家好&#xff01;我是lincyang。 今天&#xff0c;我们将一起深入探索Rust语言中的一个核心概念&#xff1a;所有权和借用机制。 这些特性是Rust区别于其他语言的重要特点&#xff0c;它们在内存管理和并发编程中扮演着关键角色。 一、Rust所有权机制 1. 什么是所有权&#x…

USB复合设备构建CDC+HID鼠标键盘套装

最近需要做一个小工具&#xff0c;要用到USB CDCHID设备。又重新研究了一下USB协议和STM32的USB驱动库&#xff0c;也踩了不少坑&#xff0c;因此把代码修改过程记录一下。 开发环境&#xff1a; ST-LINK v2 STM32H743开发板 PC windows 11 cubeMX v6.9.2 cubeIDE v1.13.2 cub…

Google 向中国开发者开放数百份 TensorFlow 资源

Google 的机器学习框架 TensorFlow 自 2015 年开源后&#xff0c;已然成为 AI 领域最受欢迎的框架。 据统计&#xff0c;在广受欢迎的 Python 编程语言在线软件知识库 PyPi 上&#xff0c;TensorFlow 的下载次数已超过 90 万&#xff0c;其中有 15% 来自中国。谷歌官方博客也表…

11.10~11.15置信区间,均值、方差假设检验,正态,t,卡方,F分布,第一第二类错误

置信度&#xff0c;置信区间 给定一个置信度&#xff0c;就可以算出一个置信区间。 如果给的置信度越大&#xff0c;那么阿尔法就越小 给的置信度越小&#xff0c;那么α就越大&#xff0c;那么 考虑精确性&#xff0c;希望区间长度尽可能小&#xff0c;所以是取正态的中间…

Postman的常规断言/动态参数断言/全局断言

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 断言&#xff0c;包括状态码断言和业务断言&#xff0c;状态码断言有一个&#xff0c;业务断言有多个。 一&#xff09;常规的…

Python数据容器之(元组)

我们前面所了解的列表是可以修改的&#xff0c;但如果想要传递的信息&#xff0c;不被篡改&#xff0c;列表就不合适了。 元组同列表一样&#xff0c;都是可以封装多个、不同类型的元素在内。 但最大的不同点在于&#xff1a; 元组一旦定义完成&#xff0c;就不可修改 所以…

exce常用

一、冻结 同时冻结行和列 打开一个Excel表格&#xff0c;点击选择需要冻结的行和列交接处对应的单元格&#xff08;例如&#xff1a;需要同时冻结1、2行和A、B列&#xff0c;则选中行列交接对应的C3单元格&#xff09;&#xff09;&#xff0c; 即下一行 和下一列的交接点。 …

虹科方案 | 从概念到生产的自动驾驶软件在环(SiL)测试解决方案

来源&#xff1a;雅名特自动驾驶 虹科方案 | 从概念到生产的自动驾驶软件在环&#xff08;SiL&#xff09;测试解决方案 自动驾驶软件在环&#xff08;SiL&#xff09;测试解决方案 自动驾驶软件在环&#xff08;SiL&#xff09;测试解决方案能够研究和验证高历程实验和恶劣驾…

Xocde 升级15 或者 iOS17报错:

错误&#xff1a; Assertion failed: (false && "compact unwind compressed function offset doesnt fit in 24 bits"), function operator(), file Layout.cpp, line 5758. 翻译&#xff1a; 断言失败&#xff1a;&#xff08;false&&“压缩展开…

计算机网络之网络体系结构

计算机网络体系结构 一、常见的计算机体系结构 1.1 OSI标准以及TCP/IP体系结构 OSI标准失败的原因&#xff1a; OSI的专家们缺乏实际经验&#xff0c;他们在完成OSI标准时没有商业驱动力OSI的协议实现起来过分复杂&#xff0c;而且运行效率很低OSI标准的制定周期太长&#x…

Python中sys模块详解:常用方法与变量

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是涛哥&#xff0c;今天为大家分享 Python中sys模块详解&#xff1a;常用方法与变量&#xff0c;文章2500字&#xff0c;阅读大约8分钟&#xff0c;大家enjoy~~ sys 模块是 Python 标准库中的一个核心模块&…

【C语言 | 数组】C语言数组详解(经典,超详细)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

服务名无效。 请键入 NET HELPMSG 2185以获得更多的帮助

遇到的问题是MySQL服务没有。 因为net start 服务名&#xff0c;启动的是win下注册的服务。此时&#xff0c;我系统中并没有注册mysql到服务中。即下面没有mysql服务。 mysqld --install net start mysql

Linux_包管理_apt和apt-get、apt upgrade会自动升级内核

1、apt和apt-get 这篇文章说的很详细&#xff0c;【精选】一文搞清apt与apt-get的异同_apt和aptget-CSDN博客&#xff0c;来自于英语原文&#xff0c;Difference Between apt and apt-get Commands [Explained]。 简单来说&#xff0c;apt更容易使用&#xff08;比如显示下载…

武汉凯迪正大—锂电池均衡维护仪

产品概况 KDZD885C 电池容量平衡测试系统&#xff0c;主要用于锂电池箱充放电测试及均衡维护&#xff0c;解决锂电池包单芯电压不均衡的痛点&#xff0c;用于快速解决锂电池电压不一致的难题,适用于各锂电池模组电压等级&#xff0c;集单芯放电&#xff0c;充电&#xff0c;均…

git服务器搭建

P1 kube-node2服务器 互联网已经存在的大型git服务器【版本控制服务器】 https://github.com【国外网站,速度慢】 https://about. gitlub.com/【国外网站,速度慢】 https://gitee.com【国内阿里的git网站,可以注册使用】 环境准备&#xff0c;使用之前的kube-node2主机&…

zabbix中监控数据,报错返回给钉钉查看

### 在钉钉中创建群聊(同组的同学创建一个群聊)&#xff0c;在群里面添加自定义机器人 1.通过自定义webhook接入自定义服务 webhook&#xff1a;记住webhook的地址 安全设置&#xff1a;设置加签&#xff0c;只有信息内容包含签才会被机器人发送。 2.配置钉钉告警脚本 #### **安…

从零搭建微服务架构:Spring Boot与Nacos完美整合

&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 从零搭建微服务架构&#xff1a;Spring Boot与Nacos完美整合 前言第一&#xff1a;服务注册与发现第二&#xff1a;配置中心第三&#xff1a;报错问题解决第四&#xff1a;什…

Solidity案例详解(四)投票智能合约

该合约为原创合约&#xff0c;功能要求如下 在⼀定时间能进⾏投票超过时间投票截⽌&#xff0c;并投赞同票超过50%则为通过。 使⽤safeMath库&#xff0c;使⽤Owner 第三⽅库拥有参与投票权的⽤户在创建合约时确定Voter 结构 要有时间戳、投票是否同意等&#xff1b;struct 结构…