【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 bytestruct 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;    //定义结构体变量p2int 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));//16return 0;
}
struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf("%d\n", sizeof(struct S4));//32return 0;
}

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

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

//例如:
struct S1
{char c1;int i;char c2;
};//12struct S2
{char c1;char c2;int i;
};//8//s1 和 s2 类型的成员一模一样,但是 s1 和 s2 所占空间大小不同

1.7 修改默认对齐数

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

#include<stdio.h>
#pragma pack(4)//设置默认对齐数为4struct S
{int i;double d;
};
#pragma pack(8)//取消设置的默认对齐数,还原为默认int main()
{printf("%d\n", sizeof(struct S));//相较修改默认对齐数之前,结构体占用内存大小从 16 变成了 12return 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 变成了 6return 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 - 32bitint _a : 2;int _b : 5;int _c : 10;//15//4byte - 32bitint _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));//结果是4return 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);//输出11223355return 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 01int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\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));//8printf("%d\n", sizeof(union Un2));//16return 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");elseprintf("%-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);

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

fopen/fwrite/fread 对UNICODE字符写入的总结

windows对fopen函数进行了升级&#xff0c;可以支持指定文件的编码格式&#xff08;ccs参数指定&#xff09;。 例如&#xff1a; FILE *fp fopen("newfile.txt", "rt, ccsUTF-8"); 当以 ccs 模式打开文件时&#xff0c;进行读写操作的数据应为 UTF-16…

LeetCode Hot100之十:239.滑动窗口最大值

题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 提示&#xff1a; 1 < nums.length < 10^5 -10^4 < nums[i…

QT专栏1 -Qt安装教程

#本文时间2023年11月18日&#xff0c;Qt 6.6# Qt 安装简要说明&#xff1a; Qt有两个版本一个是商业版本&#xff08;收费&#xff09;&#xff0c;另一个是开源版本&#xff08;免费&#xff09;&#xff1b; 打开安装程序时&#xff0c;通过判断账号是否有公司&#xff0c;安…

centos7安装mongodb

1、下载mongodb https://www.mongodb.com/try/download/community 2、解压 3、重命名 4、创建mongodb的data、logs目录 5、启动mongodb, bin/mongod --port27017 --dbpath/data/program/mongodb/data --logpath/data/program/mongodb/logs/mongodb.log --bind_ip0.0.0.0 --f…

linux 服务器进程、端口查找,nginx 配置日志查找,lsof 命令详解

一 、根据端口号 查看文件的部署位置 1.1 使用查看端口号对应的进程信息 方式一 &#xff1a; 使用netstat命令 netstat -tuln | grep 端口号-t&#xff1a;显示TCP连接 -u&#xff1a;显示UDP连接 -l&#xff1a;仅显示监听状态的连接 -n&#xff1a;以数字形式显示端口…

App测试经典面试题及参考答案

最近整理了一些关于App测试的面试题。 本参照答案是本人在工作实践中总结&#xff0c;仅代表个人观点&#xff0c;如有错误&#xff0c;请谅解。 1、说一些你在测试过程中常用到的adb命名 2、APP测试与web测试的区别&#xff1f; 3、APP闪退有哪些原因造成的&#xff1f; …

人工智能轨道交通行业周刊-第65期(2023.10.30-11.19)

本期关键词&#xff1a;高铁自主创新、智慧城轨、调车司机、大模型垂直应用、大模型幻觉 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道…

STL简介

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解c中的STL库 > 毒鸡汤&#xff1a;路难行&a…

Redis 9 数据库

4 设置键的生存时间或过期时间 通过EXPIRE命令或者PEXPIRE命令&#xff0c;客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间&#xff08;TimeToLive&#xff0c;TTL&#xff09;&#xff0c;在经过指定的秒数或者毫秒数之后&#xff0c;服务器就会自动删除生存时间…

记录联系ThinkPad T490扬声器无声音但插耳机有声音的解决办法

型号&#xff1a;联想ThinkPad T490&#xff0c;系统Win10 64位。 现象&#xff1a;扬声器无声音&#xff0c;插耳机有声音。且右下角小喇叭正常&#xff0c;设备管理器中驱动显示一切也都正常&#xff08;无黄色小叹号&#xff09;。 解决办法&#xff1a; 尝试了各种方法&a…

在vmware中给linux添加硬盘

1.必须在断开linux电源的情况下&#xff0c;才能添加硬盘成功。注&#xff1a;自己好几次在开机状态下添加硬盘都失败了&#xff0c;然后关机后&#xff0c;又试了下&#xff0c;居然成功了。

链式前向星

性质 一种邻接表的写法 关键点&#xff1a; 数据结构 // 边 class Edge {int next; // 指向相同起始点的下一条边int to; // 邻接点int w; // 权重 } Edge[] edge new Edge[9]; // edge[cnt]表示编号为cnt的边// 用数组表示 int[] next new int[MAX]; int[] to new int[M…