#include <stdio.h>
struct S
{
int n;
int arr[];//柔性数组成员
};
int main()
{
int sz = sizeof(struct S);
printf("%d\n", sz);
return 0;
}
柔性数组特点:
1.结构中的柔性数组成员前面必须至少一个其他成员。
2.sizeof返回的这种结构大小不包括柔性数组的内存。
3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配没并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
柔性数组的使用:
#include <stdio.h>
struct S
{
int n;
int arr[];
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
if (ps == NULL)
{
return 1;
}
ps->n = 100;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d", ps->arr[i]);
}
realloc(ps, sizeof(struct S) + 80);
struct s* ptr = (struct S*) ps;
if (ptr != NULL)
{
ps = ptr;
}
//释放
free(ps);
ps = NULL;
return 0;
}
原理图:
普通方式:
#include <stdio.h>
struct S
{
int n;
int* arr;
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
{
return 1;
}
ps->n = 100;
ps->arr = (int*)malloc(40);
if (ps->arr == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d", ps->arr[i]);
}
//扩容80字节
int* ptr = (int*)realloc(ps->arr, 80);
if (ptr == NULL)
{
return 1;
}
return 0;
}
原理图:
C语言结构体里的成员数组和指针,陈皓
文件:
程序设计中,文件谈的有两种:程序文件,数据文件。
程序文件:包括源程序文件(后缀为.c),目标文件(widows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
数据文件:文件的内容不一定是程序,二十程序运行时的读写的数据,比如程序运行需要从中读取数据的文件或者输出内容的文件。
文件名包括三部分:
文件路径+文件名主干+文件后缀
每个被使用的文件都会在内存开辟相应区域
fopen创建文件信息区并将起始地址返回来用指针接受FILE*(文件指针)
通过FILE*可以找到文件信息去,又可以简介维护文件
#include <stdio.h>
FILE* fopen(const char* filename, const char* mode);
int main()
{
FILE* pf = fopen("text.txt", "w");
return 0;
}
文件打开失败:
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//关闭文件
pf = NULL;
return 0;
}
//写文件
char i = 0;
for (i = 'a', i <= 'Z'; i++)
{
fputc(i, pf);
}
//读文件(一个一个读)
int ch = fgetC(pf);
printf("%c\n", ch);
//全读
int ch = 0;
while ((ch = fgetC(pf)) != EOF);
printf("%c", ch);
写文件时如果文件本身有内容会直接清空写入
//读一行数据
char arr[20];
fgets(arr, 5, pf);
printf("%s\n",arr);
fgets读取数据会放入\0
如读hello,若5个字节,只会都hell,最多读20个
#include <stdio.h>
fprinf()
struct S
{
char arr[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 25, 50.5f };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fprintf(pf, "%s %d %f", s.arr, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
fsanf(pf, "%s%d%f", s.arr, &(s.age), &(s.score));
printf( "%s%d%f", s.arr, &(s.age), &(s.score));
//fprintf(stdout...)打印到屏幕上
原理图:
任何一个C程序,只要运行起来就会默认打开三个系统:
FILE* stdin 一个标准输入流(键盘)
FILE* stdout 输出(屏幕)
FILE* stderr 错误(屏幕)
二进制输入输出:
#include <stdio.h>
struct S
{
char arr[10];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 25, 50.5f };
//以二进制的形式写到文件中
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//以二进制形式写
fwrite(&s, sizeof(struct S), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
fwrite
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
fread
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
159.19:00 改通讯录
scanf是针对标准输入的格式化输入语句
printf是针对标准输出的格式化输出语句
fscanf是针对所有输入流的格式化输入语句
fprintf是针对所有输出流的格式化输出语句
sprintf:把一个格式化的数据写到字符串中,本质是把一个格式化的数据转换成字符串,
printf("%s", buf);
sprintf(buf,"%s%d%f", s.arr...);
//"zhangsan 20 55.500000"
按字符串来打印
sscanf:从一个字符串转化出一个格式化数据
sscanf(buf, "%s %d %f", tmp,arr....);
//从字符串buf中获取一个格式化数据到tmp
printf("%s%d%f", tmp.arr...)
文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转化的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储的文件就是文本文件。
一个数据在内存中是怎样存储的?
字符一律以ASCII码的形式存储,数值型数据即可以用ASCII码形式存储,也可以使用二进制形式存储。
如有整数1000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节
测试代码:
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwright(&a, 4, 1, pf);
//二进制形式写到文件中
fcloase(pf);
pf = NULL;
return 0;
}
文件读取结束判定:
被错误使用的feof
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件是否结束。
而是应用于当文件读取结束时,判断是读取失败结束,还是遇到文件尾结束。
1.文本文件读取时否结束,判断返回值是否为EOF(fgetc),或NULL fgets
例如:
fgetc判断是否为EOF
2.二进制文件读取结束判断,判断返回值是否小于实际要读个数,例如:fread判断返回值是否小于实际要读个数
#include <stdio.h>
#include <windows.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);
//先将代码放在输出缓冲区
printf("睡眠10s写好数据,打开头文件,发现无内容\n");
Sleep(10000);
printf("刷新缓冲区");
fflush(pf);
//刷新缓冲区时,才会将输出缓冲区的数据写到文件
printf("在睡眠10s,打开,有内容");
Sleep(10000);
fclose(pf);
//在屏蔽文件时,也会刷到新缓冲区
pf = NULL;
return 0;
}
结论:因为有缓冲区的存在,C语言在操作文件时,需要做刷新缓冲区或在文件操作结束时关闭文件,若不做可能导致读写文件的问题。
程序环境分为两个:翻译和执行
编译器:cl.exe
链接器:link.exe
预处理的操作:
1.头文件的包含
2.define定义符号的替换
3.注释
文本操作
编译操作
gcc test.i-s
生成 test.s
把C语言代码转换成汇编代码
语法分析一词法分析——符号汇总——语义分析
汇编操作:
gcc test.s ——生成了test.
把汇编代码转成二进制指令,形成符号表