- C 语言进阶
- 一、 数据类型
- 1、枚举
- 2、 结构体
- 3、 共用体
- 4、 别名
- 二、 C语言预处理器
- 1、 概念
- 2、 预处理器运算符
- 3、参数化的宏
- 三、 文件读写
- 1、 打开文件
- 2、关闭文件
- 3、 写入文件
- 4、 读取文件
- 5、 实例
- 6、 二进制数据
- 一、 数据类型
枚举是C中的一种基本数据类型,它可以让数据更语言,更易读。
#include <stdio.h>
#include <stdlib.h>
typedef enum
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
}Day;
/*
也可以这样定义
enum Day {} day;
typedef enum Day {} day;
enum {} day;
*/
int main() {
Day day;
day = MON;
printf_s("%d\n", day); // 访问枚举类型数据
for (day = MON; day <= SUN; day++)
{
// 遍历枚举类型数据
printf_s("%d\n", day);
}
system("pause");
return 0;
}
2、 结构体注意:
第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
可以在定义的时候改变枚举的值,后续成员的值在前一个成员的基础上加1
C 数组允许定义可存储相同类型数据项的变量,结构体是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。
基本使用:
#include <stdio.h>
#include <stdlib.h>
struct Stu
{
char* name; // 名字
unsigned int id; // 学号
unsigned int age; // 年龄
void(*play)(); // 行为
};
void play_(struct Stu* stu) {
printf_s("%s小盆友在玩游戏\n", stu->name); // 通过地址访问成员里面的内容
}
int main() {
struct Stu stu = { "李华", 01, 18, play_ }; // 创建一个成员
stu.play(stu); // 访问成员里面的行为
system("pause");
return 0;
}
3、 共用体创建结构体的其他方法:
struct Stu { char* name; unsigned int id; unsigned int age; void(*play)(); }stu={ "李华", 01, 18, play_ }; // 创建的同时也创建一个成员 typedef struct Stu { char* name; unsigned int id; unsigned int age; void(*play)(); }stu; typedef struct { char* name; unsigned int id; unsigned int age; void(*play)(); }Stu;
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
共用体的定义和访问方法和结构体类似
#include <stdio.h>
#include <stdlib.h>
union Data
{
int i;
float f;
char str[20];
};
int main() {
union Data data1;
// 同时赋值时,发现有数据损失
data1.i = 10;
data1.f = 20;
printf("%d\n%f\n", data1.i, data1.f);
// 分开赋值时,没有精度的额损失
union Data data;
data.i = 20;
printf("%d\n", data.i);
data.f = 10;
printf("%f\n", data.f);
system("pause");
return 0;
}
4、 别名现在,Data类型的变量可以存储一个整数、一个浮点数,或者一个字符串。这意味着一个变量(相同的内存位置)可以存储多个多种类型的数据。可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。
共用体占用的内存应足够存储共用体中最大的成员。例如,在上面的实例中,Data 将占用 20 个字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。
C 语言提供了typedef
关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE:
typedef unsigned char BYTE;
typedef unsigned int size_t;
也可以使用 typedef
来为用户自定义的数据类型取一个新的名字
typedef struct
{
char* name;
unsigned int id;
unsigned int age;
void(*play)();
} Stu;
二、 C语言预处理器
1、 概念
C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:
实例,定义bool数据类型:
#include <stdlib.h>
#include <stdio.h> // 引入输入输出流的头文件
#ifndef bool
typedef int bool;
#define true 1
#define false 0
#endif
2、 预处理器运算符
-
宏延续运算符(\)
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。例如:
#define message_for(a, b) \ printf(#a " and " #b ": We love you!\n")
-
字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如:
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main(void) { message_for(Carole, Debra); return 0; }
-
标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:
#include <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main(void) { int token34 = 40; tokenpaster(34); return 0; }
#include <stdlib.h>
#include <stdio.h>
#define MAX(x,y) (x>y?x:y) // 获取最大值的函数
int main() {
int ret = MAX(1, 2);
printf("%d\n", ret);
system("pause");
return 0;
}
三、 文件读写
1、 打开文件
您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
FILE *fopen_s(FILE** fp, const char *filename, const char *mode);
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
2、关闭文件
int fclose(FILE *fp);
3、 写入文件
int fputc( int c, FILE *fp ); // 把字符写入文件中
int fputs( const char *s, FILE *fp ); // 把字符串写入文件中
int fprintf_s(FILE *fp,const char *format, ...) // 也可以使用这个将格式化字符串写入文件中
4、 读取文件
int fgetc(FILE * fp); // 从文档中读取一个字符
char *fgets( char *buf, int n, FILE *fp ); // 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串
int fscanf_s(FILE *fp, const char *format, ...) //函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取
5、 实例
#include <stdio.h>
int main(void)
{
FILE* fp;
fopen_s(&fp, "a.txt", "w"); // 以写入的形式打开文件
fprintf_s(fp, "%d+%d=%d", 1, 2, 3); // 将内容写入文件中
fclose(fp); // 关闭文件
fopen_s(&fp, "a.txt", "r"); // 以只读的形式打开文件
char p[255];
// fgets(p, 255, fp);
fscanf_s(fp, "%s", p, 255); // 主要,要输入最大接收的符号数
printf_s("%s\n", p);
fclose(fp); // 关闭文件
return 0;
}
6、 二进制数据
size_t fread(void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file); // 输出
size_t fwrite(const void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file); // 写入
参数说明:
ptr
:要将内容读取或写入的指针首地址size_of_elements
:允许输入或输出的元素的大小,bitnumber_of_elements
:允许输入或输出的元素的个数
#include<stdio.h>
int main()
{
// 文件指针fp
FILE* fp = NULL;
unsigned __int8 a[10];
unsigned __int8 b[10];
int i;
for (i = 0; i < 10; i++)
{
a[i] = 0xff;
}
// 写入二进制文件
// 文件名:"test.bin", 访问方式:"wb"
fopen_s(&fp, "test.bin", "wb");
// 数据块首地址: "&a",元素大小: "sizeof(unsigned __int8)", 元素个数: "10", 文件指针:"pd"
fwrite(&a, sizeof(unsigned __int8), 10, fp);
fclose(fp);
// 读取二进制文件
// 文件名:"test.bin", 访问方式:"rb"
fopen_s(&fp, "test.bin", "rb");
// 数据块首地址: "&b",元素大小: "sizeof(unsigned __int8)", 元素个数: "10", 文件指针:"pd"
fread(&b, sizeof(unsigned __int8), 10, fp);
for (i = 0; i < 10; i++)
{
printf("b[%d] = 0x%x\n", i, b[i]);
}
fclose(fp);
}