详解C语言中的内存四区模型及结构体对内存的使用

内存四区
1、代码区
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
2、静态区
所有的全局变量以及程序中的静态变量都存储到静态区。
3、栈区
栈stack是一种先进后出的内存结构,所有的自动变量,函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。对于自动变量,什么时候入栈,什么时候出栈,是不需要程序控制的,由C语言编译器。实现栈不会很大,一般都是以K为单位的。
当栈空间以满,但还往栈内存压变量,这个就叫栈。溢出对于一个32位操作系统,最大管理管理4G内存,其中1G是给操作系统自己用的,剩下的3G都是给用户程序,一个用户程序理论上可以使用3G的内存空间。
注意:C语言中函数参数入栈的顺序是从右往左。
4、堆区
堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。
代码示例:

#include <stdio.h> 

int c = 0; // 静态区 

void test(int a, int b) // 形参a,b都在栈区
{
  printf("%d, %d\n", &a, &b);
} 

int *geta() // 函数的返回值是一个指针
{
  int a = 100; // 栈区
  return &a;
} // int a的作用域就是这个{} 

int main()
{
  int *p = geta(); // 这里得到一个临时栈变量的地址,这个地址在函数geta调用完成之后已经无效了
  *p = 100;
  printf("%d\n", *p);
  static int d = 0; // 静态区
  int a = 0; // 栈区
  int b = 0; 

  printf("%d, %d, %d, %d, %d\n", &a, &b, &c, &d, main);
  test(a, b);
  return 0;
} 

/*
输出结果
100
2619740, 2619728, 9404720, 9404724, 9376059
2619512, 2619516
*/

堆使用注意事项:

#include <stdio.h>
#include <stdlib.h> 

int *geta() // 错误,不能将一个栈变量的地址通过函数的返回值返回
{
  int a = 0;
  return &a;
} 

int *geta1() // 可以通过函数的返回值返回一个堆地址,但记得,一定要free
{
  int *p = (int *)malloc(sizeof(int)); // 申请了一个堆空间
  return p;
} 

int *geta2() // 合法的,但是记住这里不能用free
{
  static int a = 0; // 变量在静态区,程序运行过程中一直存在
  return &a;
} 

void getHeap(int *p)
{
  printf("p = %p\n", &p);
  p = (int *)malloc(sizeof(int) * 10);
} // getHeap执行完之后,p就消失了,导致他指向的具体堆空间的地址编号也随之消失了
// 这里发生了内存泄漏 

void getHeap1(int **p)
{
  *p = (int *)malloc(sizeof(int) * 10);
} // 这里的操作就是正确的 

int main()
{
  int *p = NULL;
  printf("p = %p\n", &p);
  getHeap(p); // 实参没有任何改变
  getHeap1(&p); // 得到了堆内存的地址
  printf("p = %d\n", p); 

  p[0] = 1;
  p[1] = 2;
  printf("p[0] = %d, p[1] = %d\n", p[0], p[1]);
  free(p); 

  return 0;
}

结构体内存对齐模式

结构体内存对齐模式各种情况详解

#include <stdio.h> 

struct A
{
  int a; // 此时结构体占用4个字节
  char b; // 此时结构体占用8个字节
  char c; // 还是8个字节
  char d; // 还是8个字节
  char e; // 还是8个字节
  char f; // 现在是12个字节
}; 

struct B
{
  char a; // 1个字节
  char b; // 2个字节
  char c; // 3个字节
}; 

struct c
{
  char name[10]; // 10个字节
  char a; // 11个字节
  // 对于char型数组来说,会把数组每个元素当作一个char类型
}; 

struct d
{
  int name[10]; // 40个字节
  char a; // 44个字节
  char b; // 44个字节
}; 

struct e
{
  char a; // 1个字节
  int b; // 8个字节
  char c; // 12个字节
  // 这种写法内存的消耗相比A就会变大
}; 

struct f
{
  char a; // 1
  short b; // 4注意这里short占用的是剩下三个字节中的后两个
  // 内存对齐总是以2的倍数对齐
  char c; // 所以此时是6
  int d; // 12
  short e; // 16
  char f; // 16
};

结构体变相实现数组赋值

struct name
{
  char array[10];
}; 

int main()
{
  char name1[10] = "name1";
  char name2[20] = "name2";
  name1 = name2; // 这里是出错的,不能在数组之间进行赋值
  struct name a1 = { "hello" };
  struct name a2 = { 0 };
  a2 = a1; // 这里通过结构体可以赋值的特性变相实现了数组的赋值
  return 0;
}

结构体内存泄漏

#include <stdio.h>
#include <stdlib.h> 

union A
{
  char a;
  char *b; // 联合体的指针成员要特别注意
}; 

int main()
{
  A a;
  a.b = (char *)malloc(10); // b指向了一个堆的地址
  // 如果联合体中有指针成员,那么一定要使用完这个指针,并且free指针之后才能使用其他成员
  a.a = 10; // b的值也成了10了
  free(b); // 此时释放b是错误的,因为在上面一行对a进行赋值时,已经将b的值更改了,这里造成了内存泄漏
  return 0;
} 
(0)

相关推荐

  • C语言中结构体struct编写的一些要点解析

    一.关于结构体的声明 1.匿名声明.如: struct { int i,j; }point; 说明: 这段代码的含义是,声明一个无名(anonymous)的结构体,并创建了一个结构体变量point.如果这段声明是放在全局域(在任意函数(比如main函数)外)内,那么point内的变量将被初始化为默认值,换句话说,以这种方式声明结构体变量时就已经为它分配了内存空间. 适用于该结构体只需要产生一个变量!本例中,该匿名结构体将有且仅有point这个结构体变量! 不同的匿名结构体变量,类型是不同的!如

  • 深入分析C语言中结构体指针的定义与引用详解

    指向结构体类型变量的使用首先让我们定义结构体:struct stu{char name[20];long number;float score[4];} ;再定义指向结构体类型变量的指针变量:struct stu *p1, *p2 ;定义指针变量p 1.p 2,分别指向结构体类型变量.引用形式为:指针变量→成员:[例7-2] 对指向结构体类型变量的正确使用.输入一个结构体类型变量的成员,并输出. 复制代码 代码如下: #include <stdlib.h> /*使用m a l l o c (

  • 浅谈C语言中结构体的初始化

    <代码大全>建议在变量定义的时候进行初始化,但是很多人,特别是新人对结构体或者结构体数组定义是一般不会初始化,或者不知道怎么初始化.1.初始化 复制代码 代码如下: typedef struct _TEST_T {        int i;        char c[10];}TEST_T;TEST_T gst  = {1, "12345"};//可以初始化,设置i为1,s为一个字符串.TEST_T gst  = {1};//初始化个数少于实际个数时,只初始化前面的成员

  • 详解C语言的结构体中成员变量偏移问题

    c语言中关于结构体的位置偏移原则简单,但经常忘记,做点笔记以是个记忆的好办法 原则有三个: a.结构体中的所有成员其首地址偏移量必须为器数据类型长度的整数被,其中第一个成员的首地址偏移量为0, 例如,若第二个成员类型为int,则其首地址偏移量必须为4的倍数,否则就要"首部填充":以此类推 b.结构体所占的总字节数即sizeof()函数返回的值必须是最大成员的长度的整数倍,否则要进行"末尾填充": c.若结构体A将结构体B作为其成员,则结构体B存储的首地址的偏移量必须

  • C语言中结构体偏移及结构体成员变量访问方式的问题讨论

    c语言结构体偏移 示例1 我们先来定义一下需求: 已知结构体类型定义如下: struct node_t{ char a; int b; int c; }; 且结构体1Byte对齐 #pragma pack(1) 求: 结构体struct node_t中成员变量c的偏移. 注:这里的偏移量指的是相对于结构体起始位置的偏移量. 看到这个问题的时候,我相信不同的人脑中浮现的解决方法可能会有所差异,下面我们分析以下几种可能的解法: 方法1 如果你对c语言的库函数比较熟悉的话,那么你第一个想到的肯定是of

  • C语言中的结构体的入门学习教程

    C语言中数组允许定义类型的变量,可容纳相同类型的多个数据项,但结构体在C语言编程中,它允许定义不同种类的数据项可供其他用户定义的数据类型. 结构是用来代表一个记录,假设要跟踪图书馆的书籍.可能要跟踪有关每本书以下属性: Title - 标题 Author - 作者 Subject - 科目 Book ID - 编号 定义结构体 定义一个结构体,必须使用结构体的struct语句.该struct语句定义了一个新的数据类型,程序不止一个成员.struct语句的格式是这样的: struct [struc

  • 初步剖析C语言编程中的结构体

    C语言结构体,可谓是C强大功能之一,也是C++语言之所以能衍生的有利条件,事实上,当结构体中成员中有函数指针了后,那么,结构体也即C++中的类了. C语言中,结构体的声明.定义是用到关键字struct,就像联合体用到关键字union.枚举类型用到enum关键字一样,事实上,联合体.枚举类型的用法几乎是参照结构体来的.结构体的声明格式如下: struct tag-name{ { member 1; - member N; }; 因此,定义结构体变量的语句为:struct tag-name vari

  • C语言 结构体数组详解及示例代码

    所谓结构体数组,是指数组中的每个元素都是一个结构体.在实际应用中,结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生.一个车间的职工等. 定义结构体数组和定义结构体变量的方式类似,请看下面的例子: struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩 }class[5]; 表示一个班级有5个学生. 结构体数组在定义的同时也可以初始化,例如: str

  • 详解C语言结构体中的函数指针

    结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的. 函数指针 函数类型 (*指针变量名)(形参列表):第一个括号一定不能少. "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少.  注意指针函数与函数指针表示

  • php读取二进制流(C语言结构体struct数据文件)的深入解析

    尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持.不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转: 复制代码 代码如下: string pack ( string $format [, mixed $args [, mixed $...]] )   //Pack given arguments into binary string according to format.  array unp

随机推荐