昨天学习了结构体和共用体,今天学习动态内存分配和文件操作,彻底结束基础知识的学习,后面会花费三天的时间对前面所学知识查漏补缺,四天时间做项目,二十一天时不到一个月结束C语言学习。随后开启一月速成C++的新篇章;
动态内存分配
函数名 | 全部单词 | 作用 |
---|---|---|
malloc | memory allocation | 申请空间连续 |
calloc | contigous allocation | 申请空间+数据初始化 |
realloc | re_allocation | 修改空间大小 |
free | free | 释放空间 |
示例: |
#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
相对路径:以当前项目开始
绝对路径:以盘符开始
转义字符
路径是以字符串的形式来表示的
\转义字符。打印一个路径需要写两个\。
读取数据
把本地文件中的数据读取到程序中来。
- 打开文件(fopen)
- 读数据(fgetc,fgets,fread)
- 关闭文件(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;
}
写出数据
- 打开文件fopen
- 写数据fputc fputs fwrite
- 关闭文件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;
}