一:结构体定义与初始化引用
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
来看小甲鱼的图。非常详细了,所以我就拿来用了。
所以这样一共八个字节。