侵权投诉

C语言之结构体的声明与定义

玩转单片机 2020-07-09 09:06 次阅读

有的时候,我们所遇到的数据结构,不仅仅是一群数字或者是字符串那么简单。比如我们每一个人的学籍信息,学号是一个长整数,名字却是字符;甚至有更复杂的情况,这种问题在现实生活中并不少见。我们之前学过一种叫数组的数据结构,它可以允许我们把很多同类型的数据集中在一起处理。相对于之前,这已经是一次极大的进步。但是,新的问题,往往又会出现,这个时候,我们就得上更高端的装备——结构体。

相比于数组,结构体有以下的更强大的优势:

批量存储数据

存储不同类型的数据

支持嵌套

结构体的声明与定义

声明

结构体的声明使用struct关键字,如果我们想要把我们的学籍信息组织一下的话,可以这样表示:

struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     unsigned int year;//入学年份,用无符号整数表示     unsigned int years;//学制,用无符号整数表示 }

这样,我们就相当于描绘好了一个框架,以后要用的话直接定义一个这种类型的变量就好了。

定义

我们刚刚申请了一个名叫Info的结构体类型,那么理论上我们可以像声明其他变量的操作一样,去声明我们的结构体操作,但是C语言中规定,声明结构体变量的时候,struct关键字是不可少的。

struct 结构体类型名 结构体变量名

不过,你可以在某个函数里面定义:

#include  struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     unsigned int year;//入学年份,用无符号整数表示     unsigned int years;//学制,用无符号整数表示 }; int main(void) {     /**      *在main函数中声明结构体变量      *结构体变量名叫info      *struct关键字不能丢      */     struct Info info;     ... }

也可以在声明的时候就把变量名定义下来(此时这个变量是全局变量):

#include  struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     unsigned int year;//入学年份,用无符号整数表示     unsigned int years;//学制,用无符号整数表示 } info; /**  *此时直接定义了变量  *该变量是全局变量  *变量名叫info  */ int main(void) {     ... }

访问结构体成员

结构体成员的访问有点不同于以往的任何变量,它是采用点号运算符.来访问成员的。比如,info.name就是引用info结构体的name成员,是一个字符数组,而info.year则可以查到入学年份,是个无符号整型。

比如,下面开始录入学生的信息:

//Example 01 #include  struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     unsigned int year;//入学年份,用无符号整数表示     unsigned int years;//学制,用无符号整数表示 }; int main(void) {     struct Info info;     printf("请输入学生的学号:");     scanf("%d", &info.identifier);     printf("请输入学生的姓名:");     scanf("%s", info.name);     printf("请输入学生的入学年份:");     scanf("%d", &info.year);     printf("请输入学生的学制:");     scanf("%d", &info.years);     printf(" 数据录入完毕 ");     printf("学号:%d 姓名:%s 入学年份:%d 学制:%d 毕业时间:%d ",          info.identifier, info.name, info.year, info.years, info.year + info.years);     return 0; }

运行结果如下:

//Consequence 01 请输入学生的学号:20191101 请输入学生的姓名:Harris 请输入学生的入学年份:2019 请输入学生的学制:4 数据录入完毕 学号:20191101 姓名:Harris 入学年份:2019 学制:4 毕业时间:2023

初始化结构体

像数组一样,结构体也可以在定义的时候初始化,方法也几乎一样:

struct Info info = {     20191101,     "Harris",     2019,     4 };

在C99标准中,还支持给指定元素赋值(就像数组一样):

struct Info info = {     .name = "Harris",     .year = 2019 };

对于没有被初始化的成员,则「数值型」成员初始化为0,「字符型」成员初始化为‘’。

对齐

下面这个代码,大家来看看会发生什么:

//EXample 02 V1 #include  int main(void) {     struct A     {         char a;         int b;         char c;     } a = {'a', 10, 'o'};          printf("size of a = %d ", sizeof(a));          return 0; }

我们之前学过,char类型的变量占1字节,int类型的变量占4字节,那么这么一算,一个结构体A型的变量应该就是6字节了。别急,我们看运行结果:

//COnsequence 02 V1 size of a = 12

怎么变成12了呢?标准更新了?老师教错了?都不是。我们把代码改一下:

//EXample 02 V2 #include  int main(void) {     struct A     {         char a;         char c;         int b;     } a = {'a', 'o', 10};          printf("size of a = %d ", sizeof(a));          return 0; }

结果:

//Consequence 02 V2 size of a = 8

实际上,这是编译器对我们程序的一种优化——内存对齐。在第一个例子中,第一个和第三个成员是char类型是1个字节,而中间的int却有4个字节,为了对齐,两个char也占用了4个字节,于是就是12个字节。

而在第二个例子里面,前两个都是char,最后一个是int,那么前两个可以一起占用4个字节(实际只用2个,第一个例子也同理,只是为了访问速度更快,而不是为了扩展),最后的int占用4字节,合起来就是8个字节。

关于如何声明结构体来节省内存容量,可以阅读下面的这篇文章,作者是艾瑞克·雷蒙,时尚最具争议性的黑客之一,被公认为开源运动的主要领导者之一:

英文原版,中文版

结构体嵌套

在学籍里面,如果我们的日期想要更加详细一些,精确到day,这时候就可以使用结构体嵌套来完成:

#include  struct Date {     unsigned int year;     unsigned int month;     unsigned int day; }; struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     struct Date date;/*---入学日期,用结构体Date表示---*/     unsigned int years;//学制,用无符号整数表示 }; int main(void) {     ... }

如此一来,比我们单独声明普通变量快多了。

不过,这样访问变量,就必须用点号一层层往下访问。比如要访问day这个成员,那就只能info.date.day而不能直接info.date或者info,day。

//Example 03 #include  struct Date {     unsigned int year;     unsigned int month;     unsigned int day; }; struct Info {     unsigned long identifier;//学号,用无符号长整数表示     char name[20];//名字,用字符数组表示     struct Date date;/*---入学日期,用结构体Date表示---*/     unsigned int years;//学制,用无符号整数表示 }; int main(void) {     struct Info info;     printf("请输入学生的学号:");     scanf("%d", &info.identifier);     printf("请输入学生的姓名:");     scanf("%s", info.name);     printf("请输入学生的入学年份:");     scanf("%d", &info.date.year);     printf("请输入学生的入学月份:");     scanf("%d", &info.date.month);     printf("请输入学生的入学日期:");     scanf("%d", &info.date.day);     printf("请输入学生的学制:");     scanf("%d", &info.years);     printf(" 数据录入完毕 ");     printf("学号:%d 姓名:%s 入学时间:%d/%d/%d 学制:%d 毕业时间:%d ",            info.identifier, info.name,            info.date.year, info.date.month, info.date.day,            info.years, info.date.year + info.years);     return 0; }

运行结果如下:

//Consequence 03 请输入学生的学号:20191101 请输入学生的姓名:Harris 请输入学生的入学年份:2019 请输入学生的入学月份:9 请输入学生的入学日期:7 请输入学生的学制:4 数据录入完毕 学号:20191101 姓名:Harris 入学时间:2019/9/7 学制:4 毕业时间:2023

结构体数组

刚刚我们演示了存储一个学生的学籍信息的时候,使用结构体的例子。那么,如果要录入一批学生,这时候我们就可以沿用之前的思路,使用结构体数组。

我们知道,数组的定义,就是存放一堆相同类型的数据的容器。而结构体一旦被我们声明,那么你就可以把它看作一个类型,只不过是你自己定义的罢了。

定义结构体数组也很简单:

struct 结构体类型 {     成员; } 数组名[长度]; /****或者这样****/ struct 结构体类型 {     成员; }; struct 结构体类型 数组名[长度];

结构体指针

既然我们可以把结构体看作一个类型,那么也就必然有对应的指针变量。

struct Info* pinfo;

但是在指针这里,结构体和数组就不一样了。我们知道,数组名实际上就是指向这个数组第一个元素的地址,所以可以将数组名直接赋值给指针。而结构体的变量名并不是指向该结构体的地址,所以要使用取地址运算符&才能获取地址:

pinfo = &info;

通过结构体指针来访问结构体有以下两种方法:

(*结构体指针).成员名

结构体指针->成员名

第一个方法由于点号运算符比指针的取值运算符优先级更高,因此需要加一个小括号来确定优先级,让指针先解引用变成结构体变量,在使用点号的方法去访问。

相比之下,第二种方法就直观许多。

这两种方法在实现上是完全等价的,但是点号只能用于结构体变量,而箭头只能够用于指针。

第一种方法:

#include  ... int main(void) {     struct Info *p;     p = &info;          printf("学号: ", (*p).identifier);     printf("姓名: ", (*p).name);     printf("入学时间:%d/%d/%d ", (*p).date.year, (*p).date.month, (*p).date.day);     printf("学制: ", (*p).years);     return 0; }

第二种方法:

#include  ... int main(void) {     struct Info *p;     p = &info;          printf("学号: ", p -> identifier);     printf("姓名: ", p -> name);     printf("入学时间:%d/%d/%d ", p -> date.year, p -> date.month, p -> date.day);     printf("学制: ", p -> years);     return 0; }

传递结构体信息

传递结构体变量

我们先来看看下面的代码:

//Example 04 #include  int main(void) {     struct Test     {         int x;         int y;     }t1, t2;     t1.x = 3;     t1.y = 4;     t2 = t1;     printf("t2.x = %d, t2.y = %d ", t2.x, t2.y);     return 0; }

运行结果如下:

//Consequence 04 t2.x = 3, t2.y = 4

这么看来,结构体是可以直接赋值的。那么既然这样,作为函数的参数和返回值也自然是没问题的了。

先来试试作为参数:

//Example 05 #include  struct Date {     unsigned int year;     unsigned int month;     unsigned int day; }; struct Info {     unsigned long identifier;     char name[20];     struct Date date;     unsigned int years; }; struct Info getInput(struct Info info); void printInfo(struct Info info); struct Info getInput(struct Info info) {     printf("请输入学号:");     scanf("%d", &info.identifier);     printf("请输入姓名:");     scanf("%s", info.name);     printf("请输入入学年份:");     scanf("%d", &info.date.year);     printf("请输入月份:");     scanf("%d", &info.date.month);     printf("请输入日期:");     scanf("%d", &info.date.day);     printf("请输入学制:");     scanf("%d", &info.years);     return info; } void printInfo(struct Info info) {     printf("学号:%d 姓名:%s 入学时间:%d/%d/%d 学制:%d 毕业时间:%d ",          info.identifier, info.name,          info.date.year, info.date.month, info.date.day,          info.years, info.date.year + info.years); } int main(void) {     struct Info i1 = {};     struct Info i2 = {};     printf("请录入第一个同学的信息... ");     i1 = getInput(i1);     putchar(' ');     printf("请录入第二个学生的信息... ");     i2 = getInput(i2);     printf(" 录入完毕,现在开始打印... ");     printf("打印第一个学生的信息... ");     printInfo(i1);     putchar(' ');     printf("打印第二个学生的信息... ");     printInfo(i2);     return 0; }

运行结果如下:

//Consequence 05 请录入第一个同学的信息... 请输入学号:20191101 请输入姓名:Harris 请输入入学年份:2019 请输入月份:9 请输入日期:7 请输入学制:4 请录入第二个学生的信息... 请输入学号:20191102 请输入姓名:Joy 请输入入学年份:2019 请输入月份:9 请输入日期:8 请输入学制:5 录入完毕,现在开始打印... 打印第一个学生的信息... 学号:20191101 姓名:Harris 入学时间:2019/9/7 学制:4 毕业时间:2023 打印第二个学生的信息... 学号:20191102 姓名:Joy 入学时间:2019/9/8 学制:5 毕业时间:2024

传递指向结构体变量的指针

早期的C语言是不允许直接将结构体作为参数直接传递进去的。主要是考虑到如果结构体的内存占用太大,那么整个程序的内存开销就会爆炸。不过现在的C语言已经放开了这方面的限制。

不过,作为一名合格的开发者,我们应该要去珍惜硬件资源。那么,传递指针就是一个很好的办法。

将刚才的代码修改一下:

//Example 06 #include  struct Date {     unsigned int year;     unsigned int month;     unsigned int day; }; struct Info {     unsigned long identifier;     char name[20];     struct Date date;     unsigned int years; }; void getInput(struct Info *info); void printInfo(struct Info *info); void getInput(struct Info *info) {     printf("请输入学号:");     scanf("%d", &info->identifier);     printf("请输入姓名:");     scanf("%s", info->name);     printf("请输入入学年份:");     scanf("%d", &info->date.year);     printf("请输入月份:");     scanf("%d", &info->date.month);     printf("请输入日期:");     scanf("%d", &info->date.day);     printf("请输入学制:");     scanf("%d", &info->years); } void printInfo(struct Info *info) {     printf("学号:%d 姓名:%s 入学时间:%d/%d/%d 学制:%d 毕业时间:%d ",          info->identifier, info->name,          info->date.year, info->date.month, info->date.day,          info->years, info->date.year + info->years); } int main(void) {     struct Info i1 = {};     struct Info i2 = {};     printf("请录入第一个同学的信息... ");     getInput(&i1);     putchar(' ');     printf("请录入第二个学生的信息... ");     getInput(&i2);     printf(" 录入完毕,现在开始打印... ");     printf("打印第一个学生的信息... ");     printInfo(&i1);     putchar(' ');     printf("打印第二个学生的信息... ");     printInfo(&i2);     return 0; }

此时传递的就是一个指针,而不是一个庞大的结构体。

动态申请结构体

结构体也可以在堆里面动态申请:

//Example 01 #include  ... int main(void) {     struct Info *i1;     struct Info *i2;          i1 = (struct Info *)malloc(sizeof(struct Info));     i2 = (struct Info *)malloc(sizeof(struct Info));     if (i1 == NULL || i2 == NULL)     {         printf("内存分配失败! ");         exit(1);     }          printf("请录入第一个同学的信息... ");     getInput(i1);     putchar(' ');     printf("请录入第二个学生的信息... ");     getInput(i2);     printf(" 录入完毕,现在开始打印... ");     printf("打印第一个学生的信息... ");     printInfo(i1);     putchar(' ');     printf("打印第二个学生的信息... ");     printInfo(i2);          free(i1);     free(i2);          return 0; }

实战:建立一个图书馆数据库

实际上,我们建立的数组可以是指向结构体指针的数组。

代码实现如下:

//Example 02 #include  #include  #define MAX_SIZE 100 struct Date {     int year;     int month;     int day; }; struct Book {     char title[128];     char author[48];     float price;     struct Date date;     char publisher[48]; }; void getInput(struct Book* book);//录入数据 void printBook(struct Book* book);//打印数据 void initLibrary(struct Book* lib[]);//初始化结构体 void printLibrary(struct Book* lib[]);//打印单本书数据 void releaseLibrary(struct Book* lib[]);//释放内存 void getInput(struct Book* book) {     printf("请输入书名:");     scanf("%s", book->title);     printf("请输入作者:");     scanf("%s", book->author);     printf("请输入售价:");     scanf("%f", &book->price);     printf("请输入出版日期:");     scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);     printf("请输入出版社:");     scanf("%s", book->publisher); } void printBook(struct Book* book) {     printf("书名:%s ", book->title);     printf("作者:%s ", book->author);     printf("售价:%.2f ", book->price);     printf("出版日期:%d-%d-%d ", book->date.year, book->date.month, book->date.day);     printf("出版社:%s ", book->publisher); } void initLibrary(struct Book* lib[]) {     for (int i = 0; i < MAX_SIZE; i++)     {         lib[i] = NULL;     } } void printLibrary(struct Book* lib[]) {     for (int i = 0; i < MAX_SIZE; i++)     {         if (lib[i] != NULL)         {             printBook(lib[i]);             putchar(' ');         }     } } void releaseLibrary(struct Book* lib[]) {     for (int i = 0; i < MAX_SIZE; i++)     {         if (lib[i] != NULL)         {             free(lib[i]);         }     } } int main(void) {     struct Book* lib[MAX_SIZE];     struct Book* p = NULL;     int ch, index = 0;     initLibrary(lib);     while (1)     {         printf("请问是否要录入图书信息(Y/N):");         do         {             ch = getchar();         } while (ch != 'Y' && ch != 'N');         if (ch == 'Y')         {             if (index < MAX_SIZE)             {                 p = (struct Book*)malloc(sizeof(struct Book));                 getInput(p);                 lib[index] = p;                 index++;                 putchar(' ');             }             else             {                 printf("数据库已满! ");                 break;             }         }         else         {             break;         }     }     printf(" 数据录入完毕,开始打印验证... ");     printLibrary(lib);     releaseLibrary(lib);     return 0; }

运行结果如下:

//Consequence 02 请问是否要录入图书信息(Y/N):Y 请输入书名:人类简史 请输入作者:尤瓦尔·赫拉利 请输入售价:32.25 请输入出版日期:2016-3-4 请输入出版社:中信出版集团 请问是否要录入图书信息(Y/N):N 数据录入完毕,开始打印验证... 书名:人类简史 作者:尤瓦尔·赫拉利 售价:32.25 出版日期:2016-3-4 出版社:中信出版集团

单链表

我们知道,数组变量在内存中,是连续的,而且不可拓展。显然在一些情况下,这种数据结构拥有很大的局限性。比如移动数据的时候,会牵一发而动全身,尤其是反转这种操作更加令人窒息。那么,需要需要一种数据结构来弄出一种更加灵活的“数组”,那么这,就是「链表」。

本节我们只讲讲单链表。

所谓链表,就是由一个个「结点」组成的一个数据结构。每个结点都有「数据域」和「指针域」组成。其中数据域用来存储你想要存储的信息,而指针域用来存储下一个结点的地址。如图:

单链表

当然,链表最前面还有一个头指针,用来存储头结点的地址。

这样一来,链表中的每一个结点都可以不用挨个存放,因为有了指针把他们串起来。因此结点放在哪都无所谓,反正指针总是能够指向下一个元素。我们只需要知道头指针,就能够顺藤摸瓜地找到整个链表。

因此对于学籍数据库来说,我们只需要在Info结构体中加上一个指向自身类型的成员即可:

struct Info {     unsigned long identifier;     char name[20];     struct Date date;     unsigned int years;     struct Info* next; };

在单链表中插入元素

头插法

这种每次都将数据插入单链表的头部(头指针后面)的插入法就叫头插法。

如果要把学生信息加入到单链表,可以这么写:

void addInfo(struct Info** students)//students是头指针 {     struct Info* info, *temp;     info = (struct Info*)malloc(sizeof(struct Info));     if (info == NULL)     {         printf("内存分配失败! ");         exit(1);     }          getInput(info);          if (*students != NULL)     {         temp = *students;         *students = info;         info->next = temp;     }     else     {         *students = info;         info->next = NULL;     } }

由于students存放的是头指针,因此我们需要传入它的地址传递给函数,才能够改变它本身的值。而students本身又是一个指向Info结构体的指针,所以参数的类型应该就是struct Info**。

往单链表里面添加一个结点,也就是先申请一个结点,然后判断链表是否为空。如果为空,那么直接将头指针指向它,然后next成员指向NULL。若不为空,那么先将next指向头指针原本指向的结点,然后将头指针指向新结点即可。

那么,打印链表也变得很简单:

void printStu(struct Info* students) {     struct Info* info;     int count = 1;          info = students;     while (book != NULL)     {         printf("Student%d: ", count);         printf("姓名:%s ", info->name);         printf("学号:%d ", info->identifier);         info = info->next;         count++;     } }

想要读取单链表里面的数据,只需要迭代单链表中的每一个结点,直到next成员为NULL,即表示单链表的结束。

最后,当然还是别忘了释放空间:

void releaseStu(struct Info** students) {     struct Info* temp;          while (*students != NULL)     {         temp = *students;         *students = (*students)->next;         free(temp);     } }

尾插法

与头插法类似,尾插法就是把每一个数据都插入到链表的末尾。

void addInfo(struct Info** students) {     struct Info* info, *temp;     info = (struct Info*)malloc(sizeof(struct Info));     if (info == NULL)     {         printf("内存分配失败! ");         exit(1);     }          getInput(info);          if (*students != NULL)     {         temp = *students;         *students = info;         //定位到链表的末尾的位置         while (temp->next != NULL)         {             temp = temp->next;         }         //插入数据         temp->next = info;         info->next = temp;     }     else     {         *students = info;         info->next = NULL;     } }

这么一来,程序执行的效率难免要降低很多,因为每次插入数据,都要先遍历一次链表。如果链表很长,那么对于插入数据来说就是一次灾难。不过,我们可以给程序添加一个指针,让它永远都指向链表的尾部,这样一来,就可以用很少的空间换取很高的程序执行效率。

代码更改如下:

void addInfo(struct Info** students) {     struct Info* info, *temp;     static struct Info* tail;//设置静态指针     info = (struct Info*)malloc(sizeof(struct Info));     if (info == NULL)     {         printf("内存分配失败! ");         exit(1);     }          getInput(info);          if (*students != NULL)     {         tail->next = info;         info->next = NULL;     }     else     {         *students = info;         info->next = NULL;     } }

搜索单链表

单链表是我们用来存储数据的一个容器,那么有时候需要快速查找信息就需要开发相关搜索的功能。比如说输入学号,查找同学的所有信息。

struct Info *searchInfo(struct Info* students, long* target) {     struct Info* info;     info = students;     while (info != NULL)     {         if (info->identifier == target)         {             break;         }         info = info->next;     }          return book; }; void printInfo(struct Info* info) {     ... } ... int main(void) {     ...     printf(" 请输入学生学号:");     scanf("%d", input);     info = searchInfo(students, input);     if (info == NULL)     {         printf("抱歉,未找到相关结果! ");     }     else     {         do         {             printf("相关结果如下: ");             printInfo(book);         } while ((info = searchInfo(info->next, input)) != NULL);     }          releaseInfo(...);     return 0; }

插入结点到指定位置

到了这里,才体现出链表真正的优势。

设想一下,如果有一个有序数组,现在要求你去插入一个数字,插入完成之后,数组依然保持有序。你会怎么做?

没错,你应该会挨个去比较,然后找到合适的位置(当然这里也可以使用二分法,比较节省算力),把这个位置后面的所有数都往后移动一个位置,然后将我们要插入的数字放入刚刚我们腾出来的空间里面。

你会发现,这样的处理方法,经常需要移动大量的数据,对于程序的执行效率来说,是一个不利因素。那么链表,就无所谓。反正在内存中,链表的存储毫无逻辑,我们只需要改变指针的值就可以实现链表的中间插入。

//Example 03 #include  #include  struct Node {     int value;     struct Node* next; }; void insNode(struct Node** head, int value) {     struct Node* pre;     struct Node* cur;     struct Node* New;     cur = *head;     pre = NULL;     while (cur != NULL && cur->value < value)     {         pre = cur;         cur = cur->next;     }     New = (struct Node*)malloc(sizeof(struct Node));     if (New == NULL)     {         printf("内存分配失败! ");         exit(1);     }     New->value = value;     New->next = cur;     if (pre == NULL)     {         *head = New;     }     else     {         pre->next = New;     } } void printNode(struct Node* head) {     struct Node* cur;     cur = head;     while (cur != NULL)     {         printf("%d ", cur->value);         cur = cur->next;     }     putchar(' '); } int main(void) {     struct Node* head = NULL;     int input;     printf("开始插入整数... ");     while (1)     {         printf("请输入一个整数,输入-1表示结束:");         scanf("%d", &input);         if (input == -1)         {             break;         }         insNode(&head, input);         printNode(head);     }     return 0; }

运行结果如下:

//Consequence 03 开始插入整数... 请输入一个整数,输入-1表示结束:4 4 请输入一个整数,输入-1表示结束:5 4 5 请输入一个整数,输入-1表示结束:3 3 4 5 请输入一个整数,输入-1表示结束:6 3 4 5 6 请输入一个整数,输入-1表示结束:2 2 3 4 5 6 请输入一个整数,输入-1表示结束:5 2 3 4 5 5 6 请输入一个整数,输入-1表示结束:1 1 2 3 4 5 5 6 请输入一个整数,输入-1表示结束:7 1 2 3 4 5 5 6 7 请输入一个整数,输入-1表示结束:-1

删除结点

删除结点的思路也差不多,首先修改待删除的结点的上一个结点的指针,将其指向待删除结点的下一个结点。然后释放待删除结点的空间。

... void delNode(struct Node** head, int value) {     struct Node* pre;     struct Node* cur;          cur = *head;     pre = NULL;     while (cur != NULL && cur->value != value)     {         pre = cur;         cur = cur->next;     }     if (cur == NULL)     {         printf("未找到匹配项! ");         return ;     }     else     {         if (pre == NULL)         {             *head = cur->next;         }         else         {             pre->next = cur->next;         }         free(cur);     } }

内存池

C语言的内存管理,从来都是一个让人头秃的问题。要想更自由地管理内存,就必须去堆中申请,然后还需要考虑何时释放,万一释放不当,或者没有及时释放,造成的后果都是难以估量的。

当然如果就这些,那倒也还不算什么。问题就在于,如果大量地使用malloc和free函数来申请内存,首先使要经历一个从应用层切入系统内核层,调用完成之后,再返回应用层的一系列步骤,实际上使非常浪费时间的。更重要的是,还会产生大量的内存碎片。比如,先申请了一个1KB的空间,紧接着又申请了一个8KB的空间。而后,这个1KB使用完了,被释放,但是这个空间却只有等到下一次有刚好1KB的空间申请,才能够被重新调用。这么一来,极限情况下,整个堆有可能被弄得支离破碎,最终导致大量内存浪费。

那么这种情况下,我们解决这类问题的思路,就是创建一个内存池。

内存池,实际上就是我们让程序创建出来的一块额外的缓存区域,如果有需要释放内存,先不必使用free函数,如果内存池有空,那么直接放入内存池。同样的道理,下一次程序申请空间的时候,先检查下内存池里面有没有合适的内存,如果有,则直接拿出来调用,如果没有,那么再使用malloc。

其实内存池我们就可以使用单链表来进行维护,下面通过一个通讯录的程序来说明内存池的运用。

普通的版本:

//Example 04 V1 #include  #include  #include  struct Person {  char name[40];  char phone[20];  struct Person* next; }; void getInput(struct Person* person); void printPerson(struct Person* person); void addPerson(struct Person** contects); void changePerson(struct Person* contacts); void delPerson(struct Person** contacts); struct Person* findPerson(struct Person* contacts); void displayContacts(struct Person* contacts); void releaseContacts(struct Person** contacts); void getInput(struct Person* person) {  printf("请输入姓名:");  scanf("%s", person->name);  printf("请输入电话:");  scanf("%s", person->phone); } void addPerson(struct Person** contacts) {  struct Person* person;  struct Person* temp;  person = (struct Person*)malloc(sizeof(struct Person));  if (person == NULL)  {   printf("内存分配失败! ");   exit(1);  }  getInput(person);  //将person添加到通讯录中  if (*contacts != NULL)  {   temp = *contacts;   *contacts = person;   person->next = temp;  }  else  {   *contacts = person;   person->next = NULL;  } } void printPerson(struct Person* person) {  printf("联系人:%s ", person->name);  printf("电话:%s ", person->phone); } struct Person* findPerson(struct Person* contacts) {  struct Person* current;  char input[40];  printf("请输入联系人:");  scanf("%s", input);  current = contacts;  while (current != NULL && strcmp(current->name, input))  {   current = current->next;  }  return current; } void changePerson(struct Person* contacts) {  struct Person* person;  person = findPerson(contacts);  if (person == NULL)  {   printf("找不到联系人! ");  }  else  {   printf("请输入联系电话:");   scanf("%s", person->phone);  } } void delPerson(struct Person** contacts) {  struct Person* person;  struct Person* current;  struct Person* previous;  //先找到待删除的节点的指针  person = findPerson(*contacts);  if (person == NULL)  {   printf("找不到该联系人! ");  }  else  {   current = *contacts;   previous = NULL;   //将current定位到待删除的节点   while (current != NULL && current != person)   {    previous = current;    current = current->next;   }   if (previous == NULL)   {    //若待删除的是第一个节点    *contacts = current->next;   }   else   {    //若待删除的不是第一个节点    previous->next = current->next;   }   free(person);//将内存空间释放  } } void displayContacts(struct Person* contacts) {  struct Person* current;  current = contacts;  while (current != NULL)  {   printPerson(current);   current = current->next;  } } void releaseContacts(struct Person** contacts) {  struct Person* temp;  while (*contacts != NULL)  {   temp = *contacts;   *contacts = (*contacts)->next;   free(temp);  } } int main(void) {  int code;  struct Person* contacts = NULL;  struct Person* person;  printf("| 欢迎使用通讯录管理程序 | ");  printf("|--- 1:插入新的联系人 ---| ");  printf("|--- 2:查找现有联系人 ---| ");  printf("|--- 3:更改现有联系人 ---| ");  printf("|--- 4:删除现有联系人 ---| ");  printf("|--- 5:显示当前通讯录 ---| ");  printf("|--- 6:退出通讯录程序 ---| ");  while (1)  {   printf(" 请输入指令代码:");   scanf("%d", &code);   switch (code)   {   case 1:addPerson(&contacts); break;   case 2:person = findPerson(contacts);    if (person == NULL)    {     printf("找不到该联系人! ");    }    else    {     printPerson(person);    }    break;   case 3:changePerson(contacts); break;   case 4:delPerson(&contacts); break;   case 5:displayContacts(contacts); break;   case 6:goto END;   }  } END://此处直接跳出恒循环  releaseContacts(&contacts);  return 0; }

运行结果如下:

//Consequence 04 V1 | 欢迎使用通讯录管理程序 | |--- 1:插入新的联系人 ---| |--- 2:查找现有联系人 ---| |--- 3:更改现有联系人 ---| |--- 4:删除现有联系人 ---| |--- 5:显示当前通讯录 ---| |--- 6:退出通讯录程序 ---| 请输入指令代码:1 请输入姓名:HarrisWilde 请输入电话:0101111 请输入指令代码:1 请输入姓名:Jack 请输入电话:0101112 请输入指令代码:1 请输入姓名:Rose 请输入电话:0101113 请输入指令代码:2 请输入联系人:HarrisWilde 联系人:HarrisWilde 电话:0101111 请输入指令代码:2 请输入联系人:Mike 找不到该联系人! 请输入指令代码:5 联系人:Rose 电话:0101113 联系人:Jack 电话:0101112 联系人:HarrisWilde 电话:0101111 请输入指令代码:3 请输入联系人:HarrisWilde 请输入联系电话:0101234 请输入指令代码:5 联系人:Rose 电话:0101113 联系人:Jack 电话:0101112 联系人:HarrisWilde 电话:0101234 请输入指令代码:6

下面加入内存池:

//Example 04 V2 #include  #include  #include  #define MAX 1024 struct Person {  char name[40];  char phone[20];  struct Person* next; }; struct Person* pool = NULL; int count; void getInput(struct Person* person); void printPerson(struct Person* person); void addPerson(struct Person** contects); void changePerson(struct Person* contacts); void delPerson(struct Person** contacts); struct Person* findPerson(struct Person* contacts); void displayContacts(struct Person* contacts); void releaseContacts(struct Person** contacts); void releasePool(void); void getInput(struct Person* person) {  printf("请输入姓名:");  scanf("%s", person->name);  printf("请输入电话:");  scanf("%s", person->phone); } void addPerson(struct Person** contacts) {  struct Person* person;  struct Person* temp;  //如果内存池不是空的,那么首先从里面获取空间  if (pool != NULL)  {   person = pool;   pool = pool->next;   count--;  }  //内存池为空,则直接申请  else  {   person = (struct Person*)malloc(sizeof(struct Person));   if (person == NULL)   {    printf("内存分配失败! ");    exit(1);   }  }  getInput(person);  //将person添加到通讯录中  if (*contacts != NULL)  {   temp = *contacts;   *contacts = person;   person->next = temp;  }  else  {   *contacts = person;   person->next = NULL;  } } void printPerson(struct Person* person) {  printf("联系人:%s ", person->name);  printf("电话:%s ", person->phone); } struct Person* findPerson(struct Person* contacts) {  struct Person* current;  char input[40];  printf("请输入联系人:");  scanf("%s", input);  current = contacts;  while (current != NULL && strcmp(current->name, input))  {   current = current->next;  }  return current; } void changePerson(struct Person* contacts) {  struct Person* person;  person = findPerson(contacts);  if (person == NULL)  {   printf("找不到联系人! ");  }  else  {   printf("请输入联系电话:");   scanf("%s", person->phone);  } } void delPerson(struct Person** contacts) {  struct Person* person;  struct Person* current;  struct Person* previous;  struct Person* temp;  {  };  //先找到待删除的节点的指针  person = findPerson(*contacts);  if (person == NULL)  {   printf("找不到该联系人! ");  }  else  {   current = *contacts;   previous = NULL;   //将current定位到待删除的节点   while (current != NULL && current != person)   {    previous = current;    current = current->next;   }   if (previous == NULL)   {    //若待删除的是第一个节点    *contacts = current->next;   }   else   {    //若待删除的不是第一个节点    previous->next = current->next;   }   //判断内存池中有没有空位   if (count < MAX)   {    //使用头插法将person指向的空间插入内存池中    if (pool != NULL)    {     temp = pool;     pool = person;     person->next = temp;    }    else    {     pool = person;     person->next = NULL;    }    count++;   }   //没有空位,直接释放   else   {    free(person);//将内存空间释放   }  } } void displayContacts(struct Person* contacts) {  struct Person* current;  current = contacts;  while (current != NULL)  {   printPerson(current);   current = current->next;  } } void releaseContacts(struct Person** contacts) {  struct Person* temp;  while (*contacts != NULL)  {   temp = *contacts;   *contacts = (*contacts)->next;   free(temp);  } } void releasePool(void) {  struct Person* temp;  while (pool != NULL)  {   temp = pool;   pool = pool->next;   free(temp);  } } int main(void) {  int code;  struct Person* contacts = NULL;  struct Person* person;  printf("| 欢迎使用通讯录管理程序 | ");  printf("|--- 1:插入新的联系人 ---| ");  printf("|--- 2:查找现有联系人 ---| ");  printf("|--- 3:更改现有联系人 ---| ");  printf("|--- 4:删除现有联系人 ---| ");  printf("|--- 5:显示当前通讯录 ---| ");  printf("|--- 6:退出通讯录程序 ---| ");  while (1)  {   printf(" 请输入指令代码:");   scanf("%d", &code);   switch (code)   {   case 1:addPerson(&contacts); break;   case 2:person = findPerson(contacts);    if (person == NULL)    {     printf("找不到该联系人! ");    }    else    {     printPerson(person);    }    break;   case 3:changePerson(contacts); break;   case 4:delPerson(&contacts); break;   case 5:displayContacts(contacts); break;   case 6:goto END;   }  } END://此处直接跳出恒循环  releaseContacts(&contacts);  releasePool();  return 0; }

typedef

给数据类型起别名

C语言是一门古老的语言,它是在1969至1973年间,由两位天才丹尼斯·里奇和肯·汤普逊在贝尔实验室以B语言为基础开发出来的,用于他们的重写UNIX计划(这也为后来UNIX系统的可移植性打下了基础,之前的UNIX是使用汇编语言编写的,当然也是这两位为了玩一个自己设计的游戏而编写的)。天才就是和咱常人不一样,不过他俩的故事,在这篇里面不多啰嗦,我们回到话题。

虽然C语言诞生的很早,但是却依旧不是最早的高级编程语言。目前公认的最早的高级编程语言,是IBM公司于1957年开发的FORTRAN语言。C语言诞生之时,FORTRAN已经统领行业数十年之久。因此,C语言要想快速吸纳FORTRAN中的潜在用户,就必须做出一些妥协。

我们知道,不同的语言的语法,一般来说是不同的,甚至还有较大的差距。比如:

C:

int a, b, c; float i, j, k;

而FORTRAN语言是这样的:

integer :: a, b, c; real :: i, j, k;

如果让FORTRAN用户使用原来的变量名称进行使用,那么就能够快速迁移到C语言上面来,这就是typedef的用处之一。

我们使用FORTRAN语言的类型名,那就这么办:

typedef int integer; typedef float real; integer a, b, c; real i, j, k;

结构体的搭档

虽然结构体的出现能够让我们有一个更科学的数据结构来管理数据,但是每次使用结构体都需要struct...,未免显得有些冗长和麻烦。有了typedef的助攻,我们就可以很轻松地给结构体类型起一个容易理解的名字:

typedef struct date {     int year;     int month;     int day; } DATE;//为了区分,一般用全大写 int main(void) {     DATE* date;     ... }

甚至还可以顺便给它的指针也定义一个别名:

typedef struct date {     int year;     int month;     int day; } DATE, *PDATE;

进阶

我们还可以利用typedef来简化一些比较复杂的命令。

比如:

int (*ptr) [5];

我们知道这是一个数组指针,指向一个5元素的数组。那么我们可以改写成这样:

typedef int(*PTR_TO_ARRAY)[3];

这样就可以把很复杂的声明变得很简单:

PTR_TO_ARRAY a = &array;

取名的时候要尽量使用容易理解的名字,这样才能达到使用typedef的最终目的。

共用体

共用体也称联合体。

声明

和结构体还是有点像:

union 共用体名称 {     成员1;     成员2;     成员3; };

但是两者有本质的不同。共用体的每一个成员共用一段内存,那么这也就意味着它们不可能同时被正确地访问。如:

//Example 05 #include  #include  union Test {  int i;  double pi;  char str[9]; }; int main(void) {  union Test test;  test.i = 10;  test.pi = 3.14;  strcpy(test.str, "TechZone");  printf("test.i: %d ", test.i);  printf("test.pi: %.2f ", test.pi);  printf("test.str: %s ", test.str);  return 0; }

执行结果如下:

//Consequence 05 test.i: 1751344468 test.pi: 3946574856045802736197446431383475413237648487838717723111623714247921409395495328582015991082102150186282825269379326297769425957893182570875995348588904500564659454087397032067072.00 test.str: TechZone

可以看到,共用体只能正确地展示出最后一次被赋值的成员。共用体的内存应该要能够满足最大的成员能够正常存储。但是并不一定等于最大的成员的尺寸,因为还要考虑内存对齐的问题。

共用体可以类似结构体一样来定义和声明,但是共用体还可以允许不带名字:

union {  int i;  char ch;  float f; } a, b;

初始化

共用体不能在同一时间存放多个成员,所以不能批量初始化

union data {     int i;     char ch;     float f; }; union data a = {520};        //初始化第一个成员 union data b = a;            //直接使用一个共用体初始化另一个共用体 union data c = {.ch = 'C'};  //C99的特性,指定初始化成员

枚举

枚举是一个基本的数据类型,它可以让数据更简洁。

如果写一个判断星期的文章,我们当然可以使用宏定义来使代码更加易懂,不过:

#define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6 #define SUN 7

这样的写法有点费键盘。那么枚举就简单多了:

enum DAY {       MON=1, TUE, WED, THU, FRI, SAT, SUN };

**注意:**第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。

枚举变量的定义和声明方法和共用体一样,也可以省略枚举名,直接声明变量名。

//Example 06 #include  #include  int main() {     enum color { red = 1, green, blue };     enum  color favorite_color;     printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");     scanf("%d", &favorite_color);     //输出结果     switch (favorite_color)     {     case red:         printf("你喜欢的颜色是红色");         break;     case green:         printf("你喜欢的颜色是绿色");         break;     case blue:         printf("你喜欢的颜色是蓝色");         break;     default:         printf("你没有选择你喜欢的颜色");     }     return 0; }

执行结果如下:

//Consequence 06 请输入你喜欢的颜色: (1. red, 2. green, 3. blue): 3 你喜欢的颜色是蓝色

也可以把整数转换为枚举类型:

//Example 07 #include  #include  int main() {     enum day     {         saturday,         sunday,         monday,         tuesday,         wednesday,         thursday,         friday     } workday;     int a = 1;     enum day weekend;     weekend = (enum day) a;  //使用强制类型转换     //weekend = a; //错误     printf("weekend:%d", weekend);     return 0; }

运行结果如下:

//Consequence 07 weekend:1

位域

对于这样的设备,通常内存只有256B,那么能够给我们利用的资源就十分珍贵了。在这种情况下,如果我们只需要定义一个变量来存放布尔值,一般就申请一个整型变量,通过1和0来间接存储。但是,显然1和0只用1个bit就能够放完,而一个整型却是4个字节,也就是32bit。这就造成了内存的浪费。

好在,C语言为我们提供了一种数据结构,称为「位域」(也叫位端、位字段)。也就是把一个字节中的二进制位划分,并且你能够指定每个区域的位数。每个域有一个域名,并允许程序中按域名进行单独操作。

使用位域的做法是在结构体定义的时候,在结构体成员后面使用冒号(:)和数字来表示该成员所占的位数。

//Example 08 #include  int main(void) {  struct Test  {   unsigned int a : 1;   unsigned int b : 1;   unsigned int c : 2;  } test;    test.a = 0;  test.b = 1;  test.c = 2;  printf("a = %d, b = %d, c = %d ", test.a, test.b, test.c);  printf("size of test = %d ", sizeof(test));  return 0; }

运行结果如下:

//Consequence 08 a = 0, b = 1, c = 2 size of test = 4

如此一来,结构体test只用了4bit,却存放下了0、1、2三个整数。但是由于2在二进制中是10,因此占了2个bit。如果把test.b赋值为2,那么:

//Consequence 08 V2 a = 0, b = 0, c = 2 size of test = 4

可以看到,b中的10溢出了,只剩下0。

当然,位域的宽度不能够超过本身类型的长度,比如:

unsigned int a : 100;

那么就会报错:

错误C2034“main::a”: 位域类型对位数太小

位域成员也可以没有名称,只要给出类型和宽度即可:

struct Test {     unsigned int x : 1;     unsigned int y : 2;     unsigned int z : 3;     unsigned int : 26; };

无名位域一般用来作为填充或者调整成员的位置,因为没有名称,所以无名位域并不能够拿来使用。

C语言的标准只说明unsigned int和signed int支持位域,然后C99增加了_Bool类型也支持位域,其他数据类型理论上是不支持的。不过大多数编译器在具体实现时都进行了扩展,额外支持了signed char、unsigned char以及枚举类型,所以如果对char类型的结构体成员使用位域,基本上也没什么问题。但如果考虑到程序的可移植性,就需要谨慎对待了。另外,由于内存的基本单位是字节,而位域只是字节的一部分,所以并不能对位域进行取地址运算。

虽然科技发展日新月异,但是秉承着节约成本这个放之四海而皆准的原则,还是要注意使用!毕竟5毛钱可能是小钱,但是乘以5000万呢?

原文标题:C语言之结构体就这样被攻克了!(绝对值得收藏的文章)

文章出处:【微信号:mcu168,微信公众号:玩转单片机】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
分享:

评论

相关推荐

C语言中有三块“难啃的硬骨头”几乎是公认级别的

指针是学习绕不过去的知识点,而且学完C语言,下一步紧接着切换到数据结构和算法,指针是切换的重点,指针....
的头像 传感器技术 发表于 08-14 16:02 48次 阅读
C语言中有三块“难啃的硬骨头”几乎是公认级别的

嵌入式新手该先学C语言还是先学汇编?

另外,汇编没那么恐怖,也就是那十几个常用的命令,再加上配套的伪指令,基本程序结构,半天就差不多了。与....
的头像 传感器技术 发表于 08-14 10:16 68次 阅读
嵌入式新手该先学C语言还是先学汇编?

嵌入式系统用什么语言_生活中常见的嵌入式系统

 嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分。通常,嵌入式系统是一个控制程序存储在RO....
发表于 08-13 15:02 22次 阅读
嵌入式系统用什么语言_生活中常见的嵌入式系统

TMS320F2812原理及其C语言开发的PDF电子书免费下载

本书共分12章。第1章为处理器的功能以及开发环境CCS的介绍,用简单易懂的实例引领读者人门]。第2章....
发表于 08-13 08:00 8次 阅读
TMS320F2812原理及其C语言开发的PDF电子书免费下载

光耦控制点亮和延时关闭照明设备的C语言代码和仿真电路图免费下载

  本文档的主要内容详细介绍的是光耦控制点亮和延时关闭照明设备的C语言代码和仿真电路图免费下载。 ....
发表于 08-12 17:04 23次 阅读
光耦控制点亮和延时关闭照明设备的C语言代码和仿真电路图免费下载

Python标准库中文版资料合集

Python 的标准库包括了很多的模块, 从 Python 语言自身特定的类型和声明, 到一些只用于....
发表于 08-06 08:00 33次 阅读
Python标准库中文版资料合集

谭浩强版C语言程序设计PDF电子书免费下载

C语言是在 70 年代初问世的。一九七八年由美国电话电报公司(AT&T)贝尔实验室正式发表了C语言。....
发表于 08-06 08:00 49次 阅读
谭浩强版C语言程序设计PDF电子书免费下载

电子工程师成长之教训系列故事(上)终身受用的启发

勤奋真的很重要,特别是年轻人,不轻言放弃,遇事有钻研精神是技术上能有所成就的基本要求。只有亲身经历过....
的头像 电子发烧友网 发表于 08-05 14:52 274次 阅读
电子工程师成长之教训系列故事(上)终身受用的启发

C语言/C++基本语句编程风格

千万留意,无论是float还是double类型的变量,都有精度限制,所以一定要避免将浮点变量用“==....
的头像 玩转单片机 发表于 08-05 11:03 148次 阅读
C语言/C++基本语句编程风格

C语言开发单片机为啥都是全局变量形式?

要成为一个资深的嵌入式工程师相当难,一方面要有非常扎实的理论知识,同时也要有相当的那种大型的、高频C....
的头像 玩转单片机 发表于 08-05 10:35 262次 阅读
C语言开发单片机为啥都是全局变量形式?

单片机初学者都会遇到的10个问题

汇编语言是一种用文字助记符来表示机器指令的符号语言,是最接近机器码的一种语言。其主要优点是占用资源少....
的头像 玩转单片机 发表于 08-05 10:28 132次 阅读
单片机初学者都会遇到的10个问题

C语言指针详解

函数指针,形如: #define PMYFUN (void*)(int,int) ,可以用在大量分支....
的头像 玩转单片机 发表于 08-05 10:17 108次 阅读
C语言指针详解

有没有想过为什么嵌入式开发要使用C语言呢?

新型的单片机,寄存器成百上千,全靠记忆简直天方夜谭,复杂的大型项目不是一个人能完成的,汇编的移植性和....
的头像 玩转单片机 发表于 08-05 10:14 174次 阅读
有没有想过为什么嵌入式开发要使用C语言呢?

采用标准C的强制转换和指针的概念来实现访问MCU的寄存器

总结一下,就是(*(volatile unsigned char *)0x25)可以看作是一个普通变....
的头像 玩转单片机 发表于 08-04 16:44 211次 阅读
采用标准C的强制转换和指针的概念来实现访问MCU的寄存器

超声波测距LCD12864显示的C语言程序和工程文件免费下载

本文档的主要内容详细介绍的是超声波测距LCD12864显示的C语言程序和工程文件免费下载。
发表于 07-30 17:16 37次 阅读
超声波测距LCD12864显示的C语言程序和工程文件免费下载

超声波测距LC1602显示的C语言程序和工程文件

  本文档的主要内容详细介绍的是超声波测距LC1602显示的C语言程序和工程文件。
发表于 07-30 17:16 31次 阅读
超声波测距LC1602显示的C语言程序和工程文件

C语言的拓展归纳总结详细说明

本文档的主要内容详细介绍的是C语言的拓展归纳总结详细说明。
发表于 07-29 08:00 54次 阅读
C语言的拓展归纳总结详细说明

C语言教程之struct结构体的详细资料说明

将多个数据类型组合成一个实体,定义了了结构体,就定义了一个新的数据类型。用简单的类型封装成复杂的类型....
发表于 07-29 08:00 33次 阅读
C语言教程之struct结构体的详细资料说明

C语言动态链表的详细资料说明

  动态链表:在程序执行的过程中,动态地开辟一块内存空间,可以是不在一起的内存空间,通过链表联系起来....
发表于 07-29 08:00 36次 阅读
C语言动态链表的详细资料说明

C语言空间开辟的教程资料说明

本文档的主要内容详细介绍的是C语言空间开辟的教程资料说明1.系统自动分配内存空间的变量 不使用关键....
发表于 07-29 08:00 39次 阅读
C语言空间开辟的教程资料说明

C语言的声明和定义与引用说明

本文档的主要内容详细介绍的是C语言的声明和定义与引用说明
发表于 07-29 08:00 38次 阅读
C语言的声明和定义与引用说明

C语言的扩展资料说明

编译器不能定义编程中所有的东西,可以提供规则让程序员定义自己的东西
发表于 07-29 08:00 34次 阅读
C语言的扩展资料说明

C语言数组的学习教程说明

同样我们在程序中也需要容器,只不过该容器有点特殊,它在程序中是一块连续的,大小固定并且里面的数据类型....
发表于 07-29 08:00 26次 阅读
C语言数组的学习教程说明

了解一些C程序的基本规则

程序由一个或多个函数组成,其中一定有一个名为 main()的函数。函数的描述由函数头和函数体组成。函....
的头像 lhl545545 发表于 07-27 11:01 291次 阅读
了解一些C程序的基本规则

如何实现电子密码锁的设计

根据设定好的密码,采用二个按键实现密码的输入功能,当密码输入正确之后,锁就打开,如果输入的三次的密码....
的头像 Wildesbeast 发表于 07-26 10:35 442次 阅读
如何实现电子密码锁的设计

设计一个多路多功能的智力竞赛抢答器资料说明

我们为适应高校等多代表队单位活动的需要一个多路抢答器设计 ,讲述了电路各部分的设计及功能 ,并说明了....
的头像 Wildesbeast 发表于 07-26 10:34 401次 阅读
设计一个多路多功能的智力竞赛抢答器资料说明

请问自学汇编语言汇编语言需要什么?

    学过C语言和Java 学过51单片机, 现在想学习ARM 汇编语言 嵌入式系统Linux 这几个 有什么相关的视频或...
发表于 07-26 08:03 0次 阅读
请问自学汇编语言汇编语言需要什么?

自学C语言的好方法有哪些?

    计算机小白一枚,自学C语言中。但目前还很不熟练。碰到书上稍微比较难的例子,不看答案自己去思考的话,脑子就一...
发表于 07-26 08:03 0次 阅读
自学C语言的好方法有哪些?

DSP的设计与实现详细说明

本课程主要结合TMS320C54X、TMS320LF240X两个系列DSP介绍DSP的相关结构原理、....
发表于 07-24 17:31 55次 阅读
DSP的设计与实现详细说明

电力电子应用技术的MATLAB仿真破PDF电子书免费下载

本书概述了MATLAB软件及其图形仿真界面Simulink的基础应用知识,详细介绍了用于电力电子仿真....
发表于 07-23 08:00 81次 阅读
电力电子应用技术的MATLAB仿真破PDF电子书免费下载

实现光敏和热敏电阻实验的C语言程序和电路图与数据手册等资料

本文档的主要内容详细介绍的是实现光敏和热敏电阻实验的C语言程序和电路图与数据手册等资料。
发表于 07-21 08:00 46次 阅读
实现光敏和热敏电阻实验的C语言程序和电路图与数据手册等资料

使用C#实现Interlocked线程同步的程序免费下载

本文档的主要内容详细介绍的是使用C#实现Interlocked线程同步的程序免费下载。
发表于 07-21 08:00 19次 阅读
使用C#实现Interlocked线程同步的程序免费下载

单片机用什么编程

单片机编程用汇编和C语言的比较多。其中C语言属于比较常用比较基础的语言,很多人大学期间都学,也相对比....
发表于 07-20 09:45 107次 阅读
单片机用什么编程

使用C#实现Gettype和类的typeof反射获取实例的详细说明

本文档的主要内容详细介绍的是使用C#实现Gettype和类的typeof反射获取实例的详细说明 ....
发表于 07-20 08:00 36次 阅读
使用C#实现Gettype和类的typeof反射获取实例的详细说明

C语言的几种位操作运算程序详细说明

在汇编语言中有直接对位进行操作的指令,如置位、复位、位取反、测试某一位等,这对于硬件操作十分方便,在....
的头像 Wildesbeast 发表于 07-18 12:00 518次 阅读
C语言的几种位操作运算程序详细说明

单片机的95个问题详细解答

关于单片机的N多问题1. C语言和汇编语言在开发单片机时各有哪些优缺点?答:汇编语言是一种用文字助记....
的头像 Wildesbeast 发表于 07-18 10:10 911次 阅读
单片机的95个问题详细解答

C语言是如何控制硬件的?

    提到C语言就会想到单片机编程,像java python 这些就是纯软件编程语言,不会和硬件打交道。为什么C可以和硬件打...
发表于 07-18 08:06 1次 阅读
C语言是如何控制硬件的?

C语言版cla的程序中可以使用局部变量吗?

interrupt void Cla1Task1(void) [    int i;    for (i=0; i...
发表于 07-17 14:53 0次 阅读
C语言版cla的程序中可以使用局部变量吗?

一款同步Redis数据的异地机房同步数据程序和工程文件

Redis即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志....
发表于 07-17 08:00 45次 阅读
一款同步Redis数据的异地机房同步数据程序和工程文件

51单片机汇编语言程序设计的学习课件资料概述

MCS-51单片机的编程语言可以是汇编语言也可以是高级语言(如C语言),高级语言编程快捷,但程序长,....
发表于 07-16 15:50 49次 阅读
51单片机汇编语言程序设计的学习课件资料概述

单片机汇编语言和C语言的特点

单片机是一种可编程器件,单片机的出现使硬件设计变得更为简单,产品的功能也更强大,而程序就是单片机的灵....
发表于 07-15 17:29 76次 阅读
单片机汇编语言和C语言的特点

C语言的编程规范详细概述

今天人们越来越明白软件设计更多地是一种工程而不是一种个人艺术由于大型产品的开发通常由很多的人协同作战....
发表于 07-15 17:29 50次 阅读
C语言的编程规范详细概述

如何快速有效的学习单片机

学习单片机需要具备一定的电路基础、数字电路、模拟电路、信号系统、C语言编程等相关的基础知识。单片机的....
发表于 07-15 17:26 109次 阅读
如何快速有效的学习单片机

C语言温度控制系统的源代码

C语言温度控制系统的源代码
发表于 07-15 08:00 40次 阅读
C语言温度控制系统的源代码

如何给C语言增加几个位操作函数

在汇编语言中有直接对位进行操作的指令,如置位、复位、位取反、测试某一位等,这对于硬件操作十分方便,在C语言中尽管也提供了...
发表于 07-14 18:15 136次 阅读
如何给C语言增加几个位操作函数

C语言的几种位操作运算

在汇编语言中有直接对位进行操作的指令,如置位、复位、位取反、测试某一位等,这对于硬件操作十分方便,在C语言中尽管也提供了...
发表于 07-14 18:14 127次 阅读
C语言的几种位操作运算

嵌入式软件开发ARM中断实验的报告资料说明

一、实验目的(1)、学会使用 Embest IDE 编写简单的 C语言程序并进行调试;(2)、了解不....
发表于 07-14 16:42 48次 阅读
嵌入式软件开发ARM中断实验的报告资料说明

单片机C语言延时程序的注意事项

标准的C语言中没有空语句。但在单片机的C语言编程中,经常需要用几个空指令产生短延时的效果。这在汇编语....
发表于 07-14 11:13 78次 阅读
单片机C语言延时程序的注意事项

单片机C语言代码编写技巧

下面发一些我在网上看到的单片机C语言代码编写技巧和自己的一些经验来和大家分享;
发表于 07-14 11:02 112次 阅读
单片机C语言代码编写技巧

C语言的进阶学习课件资料合集

本文档的主要内容详细介绍的是C语言的进阶学习课件资料合集包括了:第1节-数据的存储,第2节-指针的进....
发表于 07-14 08:00 58次 阅读
C语言的进阶学习课件资料合集

Keil uVision4的使用详解教程概述

单片机开发中除必要的硬件外,同样离不开软件,我们写的汇编语言源程序要变为 CPU 可以执行的机器码有....
发表于 07-13 08:00 65次 阅读
Keil uVision4的使用详解教程概述

分享 郭天祥 十天学会C语言和51单片机视频教程+代码

郭天祥 郭天祥 十天学会C语言和51单片机 详细教程源码 回复可得 链接:https://pan.baidu.com/s/1uWWpG-o_EVscRZ3ys...
发表于 07-12 21:31 298次 阅读
分享 郭天祥 十天学会C语言和51单片机视频教程+代码

如何把C++的源程序改写成C语言

由于C++解释器比C语言解释器占用的存储空间要大500k左右。为了节省有限的存储空间,降低成本,同时也为了提高效率,将用C+...
发表于 07-11 09:34 1912次 阅读
如何把C++的源程序改写成C语言

关于ARM的三种中断调试方法介绍

在整个流程中,用户首先需要建立工程并对工程做初步的配置,包括配置处理器和配置调试设备。编辑工程文件,....
发表于 07-10 16:01 73次 阅读
关于ARM的三种中断调试方法介绍

使用C语言写出的九九乘法表

本文档的主要内容详细介绍的是使用C语言写出的九九乘法表资料合集免费下载。
发表于 07-10 08:00 47次 阅读
使用C语言写出的九九乘法表

基于C语言的用户管理系统源代码

设系统用户信息(用户代码,用户名,密码,系统身份)存放在一个名为user.txt的文件中,请编写程序....
发表于 07-09 09:27 43次 阅读
基于C语言的用户管理系统源代码

如何把C++的源程序改写成C语言

由于C++解释器比C语言解释器占用的存储空间要大500k左右。为了节省有限的存储空间,降低成本,同时也为了提高效率,将用C+...
发表于 07-08 20:51 404次 阅读
如何把C++的源程序改写成C语言

单片机C语言程序设计的详细资料

C语言目前己成为电子工程师进行单片机系统开发时的常用编程语言。用C语言来编写目标系统软件,会大大缩短....
发表于 07-07 14:48 88次 阅读
单片机C语言程序设计的详细资料

浅谈2020年六大主流编程语言

目前,计算机语言的总数总计达9000种。但是,其中只有50种编程语言是较为流行的。今天,小编就给大家....
的头像 如意 发表于 07-03 11:36 702次 阅读
浅谈2020年六大主流编程语言

HW资料华-为硬件(硬件开发C语言PCB设计天线通信)

链接:https://pan.baidu.com/s/1HFxkb4O1jAZvCEccWVcd4w 提取码:stns 回复可见提取码,资源需求可加q1051514663 ...
发表于 07-01 18:20 97次 阅读
HW资料华-为硬件(硬件开发C语言PCB设计天线通信)