IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言:近期所学知识汇总(4) -> 正文阅读

[C++知识库]C语言:近期所学知识汇总(4)

C语言:近期所学知识汇总(4)

数据类型专题

1.计算机中的数据按照补码形式存储,补码是原码按位取反加一得到。之所以采用补码是为了配合cpu的特性,因为cpu只能进行加法、按位取反、位移这三种逻辑。

2.C语言是一种强类型语言,变量的类型一旦确定就不能再被更改。

3.在C语言中,类型有自己的长度。不加任何前缀修饰时,默认是signed(有符号)类型,最高位代表符号位;当前缀关键字unsigned(无符号类型时),最高位就代表数值位。下面的程序展示了有符号和无符号之间的差别:

int main() {
	for (char i = 0;i < 128;i++) {
		printf("%4d", i);
	}
	return 0;
}

由于char类型的值始终介于-128~127之间,所以循环将无限执行。

int main() {
	for (unsigned char i = 0;i < 128;i++) {
		printf("%4d", i);
	}
	return 0;
}

由于将char定义成了无符号类型,所以它可以表示128,所以循环将会在循环128次后退出。

4.无符号和有符号的区别更多体现在类型转换方面。C语言的类型转换中,长度短的类型转换成长度长的类型时,需要进行位扩充,对于有符号数来说,扩充的应该是其符号位,之所以要扩充符号位,是因为要保证扩充后的数值正负和大小与原来保持不变;而对于无符号数来说,扩充的均为零。比如下面的程序段:

int main() {
	char c = -5;
	unsigned int a = 10;
	if (c > a) {
		printf("%d > %d", c, a);
	}
	else {
		printf("%d < %d", c, a);
	}
	return 0;
}

程序结果输出-5 > 10,这显然是不符合逻辑的,但是事实上这是类型转换造成的结果。变量c的二进制表示为1111 1011(-5的补码形式),在进行c > a时进行了隐式类型转换,将c的类型转换成了unsigned int类型,所以要进行位扩充。由于c是char类型,扩充符号位,所以扩充后用十六进制表示0xfffffffd,而a用十六进制表示为0x0000000a,对于无类型来说最高位不是符号位,0xfffffffd远远大于0x0000000a,所以最后输出时打印出-5 > 10的情况。由于有隐式类型转换的存在,所以在编程的时候,不要将有符号数和无符号数混用,这样会造成一些严重的问题,比如下面的程序:

int main() {
	for (int i = -4;i < sizeof(int);i++) {
		printf("%d ", i);
	}
	return 0;
}

程序将什么也不打印。因为sizeof(int)的返回值是一个无符号整型,所以i将会被进行隐式类型转换。i的十六进制表示为0xfffffffc,如果这是一个有符号数,那么它将是一个负数,但是此时i与一个无类型数进行比较,从int隐式转换成了unsigned int,所以i就变成了一个非常大的数,远远大于sizeof(int)的值,所以循环条件不满足,循环体未执行。

类型转换例题:

int main() {
	char c = 128;
	unsigned char uc = 128;
	unsigned short us = 0;

	us = c + uc;
	printf("%x \n", us);
	//  c = 1000 0000 -> 1111 1111 1000 0000
	//uc = 1000 0000 -> 0000 0000 1000 0000
	//                       us = 0000 0000 0000 0000  

	us = (unsigned char)c + uc;
	printf("%x \n", us);
	//  c = 1000 0000 -> 0000 0000 1000 0000
	//uc = 1000 0000 -> 0000 0000 1000 0000
	//                       us = 0000 0001 0000 0000     

	us = c + (char)uc;
	printf("%x \n", us);
	//  c = 1000 0000 -> 1111 1111 1000 0000
	//uc = 1000 0000 -> 1111 1111 1000 0000
	//                       us = 1111 1111 0000 0000

	us = (unsigned short)c + uc;
	printf("%x \n", us);
	//  c = 1000 0000 -> 1111 1111 1000 0000
	//uc = 1000 0000 -> 0000 0000 1000 0000
	//                       us = 0000 0000 0000 0000

	return 0;
}

需要注意,类型转换规则遵守的是数据原本类型的转换规则,而不是转换后类型的转换规则。

文件专题

文件的概念:一般指存储在外部介质上数据的集合,比如txt,tmp,jpg,exe等,C语言中将输入输出设备也抽象承一种“文件”。我们可以通过数据流向文件中写入或者读出数据。

流的概念:I/O设备型号众多而且标准不一,要想访问它们十分麻烦,所以我们将这些种类繁多的设备统一抽象成了“标准I/O设备”,程序绕过具体的设备,而与“标准I/O设备”进行交互,这样就可以做到不依赖与任何具体I/O设备的统一操作接口。我们将抽象出来的“标准I/O设备”或者“标准文件”称为“流”。对于将任意I/O设备转换为“标准I/O设备”的过程是由系统自动完成,不需要程序员处理。可以认为,任意输入的源端或者任意输出的终端均对应一个“流”。

文件包含三个要素:文件路径,文件名称,文件后缀。

预定义的三个标准流:stdin(标准输入流),stdout(标准输出流),stderr(标准错误流)

文件类型FILE:对象类型,包含I/O流所需的全部信息。

文件操作的三个步骤:打开文件,读写文件,关闭文件。下面分别介绍这三个过程。
1.打开文件
要想对文件进行操作,首先要将文件打开,使用fopen函数达到这个目标。但是在VS2019中原来的fopen函数禁用,改用更安全的fopen_s函数。
该函数接受三个参数,分别是文件指针、文件名、访问类型。如果打开失败,返回errno_t(int)值,用户可以通过返回的值来得知错误类型。访
问类型常用的有以下几种:

读取方式含义
r只读。文件必须存在才可以读取。
w只写。文件如果已经存在,则清除原文件重新写入;如果文件不存在,则创建新文件写入。
a末尾只写。文件如果已经存在,则在原文件的末尾继续写入;如果文件不存在,则创建新文件写入。
rb二进制文件只读。功能同“r”,以二进制模式打开。
wb二进制文件只写。功能同“w”,以二进制模式打开。
ab二进制文件末尾只写。功能同“a”,以二进制模式打开。

2.关闭文件
对文件操作完成之后应当关闭文件,使用fclose函数完成。该函数接受一个参数即文件指针,和动态内存分配中的free函数相似,在调用完fclose函数后,
应当将文件指针置为空值,原理与调用free函数之后将指向堆区内存的指针置为空值相似。
3.读写文件
3.1无格式输入输出

int main() {
	//putchar
	//puts
	const int n = 20;
	char ch = 'a';
	char stra[n] = { "hdc hello" };
	putchar(ch);// printf("%c", ch);
	putchar('\n');
	puts(stra);// prtintf("%s", stra);
	puts("\n");
	return 0;
}

将字符或者字符串送进标准输出流中。

int main() {
	//gets_s
	const int n = 100;
	char stra[n] = {};
	//scanf_s("%s", stra, n);//容纳n-1个有效字符,将空格视为结束符
	//printf("%s", stra);
	gets_s(stra, n);//容纳n-1个有效字符,将换行符视为结束符
	printf("%s", stra);
	return 0;
}

从标准输入流中获取字符串。

int main() {
	//getchar
	char ch = '\0';
	//scanf_s("%c", &ch);//stdin
	ch = getchar();//stdin

	return 0;
}

从标准输入流中获取一个字符。

int main(int argc, char* argv[]) {
	//fgetc 
	//fputc
	char ch = '\0';
	if (argc < 3) {
		printf("copy file error \n");
		exit(EXIT_FAILURE);
	}
	FILE* fr, * fw;
	errno_t rx = fopen_s(&fr, argv[1], "r");
	errno_t wx = fopen_s(&fw, argv[2], "w");
	if (fr == nullptr || fw == nullptr) {
		printf("open file error \n");
		exit(EXIT_FAILURE);
	}
	while (!feof(fr)) {
		ch = fgetc(fr);
		fputc(ch, fw);
		putchar(ch);
	}
	fclose(fr);
	fr = nullptr;
	fclose(fw);
	fw = nullptr;
}

将一个文件中的内容拷贝到另外一个文件当中,以读方式打开一个文件,通过fgetc函数获取其字符,然后以写方式打开另一个文件,通过fputc函数将刚才获得的字符送进去,当读文件指针到达文件末尾的时候,拷贝结束。

int main() {
	//fgets
	//fputs
	FILE* fpr = nullptr;
	FILE* fpw = nullptr;
	errno_t ersa = fopen_s(&fpr, "Test7_10.cpp", "r");
	if (fpr == nullptr) {
		printf("fopen file error %d", ersa);
		exit(1);
	}
	errno_t ersb = fopen_s(&fpw, "D:\\VS2019程序代码\\Test7_10\\Test7_12_copy.cpp", "w");
	if (fpw == nullptr) {
		printf("fopen file error %d", ersb);
		exit(1);
	}
	char buff[10];
	while (!feof(fpr)) {
		fgets(buff, 10, fpr);
		fputs(buff, fpw);
	}
	fclose(fpr);
	fpr = nullptr;
	fclose(fpw);
	fpw = nullptr;

}

使用fgets函数读取Test7_10.cpp中的内容进入缓冲区,然后使用fputs函数将缓冲区的数据写入另一个文件。当读文件指针到达文件末尾的时候,拷贝结束。
3.2文本文件的写入和读取
下面的实例是通过SaveData函数创建并保存了一个txt文本文件,然后通过LoadData函数读取文件中的数值并打印至屏幕上。

void LoadData() {
	int ar[10];
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "hdc.txt", "r");
	if (fp == nullptr) {
		printf("open file error %d", ers);
		exit(1);
	}
	for (int i = 0;i < 10;i++) {
		fscanf_s(fp, "%d", &ar[i]);//从文件流读取数据
		printf("%d ", ar[i]);
	}
	fclose(fp);
	fp = nullptr;
}
void SaveData() {
	int ar[] = { 12,23,34,45,56,67,78,89,90,100 };
	int n = sizeof(ar) / sizeof(ar[0]);
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "hdc.txt", "w");
	if (fp == nullptr) {
		printf("open file error %d", ers);
		exit(1);
	}
	for (int i = 0;i < n;i++) {
		fprintf_s(fp, "%d ", ar[i]);//将数据写入到文件流
	}
	fclose(fp);
	fp = nullptr;
}
int main() {
	SaveData();
	LoadData();
	return 0;
}

第二个实例与第一个类似,区别是在文件的开头保存了文件中数据的个数,这样做的话在读取文件的时候,读取了文件中第一个值之后就可以知道文件的数据个数。

void LoadData() {
	int ar[100];
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "data.txt", "r");
	if (fp == nullptr) {
		printf("fopen file error %d", ers);
		exit(1);
	}
	int n = 0;
	fscanf_s(fp, "%d", &n);
	for (int i = 0;i < n;i++) {
		fscanf_s(fp, "%d", &ar[i]);
		printf("%d ", ar[i]);
	}
	fclose(fp);
	fp = nullptr;
}
void SaveData() {
	int ar[] = { 12,23,34,45,56,67,78,89,90,100,90,89,78,67,56,45,34,23 };
	int n = sizeof(ar) / sizeof(ar[0]);
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "data.txt", "w");
	if (fp == nullptr) {
		printf("fopen file error %d", ers);
		exit(1);
	}
	fprintf_s(fp, "%d\n", n);
	for (int i = 0;i < n;i++) {
		fprintf_s(fp, "%d ", ar[i]);
	}
	fclose(fp);
	fp = nullptr;
}

int main() {
	SaveData();
	LoadData();
	return 0;
}

3.3二进制文件的写入和读取
相对于文本文件,二进制文件的读写分别采用fread和fwrite函数实现,其中fwrite接受四个参数,指向缓冲区的指针、写入数据的类型,写入数据的个数以及文件指针;fread函数接受四个参数,指向缓冲区的指针、读取数据的类型、读取数据的个数以及文件指针。

void LoadData() {
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "data.bin", "rb");
	if (fp == nullptr) {
		printf("fopen file error %d", ers);
		exit(1);
	}
	int n = 0;
	fread(&n,sizeof(int), 1, fp);
	int* ar = (int*)malloc(sizeof(int) * n);
	if (ar == nullptr)exit(1);
	fread(ar,sizeof(int), n, fp);
	for (int i = 0;i < n;i++) {
		printf("%5d", ar[i]);
	}
	free(ar);
	ar = nullptr;
	fclose(fp);
	fp = nullptr;
}
void SaveData() {
	int ar[] = { 12,23,34,45,56,67,78,89,90,100,90,89,78,67,56,45,34,23 };
	int n = sizeof(ar) / sizeof(ar[0]);
	FILE* fp = nullptr;
	errno_t ers = fopen_s(&fp, "data.bin", "wb");
	if (fp == nullptr) {
		printf("fopen file error %d", ers);
		exit(1);
	}
	fwrite(&n, sizeof(int), 1, fp);
	fwrite(ar, sizeof(int), n, fp);
	fclose(fp);
	fp = nullptr;
}
int main() {
	SaveData();
	LoadData();
	return 0;
}

文件位置相关函数介绍
ftell:接受文件指针,返回当前文件位置指示值。
fgetpos:与ftell函数功能相似,功能是获取文件位置指示器。文件指针的位置是由专门的fpos_t类型变量给出。
下面的实例会在每次读取一个数据后打印文件指针的位置,可以看出,由于使用文本文件读写,读入的数据都被当作字符类型存储,这样的结果是随着数据的读取,文件位置的增加是没有规律的。

void LoadData() {
	int val;
	const int n = 10;
	FILE* fr = nullptr;
	errno_t rx = fopen_s(&fr, "hdc.txt", "r");
	if (fr == nullptr) {
		printf("open file error\n");
		exit(EXIT_FAILURE);
	}
	fpos_t ps;//int pos = ftell(fr);
	fgetpos(fr, &ps);
	while (!feof(fr)) {
		fscanf_s(fr, "%d", &val);
		printf("val = %d ", val);
		fgetpos(fr, &ps);//pos = ftell(fr);
		printf("ps = %lld \n", ps);//printf("pos = %d \n", pos);
	}
	fclose(fr);
	fr = nullptr;
}
void SaveData() {
	FILE* fw = nullptr;
	const int n = 10;
	int ar[n] = { 12,23,34,45,56,1,234,2345,6543,231 };
	errno_t wx = fopen_s(&fw, "hdc.txt", "w");
	if (fw == nullptr) {
		printf("open file error\n");
		exit(EXIT_FAILURE);
	}
	for (int i = 0;i < n;i++) {
		fprintf(fw, "%d ", ar[i]);
	}
	fclose(fw);
	fw = nullptr;
}
int main(){
	SaveData();
	LoadData();
	return 0;
}

第二个实例指出,如果以二进制文件写如,那么数据在内存中是以什么形式存储的,那么在文件中也是以什么形式存储。所以,随着读取数据的进行,文件位置的增加是均匀的。

void LoadData() {
	FILE* fr = nullptr;
	const int n = 10;
	int val;
	errno_t rx = fopen_s(&fr, "hdc.bin", "rb");
	if (fr == nullptr) {
		printf("open file error\n");
		exit(EXIT_FAILURE);
	}
	int pos = ftell(fr);
	for (int i = 0;i < n;i++) {
		fread(&val, sizeof(int), 1, fr);
		pos = ftell(fr);
		printf("val = %d pos = %d\n", val, pos);
	}
	fclose(fr);
	fr = nullptr;
}
void SaveData() {
	FILE* fw = nullptr;
	const int n = 10;
	int ar[n] = { 12,23,34,45,56,1,234,2345,6543,231 };
	errno_t wx = fopen_s(&fw, "hdc.bin", "wb");
	if (fw == nullptr) {
		printf("open file error\n");
		exit(EXIT_FAILURE);
	}
	fwrite(ar, sizeof(int), n, fw);
	fclose(fw);
	fw = nullptr;
}
int main(){
	SaveData();
	LoadData();
	return 0;
}

fseek:将文件位置指示符移动到指定位置。
fsetpos:将文件位置指示器移动到指定位置。
rewind:将文件位置指示器移动到文件首。
以下实例通过fseek函数将文件位置指示符移动到文件尾部,然后使用ftell可以得到文件的总字节数,向堆区动态申请对应空间,然后通过rewind函数将文件位置指示器移动到文件首,最后使用fread函数读取文件并打印值屏幕上。

int main() {
	FILE* fr = nullptr;
	errno_t err = fopen_s(&fr, "Test7_19.cpp", "rb");
	if (fr == nullptr) {
		printf("open file error\n");
		exit(EXIT_FAILURE);
	}
	//fseek(fr, n, SEEK_END);//设置到末尾,向前偏移n字节
	//fseek(fr, n, SEEK_SET);//设置到开头,向后偏移n字节
	//fseek(fr, n, SEEK_CUR);//设置到当前位置,向后偏移n字节
	fseek(fr, 0, SEEK_END);
	int len = ftell(fr);
	char* buff = (char*)malloc(sizeof(char) * len + 1);//预留一个结束符空间
	if (buff == nullptr)exit(1);
	rewind(fr);//fseek(fr, 0, SEEK_SET);
	fread(buff, sizeof(char), len, fr);
	buff[len] = '\0';
	printf("%s", buff);
	fclose(fr);
	fr = nullptr;
	return 0;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:15:53  更:2021-07-24 11:17:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 18:39:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码