堆栈空间

C源代码进过预处理、编译、汇编和链接4步生成一个可执行程序。程序在没有运行之前,也就是说程序没有被加载到内存前,可执行程序内部已经分好3段信息,分别是代码区(text)、数据区(data)和未初始化数据区(bss)三个部分。(部分人直接把data和bss合起来叫做全局区或静态区)。
运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区、初始化数据区和未初始化数据区之外,还额外增加了栈区和堆区。

linux下的内存分配:

结构体和结构体指针的内存申请

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>
#include<stdlib.h>//包含malloc函数的头文件
#include<string.h>
typedef struct student//typedef可以将struct student结构体类型用std替代
{
char name[20];
int number;
char subject[20];
float scores;
}std;

int main()
{
std student1;
memset(&student1,0,sizeof(std));//初始化从student1开始的地址到后面sizeof(std)也就是结构体的字节个空间,这些空间都初始化为0也就是空。
printf("name:");
scanf("%s",student1.name);//如果不用memset初始化内存,用scanf语句输入了student1.name的信息再用printf输出是看不出来的,如果不输入直接输出的话就会乱码(原因也是系统内存没有初始化谁也不知道里面本来存的是什么)
printf("%s\n",student1.name);
//结构体是直接由栈自动分配指针的所以不需要我们来手动分配和释放内存,而接下来是用结构体指针来申请内存
std* student2 = (std*)malloc(sizeof(std));
if(student2 == NULL)//如果申请内存失败,malloc会返回一个NULl
{
printf("malloc use failure");
return 1;//申请内存失败,错误退出
}
memset (student2,0,sizeof(std));//
printf("name\n");
scanf("%s",student2->name);
printf("name:%s\n",student2->name);
free(student2);//释放指针指向的堆的内存但是不释放指针本身的内存,指针本身存放在栈区,再程序运行结束后才能自动释放
student2 = NULL;//这里就是将指针指向空指针,避免被错误调用
return 0;
}

文件中读取结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>

/* 定义结构体, 存储一个字符串和年龄 */
struct student
{
char name[20];
int age;
};

int main()
{
// 要写入文件的结构体
struct student s1 = {"Tom", 18};

// 打开要写入的文件
FILE *p = fopen("D:/File/student.dat", "w");
// 打开失败直接退出
if(p == NULL)
return 0;
// 将结构体写出到文件中
fwrite(&s1, 1, sizeof (struct student), p);
// 关闭文件
fclose(p);
// 读取文件中的结构体
// 存储读取到的结构体数据
struct student s2 = {0};

// 打开文件
FILE *p2 = fopen("D:/File/student.dat", "r");
// 如果打开失败, 退出
if(p2 == NULL)
return 0;
// 从文件中读取结构体信息
fread(&s2, 1, sizeof (struct student), p2);
// 关闭文件
fclose(p2);
// 打印数据
printf("student : name=%s, age=%d\n", s2.name, s2.age);
return 0;
}

读取结构体数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>

/* 定义结构体, 存储一个字符串和年龄 */
struct student
{
char name[20];
int age;
};

int main()
{
// 要写入文件的结构体
struct student s1[2] = {{"Tom", 18}, {"Jerry", 20}};

// 打开要写入的文件
FILE *p = fopen("D:/File/student.dat", "w");
// 打开失败直接退出
if(p == NULL)
return 0;

// 将结构体写出到文件中
fwrite(s1, 2, sizeof (struct student), p);
// 关闭文件
fclose(p);
// 读取文件中的结构体

// 存储读取到的结构体数据
struct student s2[2] = {0};

// 打开文件
FILE *p2 = fopen("D:/File/student.dat", "r");
// 如果打开失败, 退出
if(p2 == NULL)
return 0;

// 从文件中读取结构体信息
fread(s2, 2, sizeof (struct student), p2);
// 关闭文件
fclose(p2);

// 打印数据
int i = 0;
for(i = 0; i < 2; i ++)
printf("student : name=%s, age=%d\n", s2[i].name, s2[i].age);

return 0;
}

数据结构对于文件的增删查改

做了一个小系统遇到的问题

图书馆管理系统.c (gitee.com)

首先关于清屏linux有三种方式

1
2
3
system("clear");
printf("x1b[Hx1b[2J");
printf ("033c");

虽然都是清屏但是略有区别,首先是system(“clear”)好处可以替代windows中的system(“clr”)不知道是什么问题调用写的次数多了,会有不执行的情况.之后是printf(“033c”)会清楚掉之前对于终端命令行打印颜色的命令,所以它应该是重置整个终端。

之后的一些问题基本都是结构体的问题都写在了前面。

stdout与stdin,stderr的区别

当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流, 一个程序要运行,需要有输入、输出,如果出错,还要能表现出自身的错误。这是就要从某个地方读入数据、将数据输出到某个地方,这就够成了数据流。

因此,一个进程初期所拥有的这么三个数据流,就分别是标准输出、标准输入和标准错误,分别用stdout, stdin, stderr来表示。。这3个文件分别为标准输入(stdin)、标准输出(stdout)、标准错误(stderr)。
输出重定向是指把命令(或可执行程序)的标准输出或标准错误输出重新定向到指定文件中。这样,该命令的输出就不显示在屏幕上,而是写入到指定文件中。
使用 “ > ”符号,将标准输出重定向到文件中。形式为:命令>文件名
使用“ >> ”符号,将标准输出结果追加到指定文件后面。形式为:命令>>文件名
使用“ 2> ”符号,将标准错误输出重定向到文件中。形式为:命令 2> 文件名
使用“ 2>> ”符号,将标准错误输出追加到指定文件后面。形式为:命令 2>>文件名
使用“ 2>&1 ”符号或“ &> ”符号,将把标准错误输出stderr重定向到标准输出stdout

1
2
3
4
5
6
int main(){
fprintf(stdout,"Hello ");
fprintf(stderr,"World!");
return0;
}
//输出结果World!Hello

原理:在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。

而stderr是无缓冲的,会直接输出,举例来说就是printf(stdout, “xxxx”) 和 printf(stdout, “xxxx\n”),前者会憋住,直到遇到新行才会一起输出。而printf(stderr, “xxxxx”),不管有么有\n,都输出。