一、结构体
结构体定义由关键字 struct
和结构体名组成,结构体名可以根据需要自行定义。
struct tag {
member-list
member-list
member-list
...
} SnaxName1,SnaxName2 ;
tag
是结构体标签,SnaxName
是结构体变量名称,两者只可省略一个,SnaxName
可直接当变量使用。
1.结构体大小的计算
结构体内会要求内存对齐,具体要求如下:
-
第一个成员的首地址为0.
-
每个成员的首地址是自身大小的整数倍
-
结构体的总大小,为其成员中所含最大类型的整数倍。
例如:
struct S1
{
char a; //1+3
int b; //4
short c; //2+2
}s1;
//S1的大小为12字节,为4的倍数
struct S2
{
char d[7]; //7+1
double a; //8
short b; //2+2
char* c;//32位系统,指针4字节 4
}s2;
//S2的大小为24,为8的倍数
2、共用体
union
是一个重要的复合数据类型,允许在相同的内存位置存储不同类型的数据。与struct
不同,共用体中的所有成员共享同一块内存,这意味着在某一时刻,只有一个成员有效。访问方式与结构体相似。
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
一个variables
中的所有member
共享同一块内存,不同的variables
不共享一块内存。
3.位域
位域(Bit-Field)是一种特殊的结构体成员,它允许你定义一个特定数量的位来存储数据,从而有效地节省内存空间。位域不能跨字节
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
//结构体在有参情况下,占用最小为最大成员变量类型所占字节大小
//unsigned int 占4字节,但只取了1位,第一个补齐空闲位占1字节,由于结构体需要为4的倍数,第二个补齐空闲位占3字节。
struct area
{
char a:6; //向int对齐,扩展为32位
char b:3;
int c:20;
};
//a占1字节 c起始地址需要为4的倍数,所以b占1+2字节。总字节应为4最大成员的变量类型(int)字节的倍数,所有C占4字节。总占8字节。
4.typedef
typedef
关键字,您可以使用它来为类型取一个新的名字。例如:
typedef unsigned char BYTE;
typedef unsigned char byte;
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
声明成员时,
Book
相当于 struct Books
,声明成员时,可使用Book xx;
创建。
typedef vs #define
typedef
仅限于为类型定义符号名称,#define
不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。typedef
是由编译器执行解释的,#define
语句是由预编译器进行处理的。
二、文件操作
C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。
1.打开文件
使用 fopen( )
函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE
的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。
FILE *fopen( const char *filename, const char *mode );
filename
是字符串,用来命名文件,访问模式 mode
的值可以是下列值中的一个:
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。如果文件存在,则该文件会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
2.关闭文件
使用 fclose( )
函数关闭文件。
int fclose( FILE *fp );
如果成功关闭文件,fclose( )
函数返回零,如果关闭文件时发生错误,函数返回 EOF
。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF
是一个定义在头文件 stdio.h
中的常量。
3.写入文件
写入文件可以使用以下三个函数:
int fputc( int c, FILE *fp );//写入单个字符
int fputs( const char *s, FILE *fp );//写入字符串
int fprintf(FILE *fp,const char *format, ...)//写入字符串
4.读取文件
读取文件使用以下三个函数:
int fgetc( FILE * fp );//写入单个字符
char *fgets( char *buf, int n, FILE *fp );//写入字符串
int fscanf(FILE *fp, const char *format, ...) //写入字符串
fgets()
从 fp
所指向的输入流中读取 n - 1
个字符。它会把读取的字符串复制到缓冲区 buf
,并在最后追加一个 null
字符来终止字符串,函数返回值为读取到的字符。
如果fgets()
在读取最后一个字符之前就遇到一个换行符 '\n'
或文件的末尾 EOF
,则只会返回读取到的字符。
fscanf()
遇到第一个空格和换行符时,停止读取。
读取文件后,指针会移动,再次读取时不会从起始位置读取。
预宏定义
ANSI C 定义了许多宏。在编程中您可以使用这些宏,但是不能直接修改这些预定义的宏。
宏 | 描述 |
---|---|
__DATE__ | 当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量。 |
__TIME__ | 当前时间,一个以 “HH:MM:SS” 格式表示的字符常量。 |
__FILE__ | 这会包含当前文件名,一个字符串常量。 |
__LINE__ | 这会包含当前行号,一个十进制常量。 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义 |
头文件
有两种引用方式
#include <file> //这种形式用于引用系统头文件。
#include "file" //这种形式用于引用用户头文件。
三、内存管理
内存是通过指针变量来管理的。指针是一个变量,它存储了一个内存地址,这个内存地址可以指向任何数据类型的变量,包括整数、浮点数、字符和数组等。
序号 | 函数和描述 |
---|---|
1 | void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。 |
2 | void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
3 | void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
4 | void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize 。 |
5 | void *alloc(size_t size); 从栈中分配一块内存,无法释放,和 malloc 不同的是,alloc 并不会显式初始化内存,并且它可能是库或自定义函数。 |