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

C语言16-static、malloc和free、文件操作

程序员文章站 2022-03-09 15:12:49
...

变量的分类和内存分区

实际上,内存是分区的,简单而言,内存分为三个区域:

  • 全局区:全局变量、代码机器码放在此区域。全局区的大小,由程序本身的代码以及全局变量的大小决定。
  • 栈区:存放参数、局部变量、返回地址等和函数调用有关的内容
  • 堆区:除了全局区和栈区的空间,都算作堆区。堆区域的内存,需要程序员手工管理(在C语言中是这样的)我们可以将堆区域类比成一个酒店,我们需要动态的租房和退房。

变量的分类。
变量的分类涉及到变量的一下几个重要的属性:

  • 在哪里(内存分区):不同的变量所在的内存区域是不一样的
  • 作用域:在什么范围内,可以使用。作用域一般分为:
    函数作用域、文件作用域(只能在一个文件中被调用)、工程作用域
  • 生命周期:一个变量在内存中被分配空间到空间被释放销毁的过程,被称为一个生命周期。

全局变量、局部变量、静态全局变量、静态局部变量

静态局部变量
使用关键字static修饰局部变量,就得到静态局部变量

void Test()
{
    static int s_nValue=10;
    s_nValue++;
}

int main (int argc, char* argv[])
{
    Test();
    Test();
    Test();
    return 0;
}

静态局部变量,其实就是被限制了作用域的全局变量
C语言16-static、malloc和free、文件操作
以上,只有堆区的变量是比较特殊的,需要结合代码理解。

堆,malloc和free

我们已经知道了,程序运行后,有非常大的一块空间,是动态申请和释放的,那块空间称为堆。
我们可以通过C语言标准库函数中的malloc和free,来动态申请以及释放堆中的控件。
函数原型:

void *malloc( size_t(相当于 unsigned int) size );

malloc接受一个参数,size意味着想要申请的空间的大小。malloc会返回一个没有解释方式的指针,指向刚刚申请到空间的首地址。

#include <stdlib.h>
int main(int argc, char* argv[])
{
    int * pValue =NULL;
    pValue = (int *)malloc(4);
    *pValue=0x1111111;
    *(pValue+1)=0x2222222;
    return 0;
}

(fd是对空间的的上下界限,一般为两个字节,四bit)
最基本上都malloc入门已经讲完。

malloc技巧

与sizeof配合使用

pValue = (int *)malloc(4*sizeof(int ));

错误检查,malloc申请空间失败时,会返回0地址
为了语法malloc失败造成的风险,我们务必要对返回值进行检查

int * pValue = NULL;
pValue =(int *)malloc(100000000000);
if(pValue==NULL)
{
    printf("申请失败\r\n")
    return -1;
}

*pValue = 0x1111111;

free的使用方法

void free( void *memblock );

申请到的堆的首地址传递给free,即可完成堆空间的释放。
因为释放后的空间,不应该使用,但是C语言中对于使用释放后的空间这种行为,没有约束力。
所以, 强烈推荐,应该按一下代码规范,释放资源:

if(pValue!=NULL)
{
    free(pValue);
    pValue=NULL;
}

free空间后,VS的debug版会格式空间

资源申请和释放的模板

goto的使用场景。
第一个场景:跳出多重循环

int main (int argc, char* argv[])
{
    for (size_t i = 0; i < 100; i++) 
    {
        for (size_t j = 0; j < 100; j++) 
        {
            if (i == 50 && j == 75)
            {
                printf("找到(50, 75)\r\n");
                goto LOOP_EXIT; 
            } 
        }
}

LOOP_EXIT: printf("这里是LOOP标签\r\n"); 
return 0; 
}

第二个场景:资源的动态申请和释放模板。我们先看一个反例,在一个程序中,不恰当的申请多个互相有依赖关系的动态资源。

int main(int argc, char* argv[])
{
    int *pStudent = NULL;
    int *pTeacher = NULL;
    int *pSchool = NULL;

    pStudent = (int *)malloc(sizeof(int));
    if (pStudent == NULL)
    {
        return -1;
    }
    *pStudent = 0x11111111;
    pTeacher = (int *)malloc(sizeof(int));
    if (pTeacher == NULL)
    {
         if (pStudent != NULL)
         {
            free(pStudent);
            pStudent = NULL;
         }
        return -1;
    }
    *pTeacher = 0x22222222;
     
    pSchool = (int *)malloc(sizeof(int));
    if (pSchool == NULL)
    {
        if (pTeacher != NULL)
        {
            free(pTeacher);
            pTeacher = NULL;
            if (pStudent != NULL)
            {
                free(pStudent);
                pStudent = NULL;
            }    
        }
        else if (pTeacher == NULL)
        {
            if (pStudent != NULL)
            {
                free(pStudent);
                pStudent = NULL;
            }
        }
        return -1;
    }
    *pSchool = 0x33333333;
}

而我们使用goto,就很好了,请记住一下的资源申请和释放模板:

#include<sdlib.h>
int main(int argc, char* argv[])
{
    int nRet = 0;//0表示正常
    int *pStudent = NULL;
    int *pTeacher = NULL;
    int *pSchool = NULL;

    pStudent = (int *)malloc(sizeof(int));
    if (pStudent == NULL)
    {
         nRet = -1;
         goto LABEL_EXIT;
    }
    *pStudent = 0x11111111;

    pTeacher = (int*)malloc(sizeof(int));
    if (pTeacher== NULL)
    {
         nRet = -1;
         goto LABEL_EXIT;
    }
    *pTeacher = 0x2222222;

    pSchool = (int*)malloc(sizeof(int));
    if (pSchool== NULL)
    {
        nRet = -1;
        goto LABEL_EXIT;
    }
    *pSchool = 0x33333333;
    LABEL_EXIT:
    //统一检查和释放
    if (pSchool != NULL)
    {
        free(pSchool);
        pSchool = NULL;
    }

    if (pTeacher != NULL)
    {
        free(pTeacher);
        pTeacher = NULL;
    }

    if (pStudent != NULL)
    {
        free(pStudent);
        pStudent = NULL;
    }

    return nRet;
}

文件操作

所谓文件,是操作系统的文件系统对信息的抽象。
C语言中,进一步将操作系统中的文件,封装成了结构体FILE,并且,提供了一系列的函数,满足对文件的操作。

  • fopen:打开或者创建文件
  • fwrite:将内存中的内容,写入文件中
  • fread:将文件中的内容,读取到内存中
  • fclose:关闭文件
  • fseek:移动文件偏移
    所有的以上函数,都是通过FILE*来操作对应的文件的。
    以下的例子,我们创建一个文件,向其内部写入helloworld。
int main(int argc, char* argv[])
{
    FILE* pFile = fopen("nr1001.txt","w");

    if (pFile == NULL)
    {
        return -1;
    }
    char szMsg[] = "hello\r\nworld";

    fwrite(szMsg, sizeof(char), strlen(szMsg), pFile);
    fclose(pFile);
    return 0;
}

有了文件,意味着我们可以持续化存储数据。