Bootstrap

【第十四天打卡】一月速通C语言,动态内存分配,文件操作。


昨天学习了结构体和共用体,今天学习动态内存分配和文件操作,彻底结束基础知识的学习,后面会花费三天的时间对前面所学知识查漏补缺,四天时间做项目,二十一天时不到一个月结束C语言学习。随后开启一月速成C++的新篇章;

动态内存分配

函数名全部单词作用
mallocmemory allocation申请空间连续
calloccontigous allocation申请空间+数据初始化
reallocre_allocation修改空间大小
freefree释放空间
示例:
#include<stdio.h>
#include<stdlib.h>

int main()
{
	//利用malloc申请一片连续空间
	//返回这片空间的首地址
	int* p = malloc(100 * sizeof(int));  //申请一片空间,存储100个int类型的整数
	int* p2 =calloc(10,sizeof(int));//calloc不但可以申请空间还能初始化
	printf("%zu\n",p);
	//赋值
	for(int i =0;i<100;i++)
	{
		*(p+i) = (i+1)*10;
		//另外一种赋值方法p[i] = (i+1)*10;
	}
	//遍历
	for(int i = 0;i<100;i++)
	{
		printf("%d\n",*(p+1));//同样可以写成p[i]
	}
	//扩容.20个int类型的整数
	int* pp = realloc(p,20 * sizeof(int));

	//释放空间
	//申请的空间不使用了一定要释放空间,谁申请谁释放。
	free(pp);
	return 0;
}

动态内存空间分配注意事项

  • malloc创建的空间单位是字节
  • malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转。
  • malloc返回的仅仅是首地址,没用总大小,最好定义一个变量来存储总大小。
  • malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄漏。
  • malloc申请的空间过多,会产生虚拟内存。(每一个内存空间不是申请了就马上使用而是存数据的时候才会使用)
  • malloc申请的空间没有初始化值需要赋值才能使用。
  • free释放完空间后,空间中的数据叫做脏数据,可能被清空,可能被修改为其他值。
  • calloc就是在malloc的基础上多一个初始化的动作。
  • realloc修改之后的空间,地址值可能发生变化,也可能不会改变,但是原本的数据不会丢失。
  • realoc修改后无需释放原来的空间,函数底层会进行处理。

C语言的内存结构

高地址
+---------------------+
|   命令行参数与环境变量  |
+---------------------+
|        栈区          | ← 栈指针(向下增长)
+---------------------+
|         堆区         | ← 动态分配(向上增长)
+---------------------+
|        .bss段        | (未初始化数据)
+---------------------+
|       .data段        | (初始化数据)
+---------------------+
|       代码段          | (只读)
低地址
  • 栈(Stack)

      作用:存储局部变量、函数参数、返回地址等。
    
      管理方式:由编译器自动分配和释放(函数调用时创建,返回时销毁)。
    
    	 特点:
    
       内存分配速度快(仅移动栈指针)。
    
       大小有限(默认约几MB,如Linux中8MB),可能导致栈溢出(如递归过深)。
    
       后进先出(LIFO)结构。
    
  • 堆(Heap)

     作用:存储动态分配的内存(如通过 malloc、calloc、realloc 分配)。
    
     管理方式:需手动分配和释放(free),否则会导致内存泄漏。
    
     特点:
    
     内存空间大(受系统虚拟内存限制)。
    
     分配速度较慢(需系统调用)。
    
     易产生内存碎片。
    
  • 数据段(Data Segment)

      分为两部分:
    
      初始化数据段(.data)
      
      存储显式初始化的全局变量和静态变量(如 int a = 10;)。
    
      未初始化数据段(.bss)
    
      存储未初始化的全局变量和静态变量,程序启动时会被系统初始化为 0 或 NULL。
    
      特点:
    
      	生命周期为整个程序运行期间。
    
      	全局变量和静态变量在此存储。
    
  • 代码段(Text Segment)

      作用:存储可执行指令(即编译后的机器代码)和常量字符串(如 "Hello")。
    
      特点:
    
     	 只读(防止程序意外修改指令)。
    
      	可被多个进程共享(节省内存)。
    
  • 命令行参数与环境变量

     位置:存储在栈的顶部。
    
     作用:存储 main 函数的参数 argc(参数个数)和 argv(参数数组),以及环境变量。
    

C语言中文件操作

输入流 输出流

读取文件(输入流)

路径:文件在电脑的位置。(Linux显示当前路径pwd)
linux中需要改为英文名

export LANG=en_US
# 改为中文 export LANG=zh_CN.utf-8
xdg-user-dirs-gtk-update

相对路径:以当前项目开始
绝对路径:以盘符开始

转义字符

路径是以字符串的形式来表示的
\转义字符。打印一个路径需要写两个\。

读取数据

把本地文件中的数据读取到程序中来。

  1. 打开文件(fopen)
  2. 读数据(fgetc,fgets,fread)
  3. 关闭文件(fclose)
    示例:
    r:只读模式
    w:只写模式(文件不存在创建文件,存在,清空文件。)
    a:追加写出模式(文件不存在,创建新文件,文件已存在,不清空文件续写)
    rb:同上操作二进制文件
    wb:同上操作二进制文件
    ab:同上操作二进制文件
#include <stdio.h>

int main() 
{
    // 打开文件(注意检查路径和扩展名)
    FILE* file = fopen("/home/aberoqin/Desktop/myfile/read/read.txt", "r");
    //判断文件是否存在,不存在打开失败
    if (file == NULL) 
    {
        perror("文件打开失败");
        return 1;
    }

    // 正确读取字符
    //读取数据fgetc一次读取一个字符,读不到返回-1;
    //fgetc如果读到了就返回读取的字符,读不到返回-1;
    int c;
    while ((c = fgetc(file)) != -1) 
    {  
        printf("%c", c);  
    }

    fclose(file);
    return 0;
}

利用fgets一次读一行数据

fgets一次读一行读不到返回NULL。

#include<stdio.h>
int main()
{
	//打开文件fopen
	FILE* file = fopen("/home/aberoqin/Desktop/myfile/read/read.txt","r");
    //利用fgets一次读取一行
    char arr[1024];
    //每次读取一行数据以换行符为准
    //读不到返回NULL
    char* str;
    while ((fgets(arr,1024,file))!=NULL)
    {
        printf("%s\n",str);
    }
    
    printf("%s\n",arr);
    fclose(file);
	return 0;
}

利用fread一次读取多个字节

#include<stdio.h>
int main()
{
	//打开文件fopen
	FILE* file = fopen("/home/aberoqin/Desktop/myfile/read/read.txt","r");
    //利用fread读取多字节
    //英文默认1个字节,中文默认2字节
    //每次尽可能把数组装满,返回当前读取到的有效字节个数。
    //文件100字节 数组长度30
    //第一次读取30字节,把数组装满,返回30/
    //第二次读取30字节,把数组装满,返回30
    //第三次读取30字节,把数组装满,返回30
    //第四次读取10字节,把数据放在数组中,返回10;
    //第五次没有数据读取了,返回0;
    char arr[1024];
    int n;
    while (fread(arr,1,1024,file)!=0)
    {
        printf("%s",arr);
    }
    

    fclose(file);
	return 0;
}

写出数据

  1. 打开文件fopen
  2. 写数据fputc fputs fwrite
  3. 关闭文件fclose
    示例:
#include <stdio.h>

int main() 
{
    // fputc一次写一个字符,返回写出的字符
    // fputs一次写一个字符串,返回非负数,一般忽略返回值
    // fwrite 一次写读多个,返回写出的个数
    FILE* file = fopen("/home/aberoqin/Desktop/myfile/read/a.txt", "w");
    int c = fputc(97,file);
    printf("%c\n",c);
  //fputs,一次写出一个字符串。
  //如果写出失败会有一个EOF的错误
    int n = fputs("hello world",file);
    printf("%d\n",n);
    //fwrite一次读多个返回写出的字节个数。
    char arr[] = {97,98,99,100,101};
    int n2 = fwrite(arr,1,5,file);
    printf("%d\n",n2);


    fclose(file);
    return 0;
}

综合案例:拷贝文件

#include<stdio.h>
int main()
{
	//打开文件fopen
	FILE* file1 = fopen("/home/aberoqin/Desktop/myfile/read/a.txt","r");
    FILE* file2 = fopen("/home/aberoqin/Desktop/myfile/read/b.txt","w");
    char arr[1024];
    int n;
    while ((n = fread(arr,1,1024,file1))!=0)
    {
        fwrite(arr,1,n,file2);
    }
    

    fclose(file1);
    fclose(file2);
	return 0;
}
;