欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内存空间分配,以及malloc、calloc、realloc、alloca、realloc的区别

程序员文章站 2022-05-12 12:29:01
...

1、存储类别

内存空间分配,以及malloc、calloc、realloc、alloca、realloc的区别
作用域:程序可访问标识符的区域
块作用域:从定义处到块结尾
函数作用域:仅仅用与goto,goto标签整个文件有效
文件作用域:从定义处,到该文件结尾。
全局变量具有文件作用域,局部变量处于块作用域,goto标签函数作用域。
存储期:通过标识符访问对象的生存期
静态存储期:程序执行期间一直存在
自动存储期:程序进入变量定义,分配内存,退出时候释放此变量所占内存,例如的函数调用,这些变量都存储在栈里面。
全局变量,块内static声明的变量具有静态存储期。其他块内局部变量仅有自动存储期。
链接属性:
外部链接:可供其他文件使用,但是其他文件得声明。
内部链接:仅仅在单文件中使用。
无链接:块作用域变量。
链接属性除了和作用域相关,还和static声明有关。文件作用域都是外部链接,static声明的文件作用域变量仅有内部链接,块内变量无链接。

2、各种变量内存空间分配

0x1 00000 = 1M
512M 地址分配从 0 ——>512M-1 = 0x200 00000 - 1 = 0x1FF FFFFF
10M 地址分配从 0 ——>10M-1 = 0xA 00000 - 1 = 0x9 FFFFF

代码段:常见名字叫.text或.code,是程序源代码编译后机器指令所存放的位置。

通常代码段是可以共享的且只读(防止由于意外修改指令),所以重复执行程序存储器里面也只需要一个副本。

数据段:也叫.date段,内存中存放数据的地方。
初始化的全局变量和局部静态变量都存放在此段中。
BSS段:未初始化数据段,名字来源于早起汇编操作符。
没有初始化的全局变量和局部静态变量都存放在此段中。在程序执行开始,通常会被初始化为0或者空指针,因为这些值都是0,所在程序存储时候,不占内存。只有在执行的时候才分配内存。
特别注意:数据段和BSS段仅仅涉及全局变量和局部静态变量。
栈:局部变量以及每次函数调用时所需保存的信息都存放在此段.
每次函数调用时,其返回地址以及调用者的环境信息(如某些机器寄存器的值)都存放在段中.然后,最近被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈。C递归函数可以工作。递归函数每次调用自身时,就用一个新的栈帧,因此一次函数调用实例中的变量集不会影响另一次函数调用实例中的变量。所以局部变量在函数执行完毕之后,所占用内存会被释放掉。
堆:动态分配内存的变量存储在此段。
如malloc或new申请内存。

综上:源代码编译生成程序指令(代码段)和程序数据(数据段和BBS段)存储在硬盘中。然后在程序执行期间,读取程序指令和数据,进而配合堆和栈完好运行起来。

int global_init_var = 84;//数据段
int global_uninit_var;//BBS段
void func(int i)
{
    printf("%d\n" , i);//代码段
}
int main(void)
{
    static int static_var = 85;//数据段
    static int static_var2;//BBS段
    int a =1;//程序运行时,在栈上创建,退出自动消失
    int b;//程序运行时,在栈上创建,退出自动消失
    char *ch = "aabbccdd";//"aabbccdd"在数据段,ch在栈
    char *ptr = (char *)malloc(sizeof(char));//程序运行时,在堆上创建
    func1(static_var + static_var2 + a + b);//代码段
    return 0;
}

3、动态内存分配

#define __SIZE_TYPE__ long unsigned int
typedef __SIZE_TYPE__ size_t;
void *malloc (size_t  __size);
void *calloc (size_t __nmemb, size_t __size);
void *realloc (void *__ptr, size_t __size);

void *alloca (size_t __size);
void *realloc (void *__ptr, size_t __size);

以上函数定义都在stdlib.h函数库内,它们的返回值都是内存块的首地址,如果请求失败就返回NULL。
返回类型是 void*类型。void*表示通用类型的指针.void*类型可以强制转换为任何其它类型的指针.所以以上函数都配合强制类型转换使用。
2、malloc
从内存堆中分配__size字节空区域,并没有初始化所分配的内存空间。如果malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之, 如果这部分内存曾经被分配过,则其中可能遗留有以往各种各样的数据。
3、calloc
calloc与malloc类似,但是calloc会自动将分配的内存置为0。第一个参数是所需内存单元数量,第二个参数每个内存单元的大小是多少字节
3、realloc
需要把指向块己分配内存的区域指针,这块内存新的大小作为参数传入,就可以调整(扩大或缩小)这块内存区域为新的大小。这个过程中有可能涉及到内存的拷贝。
realloc对给定的指针所指的空间进行扩大时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,那么就使用堆上第一个有足够大小的*块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上,并返回这个新分配内存的首地址。
realloc对给定的指针所指的空间进行缩小时,则被缩小的那一部分的内容会丢失。realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.相反,realloc返回的指针很可能指向一个新的地址.

int main(void)
{
    int *ptr1,*ptr2;
    int *p1,*p2;

    ptr1 = (int *)malloc(10*sizeof(int));
    ptr2 = (int *)realloc(ptr1 , 8*sizeof(int));
    printf("ptr1=0x%x,ptr2=0x%x\n",ptr1,ptr2);

    p1 = (int *)malloc(10*sizeof(int));
    p2 = (int *)realloc(ptr1 , 100*sizeof(int));
    printf("p1=0x%x,p2=0x%x\n",p1,p2);
    exit(0);
}

ptr1=0x90a010,ptr2=0x90a010 缩小地址没有变化
p1=0x90a450 ,p2=0x90a480 扩大地址变化了