从内存堆栈视角,给这段结构体代码做个 "内存 CT"
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}咱们先打个比方:结构体(struct)就像快递盒 —— 可以装不同类型的东西(字符串、数字),但打包成一个整体,方便搬运和管理。而全局结构体变量就像放在公司大厅的公共快递盒,从上班到下班一直都在,谁都能看;栈上的结构体就像你办公桌上的临时快递,下班就清走。
先看懂代码干了啥
这段代码定义了一个Books结构体(相当于设计快递盒的尺寸和内部格子),然后创建了一个全局的book变量(相当于按设计做了一个快递盒,还提前放了东西),最后在main函数里打印盒子里的内容。运行结果会显示书名、作者等信息。今天咱们不看结果,专注看这个 "快递盒" 在内存里怎么放、怎么存东西。
内存区域聚焦:全局区是主角
C 程序内存的三大块里,这段代码主要用到全局区(结构体变量是全局的):
- 全局区(静态存储区):像公司大厅的公共货架,放全局变量,程序启动时创建,结束时销毁,所有人都能访问。
- 栈(Stack):像办公桌,main函数运行时会在这里建栈帧,但这段代码里main没有局部变量,栈帧里主要是函数调用信息。
- 堆(Heap):像仓库,这段代码没用到。
逐行拆解:结构体在内存里的 "打包术"
1. 结构体定义:设计 "快递盒图纸"
c
运行
struct Books {
char title[50]; // 书名(50字节的字符数组)
char author[50]; // 作者(50字节)
char subject[100]; // 主题(100字节)
int book_id; // 书号(4字节的整数)
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
- 结构体类型struct Books:这是 "设计图纸",告诉编译器这个 "快递盒" 里有几个格子,每个格子多大(比如第一个格子 50 字节放书名,第二个 50 字节放作者...)。图纸不占运行时内存,就像你画的快递盒设计图不会占仓库空间。
- 全局结构体变量book:这是按图纸做的 "实体快递盒",被分配到全局区。它的总大小是各成员大小的总和(50+50+100+4=204 字节),成员在内存里连续存放(没有缝隙)。
- 初始化:创建时就往格子里放了东西 ——title填 "C 语言 \0..."(剩余字节自动补 '\0'),author填 "RUNOOB\0...",subject填 "编程语言 \0...",book_id填 123456。
- 全局区里的book布局大概是这样:
- plaintext
┌─────────────────────────────────────────────┐
│ title[0]~title[49] (50字节) │ ← 存"C 语言\0..."
├─────────────────────────────────────────────┤
│ author[0]~author[49] (50字节) │ ← 存"RUNOOB\0..."
├─────────────────────────────────────────────┤
│ subject[0]~subject[99] (100字节) │ ← 存"编程语言\0..."
├─────────────────────────────────────────────┤
│ book_id (4字节) │ ← 存123456
└─────────────────────────────────────────────┘
(总204字节,连续存放)2.main函数启动:栈上的 "查看窗口"
c
运行
int main() { ... }
- 程序运行时,main函数在栈上创建栈帧(相当于开了个窗口,用来查看全局区的book)。
- 栈帧里没有局部变量,只有函数调用需要的信息(比如printf的返回地址)。
3. 打印结构体成员:按 "格子位置" 取东西
c
运行
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n",
book.title, book.author, book.subject, book.book_id);
- 访问book.title时,编译器会计算它在结构体中的位置:从book的起始地址开始,偏移 0 字节(第一个成员),然后读取这个 50 字节的字符数组(直到 '\0')。
- 访问book.author时,偏移 50 字节(跳过title的 50 字节),读取接下来的 50 字节。
- 访问book.subject时,偏移 100 字节(50+50),读取接下来的 100 字节。
- 访问book.book_id时,偏移 200 字节(50+50+100),读取接下来的 4 字节整数。
- 就像快递员按图纸上的格子位置,依次从盒子里拿出书名、作者信息等。
4. 程序结束:全局区 "清空货架"
c
运行
return 0;
- main函数结束,栈帧被释放(查看窗口关闭)。
- 程序退出,全局区的book变量被释放(公共货架清空)。
结构体的内存本质:"连续的复合格子"
- 连续存储:结构体成员在内存里紧紧挨着,没有空隙(除非编译器为了对齐优化加填充字节,但这段代码的成员大小刚好对齐,所以无填充)。就像快递盒里的格子无缝拼接。
- 大小固定:结构体变量的总大小是成员大小之和(可能加填充),定义时就确定,全局区会一次性分配足够空间。
- 访问方式:通过 "结构体起始地址 + 成员偏移量" 访问成员,编译器自动计算偏移量(不用我们手动算)。
关键结论:结构体是 "内存里的复合容器"
- 全局结构体变量book在全局区占 204 字节连续空间,成员按定义顺序依次存放。
- 结构体定义只是 "设计图纸",不占内存;结构体变量才是 "实体容器",占内存。
- 这就像月饼盒:图纸(结构体定义)告诉你盒子分 4 格,装不同口味;实体盒子(结构体变量)才真的占空间,里面放着各种月饼(成员数据)。
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 输出 Book2 信息 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
/* 函数声明 */
void printBook( struct Books book );
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printBook( Book1 );
/* 输出 Book2 信息 */
printBook( Book2 );
return 0;
}
void printBook( struct Books book )
{
printf( "Book title : %s\n", book.title);
printf( "Book author : %s\n", book.author);
printf( "Book subject : %s\n", book.subject);
printf( "Book book_id : %d\n", book.book_id);
}#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
/* 函数声明 */
void printBook( struct Books *book );
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 通过传 Book1 的地址来输出 Book1 信息 */
printBook( &Book1 );
/* 通过传 Book2 的地址来输出 Book2 信息 */
printBook( &Book2 );
return 0;
}
void printBook( struct Books *book )
{
printf( "Book title : %s\n", book->title);
printf( "Book author : %s\n", book->author);
printf( "Book subject : %s\n", book->subject);
printf( "Book book_id : %d\n", book->book_id);
}#include <stdio.h>
struct Person {
char name[20];
int age;
float height;
};
int main() {
struct Person person;
printf("结构体 Person 大小为: %zu 字节\n", sizeof(person));
return 0;
}标题:
- 《C 语言结构体:全局区的 "复合快递盒",成员连续放》
- 《从内存看结构体:像月饼盒一样的复合内存容器》
简介:
通过分析结构体代码的内存分配,揭示结构体变量在全局区连续存储多个不同类型成员的特性,解析结构体定义与变量的区别(定义是图纸,变量是实体),展现结构体作为复合数据类型的内存布局特点。
关键词:
#C 语言结构体 #全局区 #内存布局 #复合数据类型 #成员访问
