Linux环境下C编程指南(第二版) -- 内存管理

来源:互联网 发布:刺客信条枭雄a卡优化 编辑:程序博客网 时间:2024/05/29 04:35

内存管理:

       静态内存:在程序开始执行时,由系统分配的内存。也即栈内存

       动态内存:在程序运行过程中,由用户自己申请分配的内存。也即堆内存

      

       此处有一个典型例子:

       函数中不能返回局部变量的指针,因为在函数结束时,局部变量销毁,它的指针所指向的内存的内容不可预知,且对其进行的访问也是非法的。

     #include <stdio.h>

   #include <string.h>

    

   char * upcase( char * oldstring);

   int main(void)

   {

       char * str1;

       str1 = upcase("Hello");

       printf("str1 = %s\n", str1);

       return 0;

   }

 

   char * upcase( char * oldstring)

   {

      int counter;

      char newstring[100];

      memset(newstring, 0, sizeof(newstring));

      strcpy(newstring, oldstring);

      for(counter = 0; counter < strlen(newstring); counter++)

      {

         if(newstring[counter] >=97 && newstring[counter] <=122)

         {

             newstring[counter] -= 32;

         } 

      }

      return newstring;

   }

       解决该问题的一个方法:

       #include <stdio.h>

   #include <string.h>

 

   void upcase(char * inputstring, char * newstring);

 

   int main(void)

   {

       char str1[100], str2[100];

       memset(str1, 0, sizeof(str1));

       memset(str2, 0, sizeof(str2));

    

       upcase("Hello", str1);

       upcase("Goodbye", str2);

       printf("str1=%s, str2=%s\n", str1, str2);

       return 0;

   }

 

   void upcase(char * inputstring, char * newstring)

   {

       int counter = 0;

       strcpy(newstring, inputstring);

       for(counter= 0; counter < strlen(inputstring); counter++)

       {

           if(newstring[counter] >= 97 && newstring[counter] <= 122)

          {

              newstring[counter] -= 32;

          }

      }

   }

       使用动态内存,用户需要自行控制内存的分配和释放,用户需要根据需要随时申请所需要的内存,在使用完毕后,手动将内存区域释放。

       在大型项目中,由于释放某块动态内存释放前,将执行内存区域的指针重新赋值,导致从而导致内存区域无法释放的情况十分常见。通常将内存分配后没有释放而导致可用内存减少称之为内存泄漏,避免内存泄漏耗尽系统资源是许多服务器运行一段时间需要重新启动的原因。

      

      内存管理操作:

       内存空间分配使用说明:

       #include <stdlib.h>

       void * malloc(size_t size);

       void * calloc(size_t nmemb, size_t size);

       malloc参数size表示申请分配的内存空间大小,以字节标记。calloc的参数nmemb表示分配的内存空间占的数据项数目,参数size表示每一个数据项的大小,也即calloc分配大小nmemb× size大小的内存空间。

 

       成功时,返回指向内存空间的指针,否则返回NULL

       内存释放:

       #include <stdlib.h>

       void free( void * ptr);

      

       调整内存大小:

       #include <stdlib.h>

       void * realloc(void * ptr, size_t size);

      

       realloc函数的作用是重新调整一块动态内存区域的大小,参数ptr指向要调整的内存的指针,应该是malloc函数或calloc函数的返回值,size是新定义的内存的大小。Size可以小于或大于原来内存的大小。

       ptr NULL,则函数realloc作用相当于malloc,如果参数size0,则相当于free

 

       分配堆栈:

       #include <stdlib.h>

       void * alloca( size_t size);

       参数size指定了所分配的内存空间的大小,malloc callocrealloc函数都是在堆中进行分配,而该函数是在栈中分配,很少使用。

      

       内存映像I/O

       内存映像并不是一种内存管理,是一种特殊的I/O操作方式。它是内存管理的一个例子。内存映像其实是在内存中创建一个与外存完全相同的映像。可以将整个文件映射到内存,也可以将文件的一部分映射到内存,从而使用操作内存的方法对文件进行操作。

       内存映像I/O也有局限性,当用户将文件或文件的一部分映射到内存中,必须实现定义所使用内存空间的大小,要向内存映像添加数据是非常麻烦的。在这映像文件只能是普通文件而不能是管道、套接字这样的文件。

       创建内存印象:

       #include <sys/types.h>

       #include <sys/mman.h>

       void * mmap( void * start, size_t length, int prot, int flag, int fd, off_t offset);

      

       start void指针,通常为NULL,如果不为NULL表示希望将文件映射到指针指向的位置。但不能保证调用一定将文件映射到这一位置。

       length 定义内存映像所占用的内存空间的大小,以字节标记。

       参数prot表示内存映像的安全属性。例如

              PROT_EXEC 被映像内存可能含有机器码,可以执行

              PROT_NONE被映像内存不允许访问

              PROT_READ被映像内存可读

              PROT_WRITE被映像内存可写

       flag 是内存映像的标记,

              MAP_FIXED 如果无法在start指定的地址建立内存映像,则出错返回

              MAP_PRIVATE     对内存映像所作的改动不反映到外存

              MAP_SHARED     对内存映像所作的改动都被保存到外存文件

       fd为要映射的文件的描述符

       offset为映射的数据内容据文件头的偏移量

      

       调用成功时,返回值为指向内存映像其实地址的指针,调用失败返回值为 -1

       要将一个文件映射到内存需要使用文件的文件描述符,也即要在进程中将该文件打开。

 

       撤销内存映像:

       #include <sys/tepes.h>

       #include <sys/mman.h>

       int munmap( void * start, size_t length);

       start表示要撤销的内存映像的起始地址,length表示要撤销的内存映像的大小。调用成功返回0,调用失败是返回-1errno设置为相应值。

       将内存映像写入外存

       #include <sys/types.h>

       #include <sys/mman.h>

       int msync( const void * start, size_t length, int flag);

       此函数将内存映像中的改动刷新到外存文件中。参数start表示要保存到外存的源文件的其实地址,参数length表示内存映像的大小。参数flag设置了参数相应的操作。

       MS_ASYNC 调度一个写操作,并返回

       MS_INVALIDATE 是映像到相同文件的内存映像无效,以便是他们更改为新的数据

       MS_SYNC 完成写操作后函数返回。

      

     内存映像的例子:

     #include <sys/types.h>

   #include <sys/stat.h>

   #include <sys/mman.h>

   #include <stdio.h>

   #include <string.h>

   #include <stdlib.h>

   #include <errno.h>

   #include <unistd.h>

   #include <fcntl.h>

 

   int write_buffer( int fd, const void * buf, int count);

 

   int main(void)

   {

    int outfile;

    char * mapped;

 

    char * ptr;

    if((outfile=open("test.dat", O_RDWR|O_CREAT|O_TRUNC, 0640)) == -1)  // 打开要读写的文件

    {

        printf("Couldn't Open this file!\n");

       exit(254);

    }

    lseek(outfile,1000, SEEK_SET);    // 重新定位文件指针,在偏移量为1000的地方

    if(write_buffer(outfile, "\0", 1) == -1)  // 在文件中写入一个“\0”字符串结束符

    {

        printf("Error, write failed!\n");

       exit(254);

    }

 

    mapped = mmap(NULL, 1000, PROT_READ|PROT_WRITE, MAP_SHARED, outfile, 0);   // 在内存中创建一个映像文件

 

    if(NULL == mapped)

    {

        printf("Error, MMap Failed!\n");

       exit(254);

    }

 

    ptr = mapped;

    printf("Please Enter a Number:");

    fgets(mapped, 80, stdin);    // 向映像文件中读入内容

    ptr += strlen(mapped);    // 在映像中重新定位一个位置

    ptr ++;

    sprintf(ptr, "Your number times two is:%d.\n", atoi(mapped)*2);   // 将字符串写入重新定位的映像内存中

    printf("Your Number times Two is: %d\n", atoi(mapped)*2);

    msync(mapped, 1000, MS_SYNC);     //

    munmap(mapped, 1000);

 

    if(close(outfile))

    {

        printf("Possibly serious error, close file failed!\n");

       exit(254);

    }

 

    return 0;

}

 

int write_buffer( int fd, const void * buf, int count)

{

    const void * pts = buf;

    int status = 0, n=0;

    if(count < 0)

    {

        return -1;

    }

    while(status != count)

    {

        if( (n = write(fd, "\0", 1)) == -1)

       {

           printf("Error, write failed!\n");

           exit(254);

       }

 

       if(n < 0)

       {

           return n;

       }

 

       status += n;

        printf("Status:%d, n:%d", status, n);

    }

 

    return status;

}

      

       修改内存映像的属性:

       对映像的保护值进行修改。

       #include <sys/types.h>

       #include <sys/mman.h>

       int protect( const void * addr, size_t length, int prot);

       addr 为内存映像起始地址,length为内存映像的大小,参数prot为新设定保护值。

      

       修改内存映像的大小:

       #include <sys/types.h>

       #include <sys/mman.h>

       void * mremap(void * old_addr, size_t old_length, size_t new length, unsigned long flag);

       将参数old_addr指向的内存映像的大小有old_length调正为new_length,参数flag用与设置是否在需要是移动内存映像的位置,取值为MREMAP_MAYMOVE,则在需要移动是进行移动,否则需要移动位置时出错返回。

原创粉丝点击