摘要:
前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下 阅读全文
摘要:
文本居中 <center>markdown居中文本</center> 字体颜色 <font color=#FF0000> 你的字体</font> 图片居中 <div align=center> <img src="图片地址" width="XXX" height="XXX" /> </div> 表格 阅读全文
摘要:
前言 我们知道,变量是有数据类型的,用以说明它占用多大的内存空间,可以进行什么样的操作。 除了数据类型,变量还有一个属性,称为“存储类别”。存储类别就是变量在内存中的存放区域。在进程的地址空间中, 常量区、全局数据区和栈区可以用来存放变量的值。 常量区和全局数据区的内存在程序启动时就已经由操作系统分 阅读全文
摘要:
内存泄漏 使用 malloc()、calloc()、realloc() 动态分配的内存,如果没有指针指向它,就无法进行任何操作,这段内存会一直被程序占用,直到程序运行结束由操作系统回收。 请看下面的代码: #include <stdio.h> #include <stdlib.h> int main 阅读全文
摘要:
###野指针 如果一个指针指向的内存没有访问权限,或者指向一块已经释放掉的内存,那么就无法对该指针进行操作,这样的指针称为野指针(Wild Pointer)。 指向没有访问权限的内存 请看下面的代码: #include <stdio.h> int main(){ char *str; gets(st 阅读全文
摘要:
引言 相对于栈而言,堆这片内存面临着一个稍微复杂的行为模式:在任意时刻,程序可能发出请求,要么申请一段内存,要么释放一段已经申请过的内存,而且申请的大小从几个字节到几个GB都有可能,我们不能假设程序一次申请多少堆空间,因此,堆的管理显得较为复杂。 那么,使用 malloc() 在堆上分配内存到底是如 阅读全文
摘要:
例子1 我们先来看下面的一个例子: #include <stdio.h> int main(){ char str[10] = {0}; gets(str); printf("str: %s\n", str); return 0; } 在 main() 函数内部定义一个字符数组,并通过 gets() 阅读全文
摘要:
静态内存分配 在进程的地址空间中,代码区、常量区、全局数据区的内存在程序启动时就已经分配好了,它们大小固定,不能由程序员分配和释放,只能等到程序运行结束由操作系统回收。这称为静态内存分配。 动态内存分配 栈区和堆区的内存在程序运行期间可以根据实际需求来分配和释放,不用在程序刚启动时就备足所有内存。这 阅读全文
摘要:
debug 前面我们只是讲解了一个函数的活动记录是什么样子的,相信大家对函数的详细调用过程的认识还不是太清晰,这节我们就以 VS2010 Debug 模式为例来深入分析一下。 请看下面的代码: void func(int a, int b){ int p =12, q = 345; } int ma 阅读全文
摘要:
函数调用 我们知道,一个C程序由若干个函数组成,C程序的执行实际上就是函数之间的相互调用。请看下面的代码: #include <stdio.h> void funcA(int m, int n){ printf("funcA被调用\n"); } void funcB(float a, float b 阅读全文
摘要:
引言 函数的调用和栈是分不开的,没有栈就没有函数调用,本节就来讲解函数在栈上是如何被调用的。 栈帧/活动记录 当发生函数调用时,会将函数运行需要的信息全部压入栈中,这常常被称为栈帧(Stack Frame)或活动记录(Activate Record)。活动记录一般包括以下几个方面的内容: 1) 函数 阅读全文
摘要:
引言 在《Linux下C语言程序的内存布局(内存模型)》中我们讲到,程序的虚拟地址空间分为多个区域,栈(Stack)是其中地址较高的一个区域。栈(Stack)可以存放函数参数、局部变量、局部数组等作用范围在函数内部的数据,它的用途就是完成函数的调用。 栈内存由系统自动分配和释放:发生函数调用时就为函 阅读全文
摘要:
引言 首先我们要解释一个概念——进程(Process)。简单来说,一个可执行程序就是一个进程,前面我们使用C语言编译生成的程序,运行后就是一个进程。进程最显著的特点就是拥有独立的地址空间。 严格来说,程序是存储在磁盘上的一个文件,是指令和数据的集合,是一个静态的概念;进程是程序加载到内存运行后一些列 阅读全文
摘要:
引言 在32位环境下,Windows 默认会将高地址的 2GB 空间分配给内核(也可以配置为1GB),而将剩下的 2GB 空间分配给用户程序。 内存分布 不像 Linux,Windows 是闭源的,有版权保护,资料较少,不好深入研究每一个细节,至今仍有一些内部原理不被大家知晓。关于 Windows 阅读全文
摘要:
引言 在《虚拟地址空间以及编译模式》一节中讲到,虚拟地址空间在32位环境下的大小为 4GB,在64位环境下的大小为 256TB,那么,一个C语言程序的内存在整个地址空间中是如何分布的呢?数据在哪里?代码在哪里?为什么要这样分布?这些就是本节要讲解的内容。 内存模型 程序内存在地址空间中的分布情况称为 阅读全文
摘要:
引言 通过页表完成虚拟地址和物理地址的映射时,要经过多次转换,还要进行计算,如果由操作系统来完成这项工作,那将会成倍降低程序的性能,得不偿失,所以这种方式是不现实的。 MMU 在CPU内部,有一个部件叫做MMU(Memory Management Unit,内存管理单元),由它来负责将虚拟地址映射为 阅读全文
摘要:
分页 现代操作系统都使用分页机制来管理内存,这使得每个程序都拥有自己的地址空间。每当程序使用虚拟地址进行读写时,都必须转换为实际的物理地址,才能真正在内存条上定位数据。如下图所示: 内存地址的转换是通过一种叫做页表(Page Table)的机制来完成的,这是本节要讲解的重点,即: 页表是什么? 为什 阅读全文
摘要:
关于虚拟地址和物理地址的映射有很多思路,我们可以假设以程序为单位,把一段与程序运行所需要的同等大小的虚拟空间映射到某段物理空间。 例如程序A需要 10MB 内存,虚拟地址的范围是从 0X00000000 到 0X00A00000,假设它被映射到一段同等大小的物理内存,地址范围从 0X00100000 阅读全文
摘要:
计算机内存是以字节(Byte)为单位划分的,理论上CPU可以访问任意编号的字节,但实际情况并非如此。 CPU 通过地址总线来访问内存,一次能处理几个字节的数据,就命令地址总线读取几个字节的数据。32 位的 CPU 一次可以处理4个字节的数据,那么每次就从内存读取4个字节的数据;少了浪费主频,多了没有 阅读全文
摘要:
虚拟地址 所谓虚拟地址空间,就是程序可以使用的虚拟地址的有效范围。虚拟地址和物理地址的映射关系由操作系统决定,相应地,虚拟地址空间的大小也由操作系统决定,但还会受到编译模式的影响。这节我们先讲解CPU,再讲解编译模式,让大家了解编译器是如何配合CPU来提高程序运行速度的。 CPU的数据处理能力 CP 阅读全文