C语言学习从内存堆栈视角看结构体全局区的复合快递盒成员连续放

从内存堆栈视角,给这段结构体代码做个 "内存 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;
}

标题:

  1. 《C 语言结构体:全局区的 "复合快递盒",成员连续放》
  2. 《从内存看结构体:像月饼盒一样的复合内存容器》

简介:

通过分析结构体代码的内存分配,揭示结构体变量在全局区连续存储多个不同类型成员的特性,解析结构体定义与变量的区别(定义是图纸,变量是实体),展现结构体作为复合数据类型的内存布局特点。

关键词:

#C 语言结构体 #全局区 #内存布局 #复合数据类型 #成员访问

原文链接:,转发请注明来源!