目前正在学习结构体的相关概念,期间将此博客当做笔记本来用,因此本文可能会出现很多基本知识点,但是也可能会有 只有初学者会关注到的知识点,所以各位伙伴们斟酌观看吧 ̋(๑˃́ꇴ˂̀๑)
PS:本文仅供有些C语言基础,想回顾知识点的伙伴阅读
———————————————————————————————————————————————————
一、结构体的基本概念
我对结构体最开始的影响就是可以存储不同类型的元素
1、结构体的声明
直接举例
struct Book
{
char title[128];
float price;
unsighed int date;
}; //记得要加分号
Book是结构体名称(一般结构体变量用首字母大写的英文单词);注意最后要加分号
2、结构体的定义
可以在函数里定义
struct Book
{
char title[128];
float price;
unsighed int date;
};
int main()
{
struct Book book;
...
}
book 是结构体变量名
也可以声明、定义一起写
struct Book
{
char title[128];
float price;
unsighed int date;
} book; //在此定义
int main()
{
...
}
当然,如果是第二种情况,那么 book 结构体变量就是一个全局变量
3、为结构体成员赋值 与 访问结构体成员
引进了一个新的运算符:点号( . )运算符
来个简单的例子
#include <stdio.h>
struct Book //声明
{
char title[128];
float price;
unsigned int date;
};
int main()
{
struct Book book; //定义
printf("input title:"); //赋值
scanf("%s", book.title);
printf("input price:");
scanf("%f", &book.price);
printf("input date:");
scanf("%ud", &book.date);
//局部变量调用全局变量
printf("%s\n", book.title); //访问
printf("%f\n", book.price);
printf("%u\n", book.date);
return 0;
}
input: input title:《格林童话》
input price: 10.00
input date: 20191105
output:《格林童话》
10.000000
20191105
用点号( . )运算符表示调用结构体成员
另一种为结构体成员赋值(定义)的方法
#include <stdio.h>
struct Book //声明
{
char title[128];
float price;
unsigned int date;
};
int main()
{
struct Book book = //定义
{
"《格林童话》", //记住一定要用逗号
10.00, //记住一定要用逗号
20191105
}; //这里也要加分号哦
printf("%s\n", book.title);
printf("%f\n", book.price);
printf("%u\n", book.date);
return 0;
}
补充:上面全部代码片的结构体声明都是在全局作用域,其实结构体在函数内部声明也是可以的
也可以声明、定义合在一起写,这是比较简单的写法,不过这个结构体变量就属于局部变量了,如下:
#include <stdio.h>
int main()
{
struct Book
{
char title[128];
float price;
unsigned int date;
} book = {"格林童话",10.00,20191105};
printf("%s\n", book.title);
printf("%f\n", book.price);
printf("%u\n", book.date);
return 0;
}
C99 增加了一种特性: 可以不按结构体声明的成员顺序进行赋值(定义),如下:
#include <stdio.h>
struct Book //声明
{
char title[128];
float price;
unsigned int date;
};
int main()
{
struct Book book = //定义
{
.date = 20191105,
.title = "《格林童话》",
.price = 10.00
};
printf("%s\n", book.title);
printf("%f\n", book.price);
printf("%u\n", book.date);
return 0;
}
注意这种特性只有C99标准可以使用(但Dev-C++ 和 VS2019都不支持C99)
4、对齐
#include <stdio.h>
int main()
{
struct You
{
char a;
int b;
char c;
} me = {'x',520,'o'};
printf("size of me = %d\n",sizeof(me));
return 0;
}
output: size of book = 12
说明此结构体变量占 12 个字节的空间
但是不应该是 1(char) + 4(int) + 1(char) = 6 个字节吗?并不是,这是因为编译器对结构体的成员进行了对齐,对齐是为了让CPU可以更快地读取和处理数据
简单的理解对齐就是:将低字节的补充到与高字节一样的内存空间
所以上面代码片的结构体的内存空间就是 4(补充了 3 个字节) + 4 + 4(补充了 3 个字节) = 12 个字节空间
同理,下面的例子
#include <stdio.h>
int main()
{
struct You
{
char a;
char b;
int c;
} me = {'x','o', 520};
printf("size of me = %d\n",sizeof(me));
return 0;
}
output: size of book = 8
上面代码片的结构体的内存空间就是 4(补充了 2 个字节) + 4 = 8 个字节空间
因为前面两个 char 类型合在一起的占了两个字节,故只用对齐两个字节
如果有伙伴想知道如何优化结构体使其占用内存空间最少的,可以参考一下国外的文献(百度应该是自带翻译功能的)
5、结构体嵌套
简单地说,就是结构体里有结构体
struct Book
{
struct A { char a[10]; int b; float c; }pr; //不同结构体里可以用相同的变量名字
struct B { char a[10]; int b; float c; }ps; //但不可以用相同的结构体名称
struct C { char a[10]; int b; float c; }ae; //也不可以用相同的结构体变量
}book;
int main()
{
struct Book book =
{
{"haha",123,10.55}, //注意初始化时都用逗号
{"pipa",456,10.22},
{"ioio",789,10.47},
};
printf("%d\n", book.ps.b); //连用两个点号运算符
output:456
结构体名称和结构体变量不一样,结构体名称指的是结构体类型里面的构造,是不会变的,但结构体变量却可以申请多个,并可以为里面的构造赋不同的值👇
struct Book
{
struct A { char a[10]; int b; float c; }pr,ps,ae; //这种初始化的方法也可以
}book;
int main()
{
struct Book book =
{
{"haha",123,10.55},
{"pipa",456,10.22},
{"ioio",789,10.47},
};
printf("%d\n", book.ps.b);
output:456
6、结构体数组
结构体数组的本质和一个结构体类型有多个结构体变量的概念是一样的(下面代表两种结构体数组)
#include <stdio.h>
struct Book
{
struct A { char a1[10]; int b1; float c1; }pr;
}book[3];
int main()
{
struct Book book[3] =
{
{"haha",123,10.55},
{"pipa",456,10.22},
{"ioio",789,10.47},
};
printf("%d\n", book[1].pr.b1);
return 0;
}
output:456
#include <stdio.h>
struct Book
{
char a1[10]; int b1; float c1;
}book[3];
int main()
{
struct Book book[3] =
{
{"haha",123,10.55},
{"pipa",456,10.22},
{"ioio",789,10.47},
};
printf("%d\n", book[1].b1);
return 0;
}
output:456
7、结构体指针
结构体指针原型
struct Book *p //是以结构体类型作为申请类型
结构体指针取址
p = &book
通过结构体指针访问结构体成员
第一种 : (*结构体指针).成员名
第二种 : 结构体指针 -> 成员名
#include <stdio.h>
struct Book
{
struct A { char a[10]; int b; float c; } pr;
}book = { "haha",123,10.55 };
int main()
{
struct Book* p = &book; //注意不能 struct A* p1 = &pr 因为 struct A不是在全局作用域声明
printf("%d\n", p -> pr.b); //两种方法效果都是一样的
printf("%d\n", (*p).pr.b);
return 0;
}
output: 123
123
8、结构体间的传递
#include <stdio.h>
struct Book
{
char a[10]; int b; float c;
}book = { "haha",123,10.55 },book2;
int main()
{
book2.b = book.b; // book2.a = book.a 是错误的,因为不能传递字符串
// 若要传递字符串只能用 strcpy 函数
printf("%d\n",book2.b);
return 0;
}
output: 123
我们可以将结构体像普通类型一样传递给函数👆,但传递结构体变量耗时又耗内存,所以我们可以传递指针👇
#include <stdio.h>
struct Book
{
char a[10]; int b; float c;
}book = { "haha",123,10.55 }, book2;
int main()
{
struct Book* p;
p = &book;
book2.b = (p -> b);
printf("%d\n", book2.b);
return 0;
}
output:123
👇我们还可以用传递指向结构体变量的地址(非结构体指针)
#include <stdio.h>
void input(struct Book*);
void output(struct Book*);
struct Book
{
char title[10]; int date; float price;
};
int main(void)
{
struct Book book;
input(&book);
putchar('\n');
putchar('\n');
output(&book);
return 0;
}
void input(struct Book* book)
{
printf("请输入书名:");
scanf("%s", book->title);
printf("请输入日期:");
scanf("%d", &book->date); //注意要加 & 号
printf("请输入价格:");
scanf("%f", &book->price); //注意要加 & 号
}
void output(struct Book* book)
{
printf("书名是:%s\n", book->title);
printf("日期是:%d\n", book->date);
printf("价格是:%.2f\n", book->price);
}
input: 请输入书名:《我是夏壳壳》
请输入日期:20191107
请输入价格:16
output: 书名是:《我是夏壳壳》
日期是:20191107
价格是:16.00
9、动态申请结构体
若有不知道malloc()函数的伙伴,可以去看看我的这一篇博客:(萌新笔记)C语言有关函数的概念(2),里面有详细说明
动态申请结构体的方式和动态申请函数的方式类似,如下👇
..... //假设已存在所需结构体并已定义
struct Book *b;
b = (struct Book*)malloc(sizeof(struct Book)) //注意该结构体类型所占用的内存
if (b == NULL)
{
printf("分配内存失败");
exit(1)
}
..... //一系列操作
free(b)
C语言有关结构体的概念 正式结束
请大家期待一下 C语言的复习笔记(2)(会开始讲单链表、内存池、共用体),如果大家在阅读的过程中发现了什么问题,或者想补充些知识点,也请大家在评论区里面留言斧正,互相交流学习,我不胜感激~
最后再一次感谢大家ヾ( ̄▽ ̄)Bye ~ Bye~