以前向内存申请空间时(两种方式)
int a;//开辟几个字节的方式int a[10];//开辟连续的空间
缺点:一旦申请好空间后,大小是无法修改的如果想描述一个班的成绩,如果几个班人数不相同,只能用int arr[30]来描述吗?
申请一块连续的空间,返回指向这块空间的指针


内存布局:

给free传动态空间的起始地址,把空间的使用权限还给了操作系统

#include<stdlib.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i < 5; i++)
{
*(p + i) = i+1;
}
free(p);
p = NULL;
}calloc
calloc与malloc不同的是参数部分,并且calloc会初始化空间为零

realloc


具体是如何调整的?

错误示例:

如果realloc调整失败,返回空指针,那原来的空间也找不到了 当realloc传参部分是NULL时,功能相当于malloc






可以在中间把p赋值为NULL,free(NULL)什么事都不做

另外:

第一题: 错误案例

方法一:
void Getmemory(char **p)
{
*p = (char*)malloc(100);
}
int main()
{
char * str = NULL;
Getmemory(&str);
strcpy(str, "hellow word");
printf("%s", str);
free(str);
str = NULL;
return 0;
}方法二:
char* Getmemory()
{
char *p = (int*)malloc(100);
return p;
}
int main()
{
char * str = NULL;
str = Getmemory();
strcpy(str, "hellow word");
printf("%s", str);
free(str);
str = NULL;
return 0;
}第二题:

栈可以返回值,但是不能返回地址 第三题:避免内存泄漏的问题

第四题:free完并没有初始化为空指针,要手动初始化为空指针

int main()
{
char* p = (char*)malloc(20);
if (p == NULL)
{
perror("malloc");
}
strcpy(p, "hellow");
printf(p);
free(p);
p = NULL;
return 0;
}在结构体中,最后一个成员,并且最后一个成员是数组,数组没有指定大小,这个时候是柔性数组 两种写法:

柔性数组的特点:

”柔性“由来:
正常开辟空间: struct S s1;
而柔性数组不是这样开辟空间的,是创建在堆上,用malloc来开辟

struct S
{
int n;
int arr[];
};
int main()
{
struct S* p = (struct S*)malloc(sizeof(int) + 5 * sizeof(int));
if (p == NULL)
{
perror("malloc");
return 1;
}
p->n = 100;
for (int i = 0; i < 5; i++)
{
p->arr[i] = i + 1;
}
struct S* ptr = (struct S*)realloc(p, sizeof(int) + 10 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
for (int i = 5; i < 10; i++)
{
p->arr[i] = i + 1;
}
}
free(p);
p = NULL;
}其他方法:

struct S
{
int n;
int* arr;
};
int main()
{
struct S* p = (struct S*)malloc(sizeof(struct S));
if (p == NULL)
{
perror("malloc");
return 1;
}
p->arr = (int *)malloc(5 * sizeof(int));
if (p->arr == NULL)
{
perror("malloc");
return 2;
}
p->n = 100;
for (int i = 0; i < 5; i++)
{
p->arr[i] = i + 1;
}
int* ptr = (int*)realloc(p->arr, 10 * sizeof(int));
if (ptr != NULL)
{
p->arr = ptr;
for (int i = 5; i < 10; i++)
{
p->arr[i] = i + 1;
}
}
free(p->arr);
free(p);
return 0;
}哪一种方案更好?
第一种

文件可以让数据持久性的保存,文件一般分为程序文件(后缀为.c、.obj、.exe)和数据文件(写程序操作的文件)

文件的操作与喝一瓶水类似:
流的概念 由于不同的外部设备不同的操作方式不一样,为了简化程序员学习的难度,引入流的概念

一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。 C语言程序启动时默认会打开流

C语言中就是通过文件指针来维护各种流的操作 每一个被使用的文件在内存中开辟了文件信息区,用来存放与文件相关的信息(文件名、文件位置、文件状态),这些信息保存在结构体变量中,结构体类型取名为FILE 不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件 每一个文件都有文件信息区和指向文件信息区的文件指针 两个关键的函数



以写文件为例(fputc)


int main()
{
FILE* p = fopen("test.txt", "w");
for (char ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, p);
}
fclose(p);
p = NULL;
return 0;
}读文件为例(fgetc)


int main()
{
FILE* p = fopen("test.txt", "r");
int ch = 0;
while ((ch=getc(p))!= EOF)
{
printf("%c", ch);
}
fclose(p);
p = NULL;
return 0;
}写文件(fputs)

int main()
{
FILE* p = fopen("test.txt", "w");
if (p == NULL)
{
perror("fopen");
}
fputs("abde\n", p);
fputs("haha", p);
fclose(p);
p = NULL;
return 0;
}读文件(fgets),不会把\0写进去,并且只读一行,换行不会读取(长度允许的话,会把\n读取,再补\0)


int main()
{
FILE* p=fopen("test.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
char arr[20] = { 0 };
while (fgets(arr, 10, p) != NULL)
{
printf("%s", arr);
}
fclose(p);
p = NULL;
return 0;
}
#include<stdio.h>
struct S
{
int age;
char name[10];
};
int main()
{
struct S s1 = { 18,"zhangshan" };
char arr[100] = { 0 };
sprintf(arr, "%d %s", s1.age, s1.name);
printf("%s\n", arr);
struct S p1 = { 0 };
sscanf(arr, "%d%s", &(p1.age), p1.name);
printf("%d %s", p1.age, p1.name);
}
int main()
{
FILE*p=fopen("text.txt", "wb");
if (p == NULL)
{
perror("fopen");
}
int arr[] = { 1,2,3};
fwrite(arr, 4, 3, p);
fclose(p);
return 0;
}
返回值是成功读取到的个数
int main()
{
FILE* p = fopen("text.txt", "rb");
if (p == NULL)
{
perror("fopen");
}
int arr[5] = { 0 };
fread(arr, sizeof(int), 5, p);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
控制指针位置
int main()
{
FILE*p=fopen("text.txt", "r");
if (p == NULL)
{
perror("fopen");
}
char ch = fgetc(p);
printf("%c ", ch);
fseek(p, -4, SEEK_END);
ch = fgetc(p);
printf("%c ", ch);
fseek(p, 2, SEEK_SET);
ch = fgetc(p);
printf("%c ", ch);
return 0;
}
计算偏移量
int main()
{
FILE*p=fopen("text.txt", "r");
if (p == NULL)
{
perror("fopen");
}
fseek(p, 0, SEEK_END);
int ret = ftell(p);
printf("%d", ret);
return 0;
}
文件指针位置回到起始位置
int main()
{
FILE*p=fopen("text.txt", "r");
if (p == NULL)
{
perror("fopen");
}
fseek(p, 0, SEEK_END);
rewind(p);
char ch = 0;
ch = fgetc(p);
printf("%c", ch);
return 0;
}
当判断是什么原因导致文件结束的可以用以下函数

int main()
{
FILE* p=fopen("text.txt", "w");
if (p == NULL)
{
perror("fopen");
}
int ch = 0;
while ((ch = fgetc(p)) != EOF)
{
printf("%c\n", ch);
}
if (feof(p))
{
printf("遇到文件末尾\n");
}
else if(ferror(p))
{
perror(NULL);
}
return 0;
}

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。