IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言】自定义类型详解:结构体、枚举、联合 -> 正文阅读

[C++知识库]【C语言】自定义类型详解:结构体、枚举、联合

前言

C语言的数据类型包括基本类型(内置类型)、构造类型(自定义类型)、指针类型和空类型(void),其中基本类型就是我们常见的整形、浮点型,而自定义类型则包括数组、结构体、枚举、联合(共用体),数组我们已经非常熟悉了,今天我们主要学习自定义类型中其他几种类型:结构体、枚举以及联合。

一、结构体

结构体是一些值的集合,这些值称为成员变量;结构的每个成员可以是不同类型的变量,所以结构常用来描述复杂对象。

1、结构体的声明

一般声明

结构体的声明一般由结构体关键字 + 结构体标签 + 成员列表组成:

struct tag       //struct:结构体关键字  tag:结构体标签
{
	member - list;    //成员列表
}variable - list;  //变量列表(可以省略)

例如描述一本书:

struct Book
{
    char name[20];    //书名
    char author[20];  //作者
    char num[12];     //编号
    float price;      //价格
};    //注意最后面的这个分号不能丢

特殊声明

结构体声明的时候,可以不完全声明,即省略结构体标签,这种结构体被称为匿名结构体:

//匿名结构体
struct 
{
    member-list;
}x;

由于匿名结构体没有名字,所以不能在程序的其他位置使用该结构体创建结构体变量,而只能在结构体声明的同时定义结构体变量,也就是说,匿名结构体只能使用一次

我们可以用匿名结构体来描述一个学生:

struct
{
	char name[20];  //名字
	int age;        //年龄
	char sex[5];    //性别
	char id[20];    //学号
}stu;               //结构体变量

2、结构体的自引用

错误的自引用方式

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

上面这种结构体的声明方式是错误的,因为struct Node 中包含了一个struct Node 的Next,而Next中又会包含一个struct Node 的next,这样无限套娃,使得我们无法计算这个结构体的大小;正确的结构体自引用应该是一个结构体中包含指向该结构体的指针,如下所示:

正确的自引用方式

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

一个结构体中包含了一个指向该结构体的指针,实现了结构体的自引用,同时,由于指针的大小是固定的(4/8个字节),所以该结构体的大小也是可计算的。

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

结构体定义变量一共有两种方式,一种是在进行结构体声明的同时定义结构体变量,另一种是利用结构体类型来定义结构体变量。

struct Point
{
	int x;
	int y;
}p1;        //声明类型的同时定义变量p1

struct Point p2;  //利用结构体类型来定义变量p2

结构体变量的初始化和数组变量的初始化十分类似,在定义结构体变量的同时赋初值即可。

struct Stu        
{
	char name[15];
	int age;      
}s1 = { "zhangsan", 20 };          //初始化

struct Stu s2 = { "lisi", 22 };    //初始化

struct Node
{
	int data;
	struct Point p;
	struct Node* next; 
}n1 = {10, {4,5}, NULL};            //结构体嵌套初始化

struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

4、结构体传参

结构体传参分为两种方式:一种是传递整个结构体,这时形参需要创建一个与源结构体同等大小的空间来接收,结构体过大浪费空间的同时时会十分影响效率;另一种是传递结构体的地址,这时无论源结构体有多大,形都参只需要用一个结构体指针来接收,节省空间的同时提高效率。

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };

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

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

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

print1 和 print2 相比,首选 print2 函数,原因如下:

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

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

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

5、结构体内存对齐(重要)

结构体内存对齐是结构体大小的计算规则,是校招笔试和面试过程中一个十分热门的考点,希望大家认真对待。

在学习结构体内存对齐之前,我们先给两组计算结构体大小的题目,看看你能否做对:

//计算结构体大小
#include <stdio.h>
struct S1 
{
	char c1;
	int i;
	char c2;
};

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

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

image-20220712173935661对答案有疑问的同学不要慌,我们在学习结构体内存对齐的过程中来分析答案的由来。

结构体内存对齐的规则

关于结构体内存对齐规则,大部分参考资料是这样说的:

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

  2. 其他成员变量要对齐到它的对齐数的整数倍的地址处。

    • 对齐数 = 编译器默认的对齐数与该成员变量大小的较小值。
    • VS的默认对齐数是8.
    • 只有VS编译器下才有默认对齐数的概念,其他编译器下变量的对齐数 = 变量的大小
  3. 结构体总大小为最大对齐数的整数倍。(最大对齐数为所有变量的对齐数的最大值)

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

知道了最大对齐数的对齐规则,我们再来看上面的练习题:

struct S1 
{
	char c1;  //变量大小为1,默认对齐数为8 -> 对齐数为1
	int i;    //变量大小为4,默认对齐数为8 -> 对齐数为4
	char c2;  //变量大小为1,默认对齐数为8 -> 对齐数为1
};

我们假设struct S1的起始位置为图中箭头所示位置,则各位置的偏移量如图;由内存对齐的规则:

第一个成员在与结构体变量偏移量为0的地址处:所以c1在偏移量为0处,且c1占一个字节;

其他成员变量要对齐到它的对齐数的整数倍的地址处:由于 i 的对齐数是4,所以 i 只能从偏移量为4的位置开始存储,且 i 占四个字节;

其他成员变量要对齐到它的对齐数的整数倍的地址处:由于 c2 的对齐数是1,所以 c2 紧挨着 i 存储,且 c2 占一个字节;

结构体总大小为最大对齐数的整数倍:由于最大对齐数为4,所以总对齐数要为4的倍数,大于9的最小的4的倍数为12,所以整个结构体的大小为12个字节。image-20220712180818425

struct S2 
{
	char c1;  //变量大小为1,默认对齐数为8 -> 对齐数为1
	char c2;  //变量大小为1,默认对齐数为8 -> 对齐数为1
	int i;    //变量大小为4,默认对齐数为8 -> 对齐数为4
};

如图:image-20220712181237786c1 从0偏移处开始,占一个字节;c2 对齐数为1,所以紧挨着 c1 存储,占一个字节;i 对齐数为4,所以在4的整数倍位置 – 4偏移处开始存储,占4个字节;存放完毕后0~7一共占8个字节,因为最大对齐数为4,8为4的整数倍,所以不变。

6、offsetof函数

函数功能

检索结构体成员相对于结构体开头的偏移量。

函数参数

size_t offsetof( structName, memberName );
# size_t 函数返回值,返回成员的偏移量;
# structName 参数,结构体变量名;
# memberName 参数,成员变量名;

函数使用

#include <stdio.h>
#include <stddef.h>  //offsetof对应头文件
struct S1
{
	char c1;
	int i;
	char c2;
};

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

int main()
{
	printf("%d\t", offsetof(struct S1, c1));
	printf("%d\t", offsetof(struct S1, i));
	printf("%d\n", offsetof(struct S1, c2));

	printf("%d\t", offsetof(struct S2, c1));
	printf("%d\t", offsetof(struct S2, c2));
	printf("%d\n", offsetof(struct S2, i));

	return 0;
}

image-20220712232427656

7、为什么存在内存对齐

从上面的例子我们可以看到,结构体内存对齐会浪费一定的内存空间,但是计算机不是要尽可能的做到不浪费资源吗?那为什么还要存在内存对齐呢?关于内存对齐存在的原因,大部分的参考资料是这样说的:

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

这里我对原因中的第二点做一下解释:

大家都知道,我们的机器分为32位机器和64位机器,这里的32位和64位其实指的是CPU的位数,而CPU的位数对应着CPU的字长,而字长又决定着CPU读取数据时一次访问多大即空间,即一次读取几个字节,我们以32位机器为例:

image-20220712183831801如图,32位机器一次访问四个字节的大小,如果不存在内存对齐,那么要取出 i 中的数据需要两次读取,存在内存对齐则只需要读取一次。

设计结构体的技巧

在了解了结构体的对齐规则之后,有没有一种方法能让我们在设计结构体的时候既满足对齐规则,又能尽量的节省空间呢?其实是有的,方法就是:**让占用空间小的成员尽量集中在一起。**就像的习题,我们把占用空间下的 c1 和 c2 放在一起,从而使得 struct S2 比 struct S1 小了四个字节。

8、修改默认对齐数

我们可以使用 “#pragma pack(num)” 命令来修改VS中的默认对齐数。例如:

#include <stdio.h>

#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

image-20220712184907940

在 struct S2 中,我们通过 " #pragma pack(1) " 命令把VS的默认对齐数设置为1(相当于不对齐),使得其大小变为6。

9、结构体大小计算习题

习题1

#include <stdio.h>

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

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

image-20220712185417651

d 从0偏移处开始存储,占8个字节,所以0~7;c 紧挨 d 存储,占一个字节,所以8,i 从4的整数倍即12处开始存储,占4个字节,所以12~15;所以0 ~ 15合计16个字节,16为最大对齐数8的倍数,所以不变。

习题2

#include <stdio.h>

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

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

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

c1 从0偏移位置开始存储,占一个字节,所以0;struct S3 s3 我们上面已经算出占16个字节,又因为嵌套的结构体对齐到自己的最大对齐数的整数倍处,所以从8的整数倍即8偏移处开始存储,所以8~23;d 从8的整数倍即24偏移处开始存储,占8个字节,所以24~31;合计32个字节,且为最大偏移数8的整数倍,所以不变。

习题3

#include <stdio.h>

#pragma pack(4)
struct tagTest1
{
    short a;
    char d;
    long b;
    long c;
};
struct tagTest2
{
    long b;
    short c;
    char d;
    long a;
};
struct tagTest3
{
    short c;
    long b;
    char d;
    long a;
};
#pragma pack()

int main(int argc, char* argv[])
{
    struct tagTest1 stT1;
    struct tagTest2 stT2;
    struct tagTest3 stT3;

    printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));
    return 0;
}

image-20220712190713050

stT1:

a: 0~1 d:2 b:4~7 c:8~11 合计:0~11 = 12(4的倍数);

stT2:

b:0~3 c:4~5 d:6 a:8~11 合计:0~11 = 12(4的倍数);

stT3:

c:0~1 b:4~7 d:8 a:12~15 合计:0~15 = 16(4的倍数);


二、位段

1、什么是位段

在我们的生活中总有一些数据的取值情况是小于一个字节的,比如月份的取值是1~12,那么只需要4个比特位就能表示所有的月份;一周的星期是1 ~ 7,那么只需要3个比特位就能涵盖所有取值;又比如人的性别是男和女,那么只需要一个比特位就能表示所有情况。基于上面这种情况,C语言中出现了位段的概念。

位段:C语言允许在一个结构体中以位(比特位)为单位来指定其成员所占内存长度,这种以位为单位的成员称为 " 位段"或称 “位域” ( bit field) ;利用位段能够用较少的位数存储数据。

2、位段的声明

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

  1. 位段的成员必须是 int、unsigned int 、signed int 或者是 char 。 (一般来说,一个结构体的所有位段成员的数据类型是相同的 ,即要么全为 int,要么全为 char)

  2. 位段的成员名后边有一个冒号和一个数字。

例如:

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;;
}

3、位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char(属于整形家族) 类型
  2. 位段的空间是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
  4. 总结:跟结构体相比,位段可以达到同样的效果,且可以很好的节省空间,但是有跨平台的问题存在。

4、位段的跨平台问题

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

以上面的 struct S 为例:

首先,位段成员的数据类型是 char,那么编译器就会在内存中为 struct S 开辟一个字节的空间,如果不够,再继续开辟;

接着, a 占3个比特,b 占4个比特,加起来一共7个比特,所有第一个字节中现在还剩下一个比特的空间;

然后,c 需要5个比特的空间,这里问题来了,c 是直接从后面一个字节中4中拿5个比特,还是说先从后面的字节中拿4个比特,再从前面的字节中拿剩下的一个比特,即当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,这些剩余的位是舍弃还是利用呢?这是C语言标准未定义的;

最后,我们再来看 main 函数,在 main 函数中我们把10赋给结构体中的,我们知道10的二进制序列为 1010,但是 a 变量只有3个比特的大小,所以10会发生截断后将 010 放入 a 中,但是这里问题又来了,010是放进靠左的三个比特,还是放进靠右的三个比特呢?即位段中的成员在内存中从左向右分配,还是从右向左分配呢?这也是C语言标准未定义的;

所以我们说,位段涉及很多不确定因素,是不跨平台的,注重可移植的程序应该避免使用位段。

5、VS下位段的使用习惯

这里我直接说结论,在VS编译器下,位段的使用习惯是这样的:

  1. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,直接舍弃剩余的位;
  2. 位段中的成员在内存中是从右向左分配的;

接下来我们来验证这个结论:还是用上面那个结构体

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;;
}

以VS下位段的使用习惯条件下:

首先,此结构体 a 和 b 变量占去一个字节中的7的比特位,并把最后一个比特位丢弃;c 变量占一个比特位,并把剩余的3个比特位丢弃;d 变量占4个比特位,并把剩下的四个比特位丢弃;所以 struct S 一共占3个字节打下;然后在 main 函数中把结构体成员全部初始化为0;此时内存中的数据是:00 00 00 00 | 00 00 00 00 | 00 00 00 00;

然后,对于 a 变量来说,由于10的二进制序列为 1010,大于3个比特,所以会10会截断变成 010 后放入第一个字节中靠右的3个比特位中,此时内存中的数据是:00 00 010 10 | 00 00 00 00 | 00 00 00 00;

对于 b 变量来说,12的二进制序列是 1100,b 变量能放下,所以 1100 会放入第一个字节中 a 数据的前面,此时内存中的数据是:01 10 00 10 |00 00 00 00 | 00 00 00 00;

对于 c 变量来说,,3的二进制序列为 11,小于5个比特,所以补0变成 00011 后放入第二个字节中靠右的比特位中,此时内存中的数据是:01 10 00 10 |00 00 00 11 | 00 00 00 00;

对于 d 变量来说,4的二进制序列为 100,小于4个比特,补0变成 0100 后放入第三个字节中靠右的比特位中,此时内存中的数据是:01 10 00 10 |00 00 00 11 | 00 00 01 00

所以最终内存中的数据变为:01 10 00 10 |00 00 00 11 | 00 00 01 00,转变为16进制就是 0x 62 03 04image-20220712224652469image-20220712224734712

在VS下测试发现结果正如我们预料的那样,所以结论成立。

6、位段的用途

我们了解了位段的优缺点之后,可能有的同学会有疑惑,位段存在这么大的问题,在实际开发中真的会用到它吗?其实是会的,位段的一个常见的用途就是用于 ip数据报,如图:

如图:在 ip数据报中,版本只占4个比特,头部长度只占4个比特,服务类型只占8个比特,等等,如果这些数据我们都用一个整形大小,即32个比特位来存储的话,那么就会在一定程度上增加数据报的大小,从而增加网络负载,减缓传输效率,所以在这里,位段的作用就得到了很好的体现。


二、枚举

1、什么是枚举

顾名思义,枚举就是一一列举,把一个数据可能的取值全部列举出来,比如一周有七天,一年有12个月,性别有男、女,这些都是枚举的使用场景。

2、枚举类型的声明

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

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

enum Color//颜色
{
	RED = 3,
	GREEN,
	BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。 大括号中的内容是枚举类型的可能取值,也叫枚举常量 。这些枚举常量都是有值的,默认从0开始,每次递增1,当然我们也可以在定义的时候为其赋初值,给某一枚举常量赋初值之后,其后面的常量仍然是每次递增1。image-20220713151234834

3、枚举的优点

我们知道,在C语言中我们可以利用 #define 来定义常量,那为什么还要单独设计出一个枚举类型来定义枚举常量呢?其实是因为枚举有如下优点:

  1. 增加代码的可读性和可维护性 :我们使用枚举常量来给枚举变量赋值,可以使得这个变量变得有意义,增加其可读性和可维护性;
  2. 和 #define 定义的标识符相比,枚举有类型检查,更加严谨:在使用像C++这种语法检查较为严格的编程语言时,枚举变量必须用枚举常量来赋值,而不能使用普查常量来赋值;image-20220713152218237
  3. 防止了命名污染(封装);
  4. 便于调试 :用 #define 定义的常量在程序的预处理阶段就会被替换掉,不便于调试观察;
  5. 使用方便,一次可以定义多个常量;

4、枚举的使用

enum Color//颜色
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};
enum Color clr = GREEN;  //使用枚举类型定义枚举变量并初始化

三、联合

1、什么是联合

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

2、联合的声明

联合的声明与结构体的声明十分类似,只是把关键字 struct 变为了 union。

union tag       //struct:结构体关键字  tag:结构体标签
{
	member - list;    //成员列表
}variable - list;  //变量列表(可以省略)

例如:

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

//联合变量的定义
union Un un;

3、联合的特点

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

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

int main()
{
	printf("%d\n", &un);
	printf("%d\n", &(un.i));
	printf("%d\n", &(un.c));  
}

因为联合体成员公用同一块内存空间,所以联合变量的地址与每个联合成员变量的地址都是相同的。image-20220713153543933

4、联合大小的计算

联合大小的计算规则如下:

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

例如:

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));
}

union Un1:c 占的对齐数为1,占5个字节;i 的对齐数为4,占4个字节;二者共用一块内存本来只需要5个字节的大小,但是由于需要对齐到最大对齐数的整数倍处,所以 union Un1 最终占8个字节;

union Un2:c 的对齐数为2,占14个字节;i 的对齐数为4,占4个字节;二者共用一块内存本来只需要14个字节的大小,但是由于需要对齐到最大对齐数的整数倍处,所以 union Un2 最终占16个字节;

5、利用联合判断大小端

在前面的文章中我们介绍大大小端,并且提供了判断大小端的代码,今天我们用联合的方法来实现对判断大小端的判断:

#include <stdio.h>

int Check_Sys()  //规定返回1为小端,返回0位大端
{
	union  //因为联合体只在本函数内调用一次,所以可以声明为匿名联合体
	{
		char c;
		int i;
	}un;

	un.i = 1;     //将联合中的整型变量赋值为1
	return un.c;  //返回 c 变量,即返回内存中第一个字节的数据,小端,内存中第一个字节数据为1 ,大端则为0
}

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

image-20220713155650016


  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:03:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:28:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码