自媒体运营小程序开发网站建设,博客社区类网站模板下载,新闻cms静态网站模板,客户跟进系统 免费文章目录 前言一、栈区1、栈区的特点#xff1a;1.1 自动管理1.2 后进先出1.3 有限大小1.4 高速访问1.5 栈区存储方向 2、栈区使用注意事项 二、堆区1、堆区的定义2、堆区的特点3、堆区的内存分配与释放4、注意事项#xff1a; 三、全局/静态存储区1、全局存储区1.1 全局变量… 文章目录 前言一、栈区1、栈区的特点1.1 自动管理1.2 后进先出1.3 有限大小1.4 高速访问1.5 栈区存储方向 2、栈区使用注意事项 二、堆区1、堆区的定义2、堆区的特点3、堆区的内存分配与释放4、注意事项 三、全局/静态存储区1、全局存储区1.1 全局变量1.2 静态全局变量 2、静态存储区2.1 静态局部变量 四、常量存储区1、常量的定义和存储2、常量字符串 前言
在c语言中内存主要分为4个区域 栈区、堆区、全局/静态存储区、常量存储区 一、栈区 在c语言中栈区Stack是内存中的一个重要区域用于存储局部变量、函数参数、返回地址以及函数调用的上下文信息。 1、栈区的特点
栈区的主要特点就是由系统自动管理其内存分配和释放遵循后进先出的原则
1.1 自动管理 栈区的内存分配和释放由编译器在编译时和运行时自动处理程序员无需手动处理。当函数被调用时会在栈上为其局部变量和参数分配空间当函数返回时这些空间会被自动释放掉。 例如写一个加法函数
#includestdio.h
int add(int x, int y)
{int z x y;return z;
}
int main()
{int a 10;int b 20;int ret add(a, b);printf(%d\n,ret);return 0;
}在程序运行时编译器会给add函数开辟一个内存空间此时参数部分xy还有在函数内部创建的变量z都存储在函数的栈区当main函数运行到下一步打印返回值的时候此时add函数已经被调用完毕那么存储在栈区上的函数的变量参数返回值等都会随着函数的使用完毕而被自动释放掉还给操作系统。此时就不存在这些变量也不能对这些变量进行使用因为这部分空间已经不再给与你使用了还给操作系统了使用的时候就没有权限强制使用就会进行错误报错没有使用权限。
1.2 后进先出 栈区的内存分配和释放遵循先进后出的原则。这意味着最后分配的内存最先被释放最先分配的内存最后被释放。这种特性使得栈非常适用于函数调用与返回的。 其实对于栈区后进先出的这个特性看过函数栈帧的读者们都应该很了解 在函数栈帧中函数使用的时候都是进行压栈进行存储的。这里小编还是拿上面的加法函数进行说明。 在程序开始运行的时候都是从main函数进入的main函数是程序的入口既然要使用main函数肯定也需要开辟内存空间所以main函数在函数的最底部进行存放然后进入main函数内存创建变量a和b继续存放在内存空间此时存放的位置是由低地址到高地址进行存放放在main开辟的内存空间的上面这样一个个变量创建放在上面就像是把下面的数据进行一直压着一样的。然后当函数运行结束想要取出来的时候就跟日常去货物仓取货一样总不能从下面开始拿那么一下全部都会倒塌所以从上面开始拿上面开始拿那不就是刚存进去的么也就是后面存进的所以就是后进先出的原理。
1.3 有限大小 栈区的大小通常由操作系统在程序启动时确定并且可以在程序运行时通过系统调用进行调整。由于栈区空间的有限过大的栈区使用如深度递归和大量的局部变量可能会导致栈溢出Stack Overflow从而导致程序崩溃。 像在这里小编写的一个递归函数一样在这里递归函数没有停止条件那么函数就会一直递归下去不停的调用test函数不断的为test函数开辟空间但是栈区是有大小限制的你不断的栈区上面进行内存使用达到上限的时候就会超出使用范围就跟一杯水已经满了你还不停的往里面添加那么水就会溢出来这和栈溢出是一样的道理的。
1.4 高速访问 栈区通常位于内存的较低地址区域并且由于栈的线性增长和自动管理特性使得栈上的数据访问速度非常快。 较低地址区域 栈区通常被分配在内存的较低地址区域。这意味着栈的起始地址较低随着数据的压入push栈顶地址会逐渐向较高的内存地址移动。这种布局有利于快速定位栈顶和栈中的数据。
线性增长 栈是线性增长的意味着数据只能在一个方向上通常是向高地址方向增加或减少。这种简单的增长模式有助于快速管理栈内存因为不需要复杂的内存分配和释放算法。
自动管理特性 栈的管理是由编译器和运行时系统自动完成的。当函数被调用时函数的局部变量和参数会被压入栈中当函数返回时这些数据会从栈中弹出并释放相应的内存空间。这种自动管理减少了程序员手动管理内存的需求同时也减少了内存泄漏和野指针等错误的风险。
数据访问速度快 由于栈区通常位于内存的较低地址区域并且栈的线性增长和自动管理特性简化了内存访问模式因此栈上的数据访问速度非常快。此外栈区通常位于CPU缓存cache友好的地址空间内这意味着栈上的数据更有可能被缓存从而进一步提高了访问速度。
1.5 栈区存储方向 栈区的存储方向是向内存地址减小的方向增长即由高地址向低地址增长。 测试题 不要小看这一道题目里面涉及了5个知识点
这里main函数内是局部变量放在栈区。栈区的存储方向是向内存减小的方向增长即由高地址向低地址增长。数组在内存中的存储是连续的且由低地址向高地址存储。小端字节序存储低位字节数据内容存储在内存的低地址出高位字节数据内容存储在内存的高地址处。%x打印为十六进制形式打印且打印顺序由高位字节数据到低位字节数据。
根据上述5点我们可以画出该数据在内存中的存储
2、栈区使用注意事项
避免栈溢出由于栈区大小有限程序员应避免使用深度递归或大量局部变量来防止栈溢出。避免栈上分配大数组或结构体虽然栈区访问速度快但由于其大小限制不建议在栈上分配过大的数组或结构体。这些数据结构应优先考虑在堆区Heap上分配。注意变量生命周期由于栈区内存由系统自动管理程序员应特别注意变量的生命周期。在函数返回后栈上的局部变量将不再有效不应被访问。 二、堆区
1、堆区的定义
堆区是c语言中用于动态分配内存的区域与栈区不同的是堆区的内存分配和释放是需要程序员自己手动控制的而不是由系统自动管理的。在上面栈区的内存分配和释放是由编译器编译和运行时自动处理的。
2、堆区的特点
特点1大小可变 堆区的大小是不固定的可以根据需要动态地增加和减少。 而关于堆区的内存分配由程序员自己进行分配的话就需要用到四个函数malloccallocreallocfree。这四个函数前面三个函数是用来开辟和调整需要的内存大小free函数则是释放程序员动态分配的内存。所以在堆区堆区的大小是不固定的。可以随时根据程序员的需要程序员自己进行调整。
特点2不连续性 堆区的数据块可以随意的分配和释放它们的位置是不固定的。 简单的来说堆区是由程序员自己调用函数进行开辟的哪里内存满足程序员需要的内存大小都可以进行存储。
特点3长生命周期 堆区分配的内存空间在程序运行期间一直存在直到显式地释放。
特点4手动管理 因为堆区分配的内存空间在程序运行期间一直都存在那么如果不及时的将它释放他就会一直存放在那里所以程序员在堆区开辟完内存空间后不需要使用的时候需要将它及时的释放。
3、堆区的内存分配与释放
堆区的内存分配
malloc函数malloc函数是用于在堆区分配指定大小的内存空间并且返回一个指向该内存空间的指针。如果分配失败则返回NULL。calloc函数calloc函数和malloc函数类似只不过calloc函数在堆区分配指定大小的内存空间的时候会将分配的内存初始化为0。realloc函数realloc函数用于调整已分配内存的大小。如果新的大小大于原大小则扩展内存区域如果新的大小小于原大小则缩小内存区域并释放多余的内存空间。
堆区的内存释放
free函数free函数是用于释放之前通过malloc、calloc或者realloc分配的内存。释放后的内存不再被程序使用直到再次分配。
4、注意事项
避免内存泄漏 程序员需要确保不再需要堆区内存的时候及时释放它以避免内存泄漏。内存泄漏会导致程序占用的内存不断增加最终导致系统崩溃。 避免使用野指针 free函数将动态分配的内存释放的时候应该将指向这块内存的指针置为NULL指针以避免野指针的出现。野指针是指向已经释放内存的空间它可能导致程序崩溃或不可预测的行为。 三、全局/静态存储区
1、全局存储区 全局存储区用于存储全局变量和静态全局变量。这些变量在整个程序的整个生命周期内都存在并且可以在程序的任何地方访问对于全局变量或者定义他们文件的内部访问对于静态局部变量 1.1 全局变量
定义在函数外部定义的变量作用域整个程序所有文件如果变量被声明为extern生命周期从程序开始到程序结束示例
int a 10; //a即为全局变量
int main()
{printf(%d,a)return 0;
}1.2 静态全局变量
定义在函数外部被static关键字定义的变量作用域定义他们的文件内部切记只能在它们定义的文件内部相当于static将它锁死在那个文件中其他文件中使用会出错生命周期从程序开始到结束示例
static int c 10;
int main()
{printf(%d, c);return 0;
}2、静态存储区 静态存储区不仅包含静态全局变量还包含静态局部变量和常量字符串。这些变量在程序的整个生命周期内都存在但他们的可见性和作用域可能有所不同。 2.1 静态局部变量
定义在函数内部使用static关键字定义的变量作用域定义它们的函数内部生命周期从程序开始到程序结束即使函数执行完毕变量也不会销毁。示例
int test()
{static int i 0;i;return i;
}
int main()
{for (int i 0; i 5; i){int ret test();printf(%d, ret);}return 0;
}分析 正常来说像i这种函数变量属于临时变量存储在栈区随着函数的使用结束而内存销毁。在这里我们使用static修饰变量i此时i不再存储在栈区而是存储在静态存储区此时i不会随着函数的使用结束而销毁运行的时候i是逐渐增加的值而不是进来函数变量就重新开辟一次。这与不用static的结果是截然不同的。
使用static修饰变量不会随函数使用完毕而销毁 不使用static函数定义的变量i此时存储在栈区随着函数的使用结束会销毁需要使用的时候需要重新创建 四、常量存储区 在c语言中常量存储区也称为常量数据段或只读数据段是内存中的一个特定区域用于存储程序中的常量值。这些常量值在程序的整个生命周期都不会发生改变并且通常保存在只读内存中以防止它们被意外修改。 1、常量的定义和存储
在c语言中常量可以通过多种方式定义包括const关键字、#define预处理指令、以及枚举类型
const关键字用于声明一个变量为常量该变量的值在初始化后不能被修改
const int MAX_SIZE 100;#define预处理指令用于在预处理阶段定义常量。这种方式定义的常量实际上是在编译前进行文本替换而不是真正的变量。例如
#define PI 3.14159枚举类型enum用于定义一组命名的整数常量。例如
enum Color { RED, GREEN, BLUE };2、常量字符串
定义常量字符串是用双引号括起来一系列字符它表示一个不可变的字符序列。在c语言中字符串以空字符‘\0’作为结束标志因此常量字符串在内存中上实际是一个以空字符结尾的字符数组特性: 只读性常量字符串的值在程序运行期间是不可改变的。如果尝试修改常量字符串的内容将会导致未定义行为通常是程序崩溃或数据损坏。存储位置常量字符串通常存储在程序的只读数据段也称为常量存储区 中这意味着即使程序的其他部分如堆区或全局\静态存储区的数据发生改变常量字符串的内容也不会发生改变。c语言中常量字符串是通过字符数组来表示的。然而与普通的字符数组不同的是常量字符串的数组元素是不可以被修改的。 声明与初始化
const char *str Hello, World!;这里str是一个指向常量字符串的指针而Hello, World!则是存储在只读数据段中的常量字符串。 注意事项 当使用指针指向常量字符串时应该确保不会通过该指针修改字符串的内容。如果确实要修改字符串应该使用字符数组而不是字符串常量编译器可能会将相同的常量字符串合并为一个单一的存储位置以节省内存空间。这是编译器优化的一部分但程序员不应该依赖这种优化来节省内存。 错误示例代码
#include stdio.h int main() { const char *str Hello, World!; printf(%s\n, str); // 尝试修改常量字符串的内容错误做法 // str[0] h; // 这将导致未定义行为 return 0;
}在这个示例中str是一个指向常量字符串Hello, World!的指针。程序通过printf函数打印出这个字符串。然而如果尝试修改str指向的字符串内容如注释中所示将会导致未定义行为。