C语言结构体完全笔记


欢迎加入技术交流QQ群(2000人):电力电子技术与新能源 920776074


高可靠新能源行业顶尖自媒体


在这里有电力电子、新能源干货、行业发展趋势分析、最新产品介绍、众多技术达人与您分享经验,欢迎关注微信公众号:电力电子技术与新能源(Micro_Grid),论坛:www.21micro-grid.com,建立的初衷就是为了技术交流,作为一个与产品打交道的技术人员,市场产品信息和行业技术动态也是必不可少的,希望大家不忘初心,怀有一颗敬畏之心,做出更好的产品!

电力电子技术与新能源论坛

www.21micro-grid.com


小编推荐值得一看的书单电力电子技术与新能源小店


    本次分享一篇关于结构体的入门、提高的笔记,文章比较长,前面部分是结构体基础,已经掌握的童鞋可以跳过,直接看看后半部分的提高实例。
    有的时候,我们所遇到的数据结构,不仅仅是一群数字或者是字符串那么简单。比如我们每一个人的学籍信息,学号是一个长整数,名字却是字符;甚至有更复杂的情况,这种问题在现实生活中并不少见。我们之前学过一种叫数组的数据结构,它可以允许我们把很多同类型的数据集中在一起处理。相对于之前,这已经是一次极大的进步。但是,新的问题,往往又会出现,这个时候,我们就得上更高端的装备——结构体。
    相比于数组,结构体有以下的更强大的优势:


  • 批量存储数据
  • 存储不同类型的数据
  • 支持嵌套

结构体的声明与定义

声明

    结构体的声明使用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("\n数据录入完毕\n\n");
printf("学号:%d\n姓名:%s\n入学年份:%d\n学制:%d\n毕业时间:%d\n", \ 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,「字符型」成员初始化为‘\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\n", sizeof(a)); return 0;}



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


//COnsequence 02 V1size 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\n", sizeof(a)); return 0;}


    结果:


//Consequence 02 V2size 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("\n数据录入完毕\n\n");
printf("学号:%d\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n",\ 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;



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


  1. (*结构体指针).成员名
  2. 结构体指针->成员名


    第一个方法由于点号运算符比指针的取值运算符优先级更高,因此需要加一个小括号来确定优先级,让指针先解引用变成结构体变量,在使用点号的方法去访问。
    相比之下,第二种方法就直观许多。
    这两种方法在实现上是完全等价的,但是点号只能用于结构体变量,而箭头只能够用于指针。


    第一种方法:


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


    第二种方法:


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


传递结构体信息

传递结构体变量


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


    运行结果如下:


//Consequence 04t2.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\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n", \ 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("请录入第一个同学的信息...\n"); i1 = getInput(i1); putchar('\n'); printf("请录入第二个学生的信息...\n"); i2 = getInput(i2);
printf("\n录入完毕,现在开始打印...\n\n"); printf("打印第一个学生的信息...\n"); printInfo(i1); putchar('\n'); printf("打印第二个学生的信息...\n"); 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\n姓名:%s\n入学时间:%d/%d/%d\n学制:%d\n毕业时间:%d\n", \ 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("请录入第一个同学的信息...\n"); getInput(&i1); putchar('\n'); printf("请录入第二个学生的信息...\n"); getInput(&i2);
printf("\n录入完毕,现在开始打印...\n\n"); printf("打印第一个学生的信息...\n"); printInfo(&i1); putchar('\n'); printf("打印第二个学生的信息...\n"); 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("内存分配失败!\n");        exit(1);    }        printf("请录入第一个同学的信息...\n");    getInput(i1);    putchar('\n');    printf("请录入第二个学生的信息...\n");    getInput(i2);
printf("\n录入完毕,现在开始打印...\n\n"); printf("打印第一个学生的信息...\n"); printInfo(i1); putchar('\n'); printf("打印第二个学生的信息...\n"); 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\n", book->title); printf("作者:%s\n", book->author); printf("售价:%.2f\n", book->price); printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day); printf("出版社:%s\n", 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('\n'); } }}
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('\n'); } else { printf("数据库已满!\n"); break; } } else { break; } }
printf("\n数据录入完毕,开始打印验证...\n\n"); 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("内存分配失败!\n");        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:\n", count);        printf("姓名:%s\n", info->name);        printf("学号:%d\n", 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("内存分配失败!\n");        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("内存分配失败!\n");        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("\n请输入学生学号:"); scanf("%d", input); info = searchInfo(students, input); if (info == NULL) { printf("抱歉,未找到相关结果!\n"); } else { do { printf("相关结果如下:\n"); 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("内存分配失败!\n"); 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('\n');}
int main(void){ struct Node* head = NULL; int input;
printf("开始插入整数...\n"); while (1) { printf("请输入一个整数,输入-1表示结束:"); scanf("%d", &input); if (input == -1) { break; } insNode(&head, input); printNode(head); }
return 0;}



    运行结果如下:


//Consequence 03开始插入整数...请输入一个整数,输入-1表示结束:44请输入一个整数,输入-1表示结束:54 5请输入一个整数,输入-1表示结束:33 4 5请输入一个整数,输入-1表示结束:63 4 5 6请输入一个整数,输入-1表示结束:22 3 4 5 6请输入一个整数,输入-1表示结束:52 3 4 5 5 6请输入一个整数,输入-1表示结束:11 2 3 4 5 5 6请输入一个整数,输入-1表示结束:71 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("未找到匹配项!\n");        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("内存分配失败!\n"); 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\n", person->name); printf("电话:%s\n", 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("找不到联系人!\n"); } 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("找不到该联系人!\n"); } 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("| 欢迎使用通讯录管理程序 |\n"); printf("|--- 1:插入新的联系人 ---|\n"); printf("|--- 2:查找现有联系人 ---|\n"); printf("|--- 3:更改现有联系人 ---|\n"); printf("|--- 4:删除现有联系人 ---|\n"); printf("|--- 5:显示当前通讯录 ---|\n"); printf("|--- 6:退出通讯录程序 ---|\n");
while (1) { printf("\n请输入指令代码:"); scanf("%d", &code); switch (code) { case 1:addPerson(&contacts); break; case 2:person = findPerson(contacts); if (person == NULL) { printf("找不到该联系人!\n"); } 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("内存分配失败!\n"); 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\n", person->name); printf("电话:%s\n", 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("找不到联系人!\n"); } 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("找不到该联系人!\n"); } 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("| 欢迎使用通讯录管理程序 |\n"); printf("|--- 1:插入新的联系人 ---|\n"); printf("|--- 2:查找现有联系人 ---|\n"); printf("|--- 3:更改现有联系人 ---|\n"); printf("|--- 4:删除现有联系人 ---|\n"); printf("|--- 5:显示当前通讯录 ---|\n"); printf("|--- 6:退出通讯录程序 ---|\n");
while (1) { printf("\n请输入指令代码:"); scanf("%d", &code); switch (code) { case 1:addPerson(&contacts); break; case 2:person = findPerson(contacts); if (person == NULL) { printf("找不到该联系人!\n"); } 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\n", test.i); printf("test.pi: %.2f\n", test.pi); printf("test.str: %s\n", test.str);
return 0;}



    执行结果如下:


//Consequence 05test.i: 1751344468test.pi: 3946574856045802736197446431383475413237648487838717723111623714247921409395495328582015991082102150186282825269379326297769425957893182570875995348588904500564659454087397032067072.00test.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 07weekend:1



位域


    C语言除了开发桌面应用等,还有一个很重要的领域,那就是「单片机」开发。单片机上的硬件资源十分有限,容不得我们去肆意挥洒。单片机使一种集成电路芯片,使采用超大规模集成电路技术把具有数据处理能力的CPU、RAM、ROM、I/O、中断系统、定时器/计数器等功能(有的还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工控领域使用广泛。
    对于这样的设备,通常内存只有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\n", test.a, test.b, test.c); printf("size of test = %d\n", sizeof(test));
return 0;}



    运行结果如下:


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



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


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



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

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


unsigned int a : 100;



    那么就会报错:


错误  C2034  “main::test::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万呢?


文章首尾冠名广告正式招商,功率器件:IGBT,MOS,SiC,GaN,磁性器件,电源芯片,DSP,MCU,新能源厂家都可合作,有意者加微信号1768359031详谈。

说明:本文来源网络;文中观点仅供分享交流,不代表本公众号立场,转载请注明出处,如涉及版权等问题,请您告知,我们将及时处理。

电力电子技术与新能源通讯录:

Please clik the advertisement and exit

重点

如何下载《电力电子技术与新能源板块内高清PDF电子书


点击文章底部阅读原文,访问电力电子技术与新能源论坛(www.21micro-grid.com)下载!


或者转发所要文章到朋友圈不分组不屏蔽,然后截图发给小编(微信1413043922),小编审核后将文章发你!


推荐阅读:点击标题阅读

LLC_Calculator__Vector_Method_as_an_Application_of_the_Design

自己总结的电源板Layout的一些注意点

华为电磁兼容性结构设计规范V2.0

Communication-less Coordinative Control of Paralleled Inverters

Soft Switching for SiC MOSFET Three-phase Power Conversion


看完有收获?请分享给更多人


公告:

电力电子技术与新能源微信群,欢迎加小编微信号:(QQ号)1413043922,请注明研究方向或从事行业(比如光伏逆变器硬件),小编对电力电子技术与新能源及微电网的市场发展很看好,对其关键技术很感兴趣,如有技术问题,欢迎加小编微信,共同讨论。

    在这里有电力电子技术:光伏并网逆变器(PV建模,MPPT,并网控制,LCL滤波,孤岛效应),光伏离网,光伏储能,风电变流器(双馈、直驱),双向变流器PCS,新能源汽车,充电桩,车载电源,数字电源,双向DCDC(LLC,移相全桥,DAB),储能(锂电池、超级电容),低电压穿越(LVRT),高电压穿越,虚拟同步发电机,多智能体,电解水,燃料电池,能量管理系统(直流微网、交流微网)以及APF,SVG ,DVR,UPQC等谐波治理和无功补偿装置等。

PSCAD/MATLABsimulink/Saber/PSPICE/PSIM——软件仿真+DSP+(TI)TMS320F2812,F28335,F28377,(Microchip)dsPIC30F3011,FPGA,ARM,STM32F334——硬件实物。
欢迎技术人员加入,多多交流,共同进步!


更多精彩点下方阅读原文

      点亮在看,小编工资涨1毛!

电力电子技术与新能源 电力电子技术,交直流微电网,光伏并网逆变器,储能逆变器,风电变流器(双馈,直驱),双向变流器PCS,新能源汽车,充电桩,车载电源,数字电源,双向DCDC,锂电池,超级电容,燃料电池,能量管理系统以及APF,SVG ,UPQC等
评论
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 70浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 73浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-10 16:13 105浏览
  • 概述 通过前面的研究学习,已经可以在CycloneVGX器件中成功实现完整的TDC(或者说完整的TDL,即延时线),测试结果也比较满足,解决了超大BIN尺寸以及大量0尺寸BIN的问题,但是还是存在一些之前系列器件还未遇到的问题,这些问题将在本文中进行详细描述介绍。 在五代Cyclone器件内部系统时钟受限的情况下,意味着大量逻辑资源将被浪费在于实现较大长度的TDL上面。是否可以找到方法可以对此前TDL的长度进行优化呢?本文还将探讨这个问题。TDC前段BIN颗粒堵塞问题分析 将延时链在逻辑中实现后
    coyoo 2024-12-10 13:28 101浏览
  • 智能汽车可替换LED前照灯控制运行的原理涉及多个方面,包括自适应前照灯系统(AFS)的工作原理、传感器的应用、步进电机的控制以及模糊控制策略等。当下时代的智能汽车灯光控制系统通过车载网关控制单元集中控制,表现特殊点的有特斯拉,仅通过前车身控制器,整个系统就包括了灯光旋转开关、车灯变光开关、左LED前照灯总成、右LED前照灯总成、转向柱电子控制单元、CAN数据总线接口、组合仪表控制单元、车载网关控制单元等器件。变光开关、转向开关和辅助操作系统一般连为一体,开关之间通过内部线束和转向柱装置连接为多,
    lauguo2013 2024-12-10 15:53 78浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 66浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 84浏览
  • 【萤火工场CEM5826-M11测评】OLED显示雷达数据本文结合之前关于串口打印雷达监测数据的研究,进一步扩展至 OLED 屏幕显示。该项目整体分为两部分: 一、框架显示; 二、数据采集与填充显示。为了减小 MCU 负担,采用 局部刷新 的方案。1. 显示框架所需库函数 Wire.h 、Adafruit_GFX.h 、Adafruit_SSD1306.h . 代码#include #include #include #include "logo_128x64.h"#include "logo_
    无垠的广袤 2024-12-10 14:03 69浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 45浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 44浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 136浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦