深入理解C语言结构体(数据结构基础)

简介: 一:结构体定义与初始化引用1:结构体是什么?它的特点?<1>相对于数组存储结构的区别?数组是一种存储结构,一种可以存放相同类型的存储结构。比如int类型的存储结构就只能存放Int类型的数据,但是你若是想要描述清除一个学生的身份信息,一个数组绝对是不行的,比如名字,学号等这些,必须要使用多个数组来说明学生的信息。我们还需要构造关联的索引形成一一的对应,如果大型项目这样做,那么就会无比臃肿了。

一:结构体定义与初始化引用


1:结构体是什么?它的特点?


<1>相对于数组存储结构的区别?

数组是一种存储结构,一种可以存放相同类型的存储结构。比如int类型的存储结构就只能存放Int类型的数据,但是你若是想要描述清除一个学生的身份信息,一个数组绝对是不行的,比如名字,学号等这些,必须要使用多个数组来说明学生的信息。我们还需要构造关联的索引形成一一的对应,如果大型项目这样做,那么就会无比臃肿了。


结构体是这样一种数据结构,一种可以存放多种数据类型的数据结构。也就是既可以有int类型,也可以有char,float等,以此类推。是一种非常方便的数据结构,对于描述一个事物的特征具有非常方便的意义。


<2>结构体定义(常规)

我们定义结构体的方式有多种

1:第一种比较规范化的定义如下


//定义结构体,包含完整的结构体名和结构体变量
struct name//结构体名/标签
{
    /* data */
    int a;//结构体成员
    float b;
    char c;
    double d;
}var;//结构体变量


2:无结构体名


//定义结构体
struct 
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var;//结构体变量


3:无结构变量


//定义结构体
struct name
{
    /* data */
    int a;
    float b;
    char c;
    double d;
};


当然你就算定义一个空的结构体也是可以的,我这边只得空就是没有结构体成员,没有成员就是空的啦!

那么结构体在定义的时候可以简化到什么程度呢?

可以简化到只有结构体变量或者结构体名。但是如果连结构体变量或者结构体名都没有的化就会报错,没有办法识别。也当然没有什么意义。


//定义结构体
struct name//只含有结构体名
{
};
1
2
3
4
5
6
7
//定义结构体
struct 
{
}var;//只含有结构体变量


4:有typedef 关键字情况下对结构体初始化


<3>结构体初始化(常规)

我们下面对结构体进行初始化,初始化的结构体才真正具有了一定的意义。

我们来举例几种初始化结构体的方法

1:在定义变量的时候初始化


/

/定义结构体
struct name
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var = {1,2.0,'c',3.00};//对结构体变量进行了初始化



顺便说明一下结构体初始化对结构体变量的意义的说明

结构体名和结构体变量是不一样的,结构体名可以作为结构体的一种表标识,但是它不具有a,b,c,d这四个成员的属性,而我们的一个结构变量就具有了a,b,c,d四个成员属性。所以我们初始化需要对var赋值,而不是name。

2:在main函数中通过 struct name + var 定义变量并进行初始化


#include<stdio.h>
#include<windows.h>
//定义结构体
struct name
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var = {1,2.0,'c',3.00};//对结构体变量进行了初始化
int main()
{  
    struct name var1 = {1,2.0,'c',4.0};//在main函数中声明结构体变量并进行初始化
    system("pause");
}


注意:如果在main函数中,我们不能直接用var={};来进行赋值。


<3>typedef关键字对结构体的改变

先说明一下typedef关键字的作用。

我们常规的定义结构体的方式便是struct 这样的形式,但是我们也会常常见到typedef struct的形式。

那么typedef关键字的作用是什么呢?


明白了说就是给紧挨着后i面的类型起一个别名。举例如下:


// An highlighted block
//定义结构体
typedef struct name
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var_dif_name;


这里定义的struct name是我们的结构体,然后下面是var_dif_name,此时的var_dif_name相当于struct name,现在的var_dif_name并不是结构体变量,而是struct name这个整体类型的别称。所以你此时这样定义了不能再对var_dif_name进行赋值。如下是错误的



我们可以更加简化一些,这样来做


//定义结构体
typedef struct 
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var_dif_name ;
int main()
{  
    var_dif_name var = {1,2,'c',8};
    system("pause");
}


可以看到,若如上所言,那么这个意思就是var_dif_name就相当于struct了吗?如果这样去理解,顺着逻辑理解下去的化,那么var_dif_name就相当于struct var了,如果这样的话,这样做不是错误的话吗?其实这里的名字是隐含掉了,tag成为一种匿名的方式,我们这里省略了struc后面的名字tag。所以我们这样定义也是可以的。编译器会认为合法的,


举例了结构体使用typedef,那么我们为什么要使用它,使用它的意义何在?你想啊,如果没有typedef,那么如果我们再主函数里面声明多个结构体变量就需要struct name +var,当有了typedef,我们就可以用一个替代的var_dif_name + var来代替struct name +var。


2:引用结构体变量的成员属性

<1>在主函数中使用结构体变量

那么我们如何在主函数中使用结构体变量呢?

直接printf()可以吗?当然不可以。你想啊,我们输出一个结构体变量,它可能具有不同类型的成员属性,那么你怎么可以用一种形式输出呢?当然不可以。所以我们需要这样做。


#include<stdio.h>
#include<windows.h>
//定义结构体
typedef struct 
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var_dif_name ;
int main()
{  
    var_dif_name var = {1,2,'c',8};
    printf("%d-%f,%c,%d",var.a,var.b,var.c,var.d);
    system("pause");
}


引用结构体的成员属性,需要我们通过结构体变量来进行引用。结构体变量.结构体成员。


<2>通过指针进行引用

如何通过指针对结构体进行操作也是一件比较巧妙地事情。指针与结构体地结合是过渡数据结构的重要一步。

这边需要考虑的是,结构体变量内含有多个属性。int,char,类型这些,我们可以定义一个指针指向一个结构体变量吗?当然不可以,为了使类型匹配,所以需要也定义一个结构体指针来进行操作。


#include<stdio.h>
#include<windows.h>
//定义结构体
typedef struct 
{
    /* data */
    int a;
    float b;
    char c;
    double d;
}var_dif_name ;
int main()
{  
    var_dif_name var = {1,2,'c',8.00};
    var_dif_name * var_v = NULL;
    var_v= &var;
    printf("%d",var_v->a);
    //printf("%d",(*var_v).a);
    system("pause");
}


释疑:通过定义的结构体指针来引用结构体成员属性,先让我们的指针指向变量地址,然后一种是通过->符号取成员属性的值,一种是进行一层进行解引用,然后取到结构体变量层次,然后进行.来进行取属性,也就是说这两种方法是等效的。但是要注意.的运算级别高于号,所以为了符合逻辑上的需要我们需要加上()。


二:结构体嵌套


1:结构体嵌套使用

结构体嵌套的话,也是一样的道理,如果你的结构体嵌套了另一个结构体,如果你要取到成员属性的值,那么你需要进行两层解引用。来举例。


#include<stdio.h>
#include<windows.h>
struct Date
{
    int year;
    int month;
    int day;
};
struct Book
{
    char title[128];
    char author[40];
    float price;
    struct Date date;//结构体的嵌套
    char  publisher[40];
} book = {
            "<<带你学c带你飞>>",
            "小甲鱼",
            42,
            {2017,11,11},
            "清华大学出版社"
         };
int main()
{
    printf("title::%s\n",book.title);
    printf("author:%s\n",book.author);
    printf("food: %.2f\n",book.price);
    printf("date:%d-%d-%d\n",book.date.year,book.date.month,book.date.day);
    printf("publisher:%s\n",book.publisher);
    system("pause");
}


三:结构体变量做参数传递


1:在函数中传入结构体变量

结构体作为参数传递的话,我们只要在函数内部将形式参数的类型定义为结构体类型。具体代码示例如下。


#include<stdio.h>
#include<windows.h>
struct Date
{
    int year;
    int month;
    int day;
};
struct Book
{
    char title[128];
    char author[40];
    float price;
    struct Date date;
    char publisher[40];
};
struct Book getInput(struct Book book)
{
    printf("Please enter the title::\n");
    scanf("%s",book.title);
    printf("Please enter the author:\n");
    scanf("%s",book.author);
    printf("please enter the price\n");
    scanf("%f",&book.price);
    printf("please enter the date\n");
    scanf("%d-%d-%d",&book.date.year,&book.date.month,&book.date.day);
    printf("please enter the publisher:\n");
    scanf("%s",book.publisher);
}
void printBook(struct Book book)
{
    printf("title:%s\n",book.title);
    printf("author:%s\n",book.author);
    printf("price:%.2f\n",book.price);
    printf("date:%d-%d-%d\n",book.date.year,book.date.month,book.date.day);
    printf("publisher:",book.publisher);
}
int main(void)
{
    struct Book b1,b2;
    printf("please enter the message of the first book:\n");
    b1 = getInput(b1);
    putchar('\n');
    printf("please enter the message of the second book:\n");
    b2 = getInput(b2);
    printf("\n\nover!");
    printf("first book:\n");
    printBook(b1);
    putchar('\n');
    printf("second book:\n");
    printBook(b2);
    system("pause");
}


说明,如果你的结构体变量传入到函数不是传入的地址,那么如果想要成功的更新结构体包括赋值这些,那么你就需要返回一个接受的变量,否则结构体无法更新。


2:在函数中传入结构体变量的地址

当我们给函数中传入结构体变量的地址的时候,那么在主函数就无需再用变量接收传递改变,当把地址传入去的时候,那么就成为一种实质上的改变。具体看示例代码


#include<stdio.h>
#include<windows.h>
struct Date
{
    int year;
    int month;
    int day;
};
struct Book
{
    char title[128];
    char author[40];
    float price;
    struct Date date;
    char publisher[40];
};
void  *getInput(struct Book *book)
{
    printf("Please enter the title::\n");
    scanf("%s",book->title);
    printf("Please enter the author:\n");
    scanf("%s",book->author);
    printf("please enter the price\n");
    scanf("%f",&book->price);
    printf("please enter the date\n");
    scanf("%d-%d-%d",&book->date.year,&book->date.month,&book->date.day);
    printf("please enter the publisher:\n");
    scanf("%s",book->publisher);
}
void *printBook(struct Book *book)
{
    printf("title:%s\n",book->title);
    printf("author:%s\n",book->author);
    printf("price:%.2f\n",book->price);
    printf("date:%d-%d-%d\n",book->date.year,book->date.month,book->date.day);
    printf("publisher:",book->publisher);
}
int main(void)
{
    struct Book b1,b2;
    printf("please enter the message of the first book:\n");
    getInput(&b1);
    putchar('\n');
    printf("please enter the message of the second book:\n");
    getInput(&b2);
    printf("\n\nover!");
    printf("first book:\n");
    printBook(&b1);
    putchar('\n');
    printf("second book:\n");
    printBook(&b2);
    system("pause");
}


当我们传入地址的时候,那么形式参数就需要为指针类型,指针才可以存放地址。


四:计算结构体变量占用的字节?(结构体成员的内存对齐?)

 struct A

   {

       char a;

       int b;

       char c;

   } a={'x',5,'z'};//结构体存在内存对齐

1

2

3

4

5

6

请问结构体A占用多少字节?是1+4+1=6吗?


非也,答案是12个字节,你看啊!虽然char一个字节,但是int类型占用4个字节,两个char类型会和int类型对齐,int类型是四个字节,那么两个char也会给4个,这样就一共十二个字节。


那么再来看下面的占用多少?编译器不是傻子。


struct B
    {
        char a;
        char c;
        int b;
    }  c={'x','y',6};


什么?你不会认为三个字节吧?hh

来看小甲鱼的图。非常详细了,所以我就拿来用了。



所以这样一共八个字节。

相关文章
|
3天前
|
存储 算法 C语言
通义灵码在考研C语言和数据结构中的应用实践 1-5
通义灵码在考研C语言和数据结构中的应用实践,体验通义灵码的强大思路。《趣学C语言和数据结构100例》精选了五个经典问题及其解决方案,包括求最大公约数和最小公倍数、统计字符类型、求特殊数列和、计算阶乘和双阶乘、以及求斐波那契数列的前20项和。通过这些实例,帮助读者掌握C语言的基本语法和常用算法,提升编程能力。
|
1天前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
5天前
|
编译器 C语言 C++
C语言结构体
C语言结构体
15 5
|
3天前
|
存储 算法 C语言
【趣学C语言和数据结构100例】
《趣学C语言和数据结构100例》精选5个编程问题,涵盖求最大公约数与最小公倍数、字符统计、特殊序列求和及阶乘计算等,通过实例讲解C语言基础与算法思维,适合初学者实践学习。
|
13天前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
17 2
|
13天前
|
C语言
C语言结构体链式结构之有头单链表
文章提供了一个C语言实现的有头单链表的完整代码,包括创建链表、插入、删除和打印等基本操作。
17 1
|
1天前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
6天前
|
编译器 Linux C语言
C语言 之 结构体超详细总结
C语言 之 结构体超详细总结
9 0
|
11天前
|
存储 编译器 Linux
深入C语言:探索结构体的奥秘
深入C语言:探索结构体的奥秘
|
11天前
|
存储 编译器 C语言
c语言回顾-结构体(2)(下)
c语言回顾-结构体(2)(下)
23 0