【C语言】《C语言自定义类型(结构体+联合体+枚举类型)》--详解

一.结构体

1.结构体基本知识

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


2.结构体的说明

结构体声明语法

cpp 复制代码
struct tag
{
    member-list;  // 成员列表
} variable-list;  // 变量列表(可选)

示例:描述一个学生

cpp 复制代码
struct Stu
{
    char name[20];  // 名字
    int age;        // 年龄
    char sex[5];    // 性别
    char id[20];    // 学号
};  // 分号不能丢

3.结构体变量的创建和初始化

完整示例

cpp 复制代码
#include <stdio.h>

struct Stu
{
    char name[20];  // 名字
    int age;        // 年龄
    char sex[5];    // 性别
    char id[20];    // 学号
};

int main()
{
    // 方式1:按照结构体成员的顺序初始化
    struct Stu s = { "hjq", 20, "男", "20260818001" };
    printf("name: %s\n", s.name);
    printf("age : %d\n", s.age);
    printf("sex : %s\n", s.sex);
    printf("id  : %s\n", s.id);

    printf("\n");  // 分隔线

    // 方式2:按照指定的顺序初始化(使用 .成员名)
    struct Stu s2 = { .age = 18, .name = "hjq", .id = "20260818002", .sex = "女" };
    printf("name: %s\n", s2.name);
    printf("age : %d\n", s2.age);
    printf("sex : %s\n", s2.sex);
    printf("id  : %s\n", s2.id);

    return 0;
}

运行如下:

补充其他初始化方式

cpp 复制代码
// 方式3:先创建,后逐个赋值
struct Stu s3;
strcpy(s3.name, "hf");
s3.age = 22;
strcpy(s3.sex, "男");
strcpy(s3.id, "20260818003");

// 方式4:使用typedef简化(推荐)
typedef struct Stu Stu;  // 或直接在声明时加 typedef
Stu s4 = { "hf", 23, "男", "20260818004" };

4.匿名结构体(不完全声明)

cpp 复制代码
// 匿名结构体类型(没有标签名 tag)
struct
{
    int a;
    char b;
    float c;
} x;  // 直接创建变量 x

struct
{
    int a;
    char b;
    float c;
} a[20], *p;  // 创建数组 a[20] 和指针 p

上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。

那么问题来了?

cpp 复制代码
//在上⾯代码的基础上,下⾯的代码合法吗?
p = &x;

这里因为会被认为是两个不同的类型,所以 *p 不能存变量 x 的地址。

警告:

  • 编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
  • 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

改进方法(使用 typedef 解决)

cpp 复制代码
// 如果想让匿名结构体可复用,可以用typedef
typedef struct
{
    int a;
    char b;
    float c;
} MyStruct;  // 这样就有了类型名

MyStruct s1 = { 1, 'a', 3.14 };
MyStruct s2 = { 2, 'b', 2.71 };
MyStruct* p = &s1;  //  现在可以了

5.结构的⾃引⽤

结构体允许包含一个指向同类型结构体的指针作为成员(即自引用指针),但不允许包含同类型结构体的完整变量作为成员。

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?

cpp 复制代码
⽐如,定义⼀个链表的节点:
struct Node
{
    int data;
    struct Node next;
};

上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?我们运行一下看一下

  1. 结构体不能包含自身类型的完整对象

  2. 编译器无法计算 sizeof(struct Node)(无限递归)

  3. 会报错:C2079: "next" 使用未定义的 struct "Node"

所以,仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的。

正确的⾃引⽤⽅式:

cpp 复制代码
代码:
struct Node
{
    int data;
    struct Node* next;
};

在结构体⾃引⽤使⽤的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引⼊问题,看看下⾯的代码,可⾏吗?

cpp 复制代码
typedef struct
{
    int data;
    Node* next;
}Node;

答案是不⾏的,因为Node是对前⾯的匿名结构体类型的重命名产⽣的但是在匿名结构体内部提前使⽤Node类型来创建成员变量,这是不⾏的。

解决⽅案如下:定义结构体不要使⽤匿名结构体了

cpp 复制代码
typedef struct Node
{
int data;
struct Node* next;
}Node;

在结构体定义中使用 struct Node 来指明 next 指针的类型,而不是直接使用 Node。这样编译器就能正确识别 Node 类型了


6.结构体内存对⻬

我们已经掌握了结构体的基本使⽤了。现在我们深⼊讨论⼀个问题:计算结构体的⼤⼩。

这也是⼀个特别热⻔的考点:结构体内存对⻬.

对⻬规则

⾸先得掌握结构体的对⻬规则:

  • 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
  • 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS 中默认的值为 8

- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

  • 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

练习如下:

练习1:
cpp 复制代码
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));

过程如下:

c1 是第一个成员,从地址0开始放,占1个字节。

接下来放 iint 的对齐数是4,所以它的起始地址必须是4的倍数。现在地址只到0,下一个是1,不是4的倍数,所以 1、2、3这三个地址都被浪费掉了,从地址4开始放 i,占4个字节。然后放 c2char 的对齐数是1,随便放,所以直接放在地址8,占1个字节。

现在结构体已经占了地址0到8,一共9个字节了。但是规则要求结构体总大小必须是最大对齐数(这里是4)的倍数,9不是4的倍数,所以要再补3个字节,变成12。

所以最后就是12。


练习2:
cpp 复制代码
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));

运行如下:

过程如下:

c1 是第一个成员,从地址0开始放,占1个字节。

接下来是 c2,它是 char 类型,对齐数是1,所以它可以挨着放,直接放在地址1,不用浪费空间;

然后到 i 了,int 的对齐数是4,所以它的起始地址必须是4的倍数。现在地址到1了,下一个是2,不行,3也不行,必须到4才可以,所以地址2和3被浪费掉了,从地址4开始放 i,占4个字节;

这时候结构体已经占了地址0到7,一共8个字节。所有成员里最大对齐数是4,8刚好是4的倍数,不需要再补位了。

所以最后就是 8个字节


练习3:
cpp 复制代码
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));

运行如下:

过程如下:

d 是第一个成员,从地址0开始放,double 是8个字节,所以占地址0到7。

接下来是 cchar 类型,对齐数是1,可以直接挨着放,放在地址8,占1个字节;

然后到 i 了,int 的对齐数是4,所以它的起始地址必须是4的倍数。现在地址到8了,下一个是9,9不是4的倍数,地址9、10、11被浪费掉了,从地址12开始放 i,占4个字节,到地址15结束;

这时候结构体已经占了地址0到15,一共16个字节。所有成员里最大对齐数是 double 的8,16刚好是8的倍数,不需要再补位。

所以最后就是 16个字节


练习4:(结构体嵌套)
cpp 复制代码
//练习4-结构体嵌套问题
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));

练习4,看出这个嵌套结构体,那又怎么算?

其实还是按之前的规则来,只是多了一条:被嵌套的结构体,它的对齐数等于它内部所有成员中最大的那个对齐数

过程如下:

c1 是第一个成员,从地址0开始放,占1个字节;

接下来放 s3struct S3 的对齐数是8(因为它里面有个 double),所以它的起始地址必须是8的倍数。现在地址到0了,下一个是1,不行,2、3、4、5、6、7都不行,必须到8才可以,所以地址1到7全被浪费了 ,从地址8开始放 s3,s3 自己内部占16个字节,所以它占地址8到23;

然后放 ddouble 的对齐数是8,起始地址必须是8的倍数。现在地址到23了,下一个是24,24是8的倍数(8×3=24),所以直接放,占地址24到31;

现在整个结构体占了地址0到31,一共32个字节。所有成员里最大对齐数是8(s3d 都是8),32刚好是8的倍数,不需要再补位。

所以最后 sizeof(struct S4)32个字节


7.为什么存在内存对⻬?

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

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  • 性能原因:

数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

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


那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:让占⽤空间⼩的成员尽量集中在⼀起

代码如下:

cpp 复制代码
struct S1
{
    char c1;
    int i;
    char c2;
};

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

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。


8.修改默认对⻬数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

  • 没写pack:编译器会在成员之间插空,保证每个成员都放在它对齐数的整数倍地址上,访问更快,但浪费内存。

  • 写了pack(1):强制1字节对齐,所有成员紧挨着放,中间不插空,省内存,但访问速度会慢一点。

根据以上结果可以说明:#pragma可以改变编译器的默认对⻬数。 所以,结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数。

问题补充:

不写第二个行不行?后来才知道,第二个是必须写的,因为:

#pragma pack(1) 会影响它后面所有 的结构体定义,直到遇到 #pragma pack() 才会恢复。

如果不恢复,后面其他结构体也会按1字节对齐,可能会导致性能下降(或者跟其他代码不兼容)。所以标准的写法就是:用完之后马上恢复,避免影响其他代码


9.结构体传参

代码如下:

cpp 复制代码
struct S
{
    int data[1000];
    int num;
};

struct S s = {{1,2,3,4}, 1000};

// 方式1:传结构体本身(值传递)
void print1(struct S s)
{
    printf("%d\n", s.num);
}

// 方式2:传结构体指针(地址传递)
void print2(struct S* ps)
{
    printf("%d\n", ps->num);
}

int main()
{
    print1(s);    // 传结构体
    print2(&s);   // 传地址
    return 0;
}

那哪个更好?首选 print2(传地址)。


原因如下:

在空间开销方面print1 传结构体的时候,会把整个结构体拷贝一份压到栈上。这个结构体里有 int data[1000](4000字节)+ int num(4字节),一共4004字节,每次调用都要拷贝这么多数据,太浪费了。print2 只传一个指针,8字节(64位系统)或4字节(32位系统),省很多。

在时间开销方面:拷贝4004字节比拷贝8字节慢得多,尤其是结构体很大的时候,性能差距很明显。

在压栈开销方面:函数传参是要压栈的,数据越大,压栈消耗的时间和空间就越大。


结论

**结构体传参的时候,尽量传结构体的地址(指针),而不是传结构体本身。**除非结构体特别小(比如就一两个int),那传值和传指针差别不大,怎么写都行。但大多数情况下,传指针是更好的选择。


10.结构体实现位段

1. 什么是位段

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

  1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。

示例如下:

cpp 复制代码
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

A就是⼀个位段类型。那位段A所占内存的⼤⼩是多少

成员是 int,所以一次开4个字节(32个bit)。

_a 占2个bit,存进去,还剩30个bit。

_b 占5个bit,存进去,还剩25个bit。

_c 占10个bit,存进去,还剩15个bit。

轮到 _d 了,它需要30个bit,但当前剩下的只有15个bit,不够了 ,所以剩下的15个bit直接浪费掉 ,再重新开4个字节(32个bit)来存 _d

_d 占30个bit,放到新开的这个4字节里。

四个成员占47个比特位,而8个字节是64个比特位,所以一共用了 两个4字节 = 8个字节


2.位段的内存分配

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

示例如下:

cpp 复制代码
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

空间是如何开辟的?


3.位段的跨平台问题

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

总结:

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


4.位段的应⽤

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。


5. 位段使⽤的注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。

所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

代码如下:

cpp 复制代码
#include<stdio.h>
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	struct A sa = { 0 };
	scanf("%d", &sa._b);//这是错误的
	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa._b = b;
	return 0;
}

二.联合体

1. 联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成, 这些成员可以不同的类型。

但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。

给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

代码如下:

cpp 复制代码
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的定义
	union Un un = { 0 };
	//计算连个变量的⼤⼩
	printf("%d\n", sizeof(un));
	return 0;
}
cpp 复制代码
#include <stdio.h>

union Un
{
    char c; //1
    int i; //4
};

int main()
{
    union Un u;
    printf("%p\n", &u);
    printf("%p\n", &(u.c));
    printf("%p\n", &(u.i));

    return 0;
}

为什么是 4 个字节呢?

这i和c的起点都是一样的,对应这个红色的部分

看上面输出的结果,他们的内存地址也都是一样的。
结论 :联合体的成员是共用同一块内存空间的,因为联合至少要有保存最大的那个成员的能力,所以一个联合变量的大小至少是最大成员的大小。注意:在同一时间内你只可以使用联合体中的一个成员。


2.相同成员的结构体和联合体对⽐

我们再对⽐⼀下相同成员的结构体和联合体的内存布局情况。

cpp 复制代码
struct S
{
char c;
int i;
};
struct S s = {0};

union Un
{
char c;
int i;
};
union Un un = {0};

3. 联合体⼤⼩的计算

  • 联合的⼤⼩⾄少是最⼤成员的⼤⼩。
  • 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
cpp 复制代码
#include <stdio.h>
union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	//下⾯输出的结果是什么?
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;
}

过程如下:

找出成员大小:char[5]=5int=4 -->最大成员 = 5 字节;

找出各成员对齐数:1、4 --> 最大对齐数 = 4。

判断:5 是不是 4 的整数倍?向上取最近的 4 的倍数:8 所以 sizeof(union Un1) = 8
成员大小:short[7]=14int=4 --> 最大成员 = 14 字节;

各成员对齐数:2、4 --> 最大对齐数 = 4;

判断:14 是不是 4 的整数倍?向上找最近的 4 的倍数: 4×3=12(小于 14,不行),4×4=16 所以 sizeof(union Un2) = 16


使⽤联合体是可以节省空间的,举例:

⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。

每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:书名、作者、⻚数

杯⼦:设计

衬衫:设计、可选颜⾊、可选尺⼨

代码如下:

cpp 复制代码
struct gift_list
{
	//公共属性
	int stock_number;//库存量
	double price; //定价
	int item_type;//商品类型
	//特殊属性
	char title[20];//书名
	char author[20];//作者
	int num_pages;//⻚数
	char design[30];//设计
	int colors;//颜⾊
	int sizes;//尺⼨
};

上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的⼤⼩就会偏⼤,⽐较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常⽤的。⽐如:

商品是图书,就不需要design、colors、sizes。

所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使⽤联合体起来,这样就可以介绍所需的内存空间,⼀定程度上节省了内存。

cpp 复制代码
struct gift_list
{
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型
union {
    struct
    {
    char title[20];//书名
    char author[20];//作者
    int num_pages;//⻚数
    }book;

    struct
    {
    char design[30];//设计
    }mug;

    struct
    {
    char design[30];//设计
    int colors;//颜⾊
    int sizes;//尺⼨
    }shirt;
    }item;
};

4.联合的⼀个练习(判断大小端)

定义:大小端是数据在内存中的存储方式:

  • 大端模式:数据的高位字节保存在低地址,低位字节保存在高地址(跟我们平时写数字的习惯一样)

  • 小端模式:数据的低位字节保存在低地址,高位字节保存在高地址

举个例子,int a = 0x11223344

地址 0x00 0x01 0x02 0x03
大端 11 22 33 44
小端 44 33 22 11

写⼀个程序,判断当前机器是⼤端?还是⼩端?

方法一:用指针判断

cpp 复制代码
#include <stdio.h>

int check_sys()
{
    int a = 1;              // 0x00000001
    return *(char*)&a;      // 取第一个字节
}

int main()
{
    int ret = check_sys();
    if (ret == 1)
        printf("小端\n");   // 第一个字节是 01,说明低地址存低位
    else
        printf("大端\n");   // 第一个字节是 00,说明低地址存高位
    return 0;
}

过程如下:

int a = 1,十六进制是 0x00 00 00 01

强转成 char* 后,指向的是第一个字节(低地址)

如果第一个字节是 01 -->小端

如果第一个字节是 00 --> 大端


方法二:用联合体判断

cpp 复制代码
#include <stdio.h>

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

    u.i = 1;                 // 存整数 1
    return u.c;              // 取的是 int 的第一个字节
}

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

过程如下:

联合体的成员ic共用同一块内存

u.i = 1 写入4个字节

u.c 读取的是第一个字节(低地址)

如果第一个字节是1 --> 小端

如果第一个字节是0 -->大端


三. 枚举类型

1.枚举的定义

枚举就是把一个集合里所有可能的值一一列举出来,在C语言里用 enum 关键字定义,枚举成员本质上是整型常量,默认从0开始依次递增,也可以手动指定值。

枚举比 #define 宏更好用,因为它有类型检查、支持调试、相关常量可以分组管理,能让代码更可读、更易维护。

实际开发中,枚举常用于表示状态码、选项配置、星期月份这类取值固定的场景,用枚举名代替魔术数字,代码一看就懂,改起来也方便。

2. 枚举类型的声明

cpp 复制代码
enum Color//颜⾊
{
RED=2,
GREEN=4,
BLUE=8
};

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

这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值

cpp 复制代码
enum Color//颜⾊
{
RED=2,
GREEN=4,
BLUE=8
};

3. 枚举类型的优点

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

枚举的优点:

  • 增加代码的可读性和可维护性
  • 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
  • 便于调试,预处理阶段会删除 #define 定义的符号
  • 使⽤⽅便,⼀次可以定义多个常量
  • 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤

4.枚举类型的使⽤

cpp 复制代码
#include<stdio.h>
enum Color
{
    red,//0
    yellow,//1
    blue//1
};

int main()
{
    enum Color c = blue; // 创建一个变量c,并将blue赋给它
    c = yellow; // 这时将yellow赋给它,完全没有问题
    // 只能拿枚举常量给枚举变量赋值,才不会出现类型的差异

   // blue = 6; // error - 枚举常量是不能改变的
    printf("%d\n", c);

    return 0;
}

那是否可以拿整数给枚举变量赋值呢?在C语⾔中是可以的,但是在C++是不⾏的,C++的类型检查⽐较严格。

相关推荐
Escalating_xu1 小时前
C++11 彻底吃透 emplace_back 与 push_back(最全闭环:左右值|深浅拷贝|移动构造|万能引用|完美转发)
开发语言·c++
橙臣程1 小时前
深度学习11——Tokenization、embedding、位置编码
人工智能·深度学习·embedding
皓月斯语2 小时前
B3865 [GESP202309 二级] 小杨的 X 字矩阵 题解
开发语言·c++·矩阵·题解
Highcharts.js10 小时前
教程:基于 React + Highcharts 构建一个单页应用程序、按需数据拉取与图表渲染
开发语言·前端·数据结构·react.js·前端框架·highcharts·页面应用
头发还在的女程序员11 小时前
医院陪诊管理系统怎么选择?——2026 年选型避坑与架构参考
java·开发语言·陪诊系统·陪诊app·医院陪诊陪护
大鹏的NLP博客13 小时前
机器学习中的统计波动控制与表征几何优化原则:从函数稳定性到隐空间结构学习
人工智能·深度学习·机器学习
爱写代码的小朋友13 小时前
从零开始学 Win32 API:C++ 窗口编程实战(VS Code + MinGW-w64 命令行详解)
开发语言·c++
果汁华13 小时前
Function Calling 与 Python 实战完整指南
开发语言·网络·python
小小晓.14 小时前
C++:语句和作用域
开发语言·c++