[C完结] 童年回忆杀 [源代码] 终点亦是起点

源.c文件: 

#define _CRT_SECURE_NO_WARNINGS
来深入一下长度不受限制的字符串函数
//1. strlen字符串长度函数  
//#include<stdio.h>
//#include<string.h>
//int main() {
//	if (strlen("abc") - strlen("abcdef") > 0) {
//		printf(">\n");//返回值是无符号类型   size_t(unsigned int)     所以打印结果是>
//	}
//	else {
//		printf("<\n");
//	}
//	return 0;
//}

//  模拟实现strlen函数
//#include<stdio.h>
//int my_strlen(const char* array) {//计算变量的长度 不修改 const
//	assert(array != NULL);//让代码更加健康
//	int count = 0;//计数器实现strlen
//	while (*array != '\0') {
//		count++;
//		array++;
//	}
//	return count;
//}
//int main() {
//	char array[] = "abc";
//	int much = my_strlen(array);
//	printf("%d\n", much);
//	return 0;
//}

//2.  strcpy拷贝函数    string字符串   copy拷贝
//#include<stdio.h>
// #include<string.h>
//int main() {
//	char array[20] = "###################";
//	//strcpy(array, "hello");//这么写没毛病  但有点小别扭
//	char array_one[] = "hello";
//	strcpy(array, array_one);
//	printf("%s\n", array);//调试你会发现  '\0'  也拷贝进去了
//	return 0;
//}

//3.  strcat字符串追加  追加时先找到目标文件的  '\0'  再把源数据追加进去
//#include<stdio.h>
//#include<string.h>
//int main() {
//	char array1[30] = "hello \0#########";
//	char array2[] = "world";
//	strcat(array1, array2);
//	printf("%s\n", array1);//调试你会发现  '\0'  也追加进去了  并且在 \0 后面追加的
//	return 0;
//}

//模拟实现  strcat
//#include<stdio.h>
//#include<assert.h>
//char* my_strcat(char* array, const char* array1) {//追加又不修改  更加健康
//	char* start = array;//追加函数完成后会返回目标文件起始地址
//	assert(array && array1);//断言一下 预防空指针
//	//1.找到目标的  '\0'
//	while (*array) {
//		array++;
//	}
//	//2.追加字符串,包含  '\0'
//	while (*array++ = *array1++) {
//		;
//	}
//	return start;
//}
//int main() {
//	char array[20] = "hello";
//	char array1[] = "world";
//	printf("%s\n", my_strcat(array, array1));
//	return 0;
//}

//4. 模拟实现字符串比较函数  strcmp
//#include<stdio.h>
//#include<assert.h>
//int my_strcmp(const char* array1,const char* array2) {//  strcmp函数返回大于或小于 或等于的数字
//	assert(array1 && array2);//健康
//	while (*array1 == *array2) {
//		if (*array1 == '\0') {
//			return 0;
//		}
//		array1++;
//		array2++;
//	}
//	if (*array1 > *array2) {
//		return 1;
//	}
//	else {
//		return -1;
//	}
//}
//int main() {
//	char* array1 = "abcde";
//	char* array2 = "able";
//	int number = my_strcmp(array1, array2);
//	if (number > 0) {
//		printf("array1 > array2\n");
//	}
//	else if (number < 0) {
//		printf("array1 < array2\n");
//	}
//	else {
//		printf("array1 = array2\n");
//	}
//	return 0;
//}


//长度受限制的字符串函数  相对较于安全  可控
//1. strncpy拷贝函数
//#include<stdio.h>
//  #include<string.h>
//int main() {
//	char array1[20] = "abcdef";
//	char array2[] = "ghk";
//	strncpy(array1, array2, 5);//不够五个  将\0拷贝过去
//	printf("%s\n", array1);
//	return 0;
//}
//2.strncat 追加函数
//#include<stdio.h>
// #include<string.h>
//int main() {
//	char array1[30] = "hello \0##############";
//	char array2[] = "world";
//	strncat(array1, array2, 8);//   /0也追加过去
//	printf("%s\n", array1);
//	return 0;
//
//}

//3.strncmp  你想比较几个字符就比较几个字符


//模拟实现strstr函数  在array1中查找是否有array2数组
//#include<stdio.h>
//#include<string.h>
//#include<assert.h>
//char* my_strstr(const char* array1, const char* array2) {//只找到不修改
//	assert(array1 && array2);
//	const char* point1 = NULL;
//	const char* point2 = NULL;
//	char* start = array1;
//	if (*array2 == '\0') {
//		return array1;
//	}
//	while (*start) {
//		point1 = start;
//		point2 = array2;
//		while (*point1 && *point2 && (*point1 == *point2)) {
//			point1++;
//			point2++;
//		}
//		if (*point2 == '\0') {
//			return start;
//		}
//		start++;
//	}
//	return NULL;
//}
//int main() {
//	char array1[] = "abcdefabcdef";
//	char array2[] = "cdef";
//	char* point = my_strstr(array1, array2);
//	if (point == NULL) {
//		printf("没找到\n");
//	}
//	else {
//		printf("找到了:%s\n", point);
//	}
//	return 0;
//}


//strtok字符串切割函数  找到标记会用\0结尾,返回一个指向这个标记的指针
//#include<stdio.h>
//#include<string.h>
//int main() {
//	char array[] = "110120@qq.com";
//	char* point = "@.";
//
//	char alternatives[20] = { 0 };
//	strcpy(alternatives, array);//strtok函数会改变被操作字符串,所以使用它切分的字符串一般都是临时拷贝的内容并可以修改
//	char* point1 = NULL;
//
//	//point = strtok(alternatives, point);
//	//printf("第一次切割 %s\n", point);
//
//	//point = strtok(NULL, point);//strtok函数第一个函数为NULL,则在标记处保存的位置开始,找下一个标记
//	//printf("第二次切割 %s\n", point);
//
//	//point = strtok(NULL, point);
//	//printf("第三次切割 %s\n", point);//如果字符串不存在更多标记,返回NULL指针
//
//	for (point1 = strtok(alternatives, point); point1 != NULL; point1 = strtok(NULL, point)) {
//		printf("%s\n", point1);
//	}
//
//	return 0;
//}

//使用库函数的时候,调用失败都会有错误码  errno
//strerror  错误码转换为错误信息
//#include<stdio.h>
//#include<string.h>//strerror头文件
//#include<errno.h> //errno头文件  全局错误码
//#include<stdio.h> //perror头文件
//
//int main() {
//
//	//printf("%s\n", strerror(0));      // No error
//	//printf("%s\n", strerror(1));     //Operation not permitted
//	//printf("%s\n", strerror(2));     //No such file or directory
//	//printf("%s\n", strerror(3));     //No such process
//	//printf("%s\n", strerror(4));     //Interrupted function call
//	//printf("%s\n", strerror(5));	 //Input / output error
//
//	FILE* point = fopen("test.txt", "r");//打开文件 读的形式打开  打开失败会返回NULL指针
//	if (point == NULL) {
//		/*printf("%s\n", strerror(errno));*/
//		perror("fopen");//   错误码转化为错误信息,打印错误信息  一步到位
//		return 1;//调用失败就不用往下走了 程序结束
//	}
//	fclose(point);//关闭文件
//	point = NULL;
//	return 0;
//}


//讲一下字符分类函数
//#include<stdio.h>
//#include<ctype.h> //isdigit头文件  
//int main() {
//	char judge = '#';
//	int number = isdigit(judge);//isdigit 如果是数字字符0-9返回非零值  不是返回0
//	printf("%d\n", number);
//	return 0;
//}

//字符转换函数
//#include<stdio.h>
//#include<ctype.h>//isupper大写字母字符分类函数头文件
//int main() {
//	char array[20] = { 0 };
//	scanf("%s", array);
//	int frequency = 0;
//	while (array[frequency] != '\0') {
//		if (isupper(array[frequency])) {
//			array[frequency] = tolower(array[frequency]);//tolower将大写转换为小写
//		}
//		printf("%c ", array[frequency]);
//		frequency++;
//	}
//	return 0;
//}

//内存函数
//内存拷贝  memcpy  拷贝不重叠的内存  VS中重不重叠都可以拷贝   memory记忆在程序中为内存  cpy(copy)复制,拷贝
//         memmove函数可以处理重叠内存
//#include<stdio.h>
//#include<string.h>//memcpy头文件
//#include<assert.h>
//void* my_memcpy(void* target, const void* source, size_t number) {// 我不知道内存拷贝的类型  用void*   ; size_t无符号整数
//	void* start = target;
//	assert(target && source);
//	while (number--) {
//		*(char*)target = *(char*)source;
//		target = (char*)target + 1;
//		source = (char*)source + 1;
//	}
//	return start;
//} 
//void* my_memmove(void* target, const void* source, size_t number) {
//	void* start = target;
//	assert(target && source);
//	if (target < source) {//前->后
//		while (number--) {
//			*(char*)target = *(char*)source;
//			target = (char*)target + 1;
//			source = (char*)source + 1;
//		}
//	}
//	else {//后->前
//		while (number--) {
//			*((char*)target + number) = *((char*)source + number);
//		}
//	}
//	return start;
//}
//int main() {
//	/*int array1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//	int array2[10] = { 0 };
//	memcpy(array2, array1, 20);*/
//
//	//模拟实现
//	int array1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//	int array2[10] = { 0 };
//	//my_memcpy(array2, array1, 20);
//	//my_memcpy(array1+2, array1, 20);//自己写的不能拷贝重叠的内存
//
//	//memmove(array1 + 2, array1, 20);
//	//模拟实现memmove
//	my_memmove(array1 + 2, array1, 20);
//	return 0;
//}

//memcmp 内存比较   memset内存设置
//#include<stdio.h>
//#include<string.h>//内存函数头文件
//int main() {
//	int array1[] = { 1, 2, 3, 4, 5 };
//	int array2[] = { 1, 1, 4 };
//	int judge = memcmp(array1, array2, 8);//  和strcmp字符串比较类似
//	printf("%d\n", judge);
//	int array3[10] = { 0 };
//	memset(array3, 1, 20);//以字节为单位进行内存设置
//	return 0;
//}


//结构体深刨
//1.只有关键字 没有名字 这种被称为匿名结构体类型
//struct person
//{
//	char name[20];
//	int age;
//	double height;
//	struct//匿名结构体类型
//	{
//		char phone[12];
//		char address[20];
//	};
//};
//#include<stdio.h>
//int main() {
//	struct person one = { "one",18,180.00,{"110120","中国大陆"} };
//	printf("%s\n", one.address);
//	return 0;
//}//结构体对象 one 可以通过 one.address 直接访问匿名结构体成员变量 phone,代码相对比较简洁
//  //反之则必须通过 jim.结构体名字.phone 来访问结构体成员变量

//2.结构体自引用  不是包含同类型的变量,而是包含同类型的指针

//数据结构:数据在内存中存储的结构 ; 线性数据结构有一种是顺序表 就是 1,2,3,4…… 一个挨一个存放;第二个是链表,没有相邻,而是你在这个位置,我在那个位置,每个数据前半段是存放数据的数据域,后半段是指针域(你可以找到我,我也可以找到你)
//struct person
//{
//	char name[20];
//	int age;
//	struct person* next;//如果用 struct person next,这不是死递归嘛
//	//          指针变量大小是可算得,  struct person* next可以找到和自己同类型的结构
//}


//3.结构体类型对齐
//#include<stdio.h>
//struct student1
//{
//	char one;//第一个成员在结构体变量偏移量为0的地址处,其他成员变量要对齐到对齐数(编译器默认对齐数与该成员大小的较小值,VS是8)的整数倍处
//	int two;
//	char three;
//};//结构体总大小为最大对齐数的整数倍  如果嵌套了结构体,则找结构体最大对齐数就可以了
//struct student2
//{
//	char one;
//	char three;
//	int two;
//};
//int main() {
//	struct student1 one = { 0 };
//	struct student2 two = { 0 };
//	printf("%d\n", sizeof(one));//12
//	printf("%d\n", sizeof(two));//8   student1和2的成员一摸一样,但所占总空间还是有些区别的 让占用空间小的尽量集中在一起
//	return 0;
//}
/*
   为什么存在内存对齐?
   1.平台原因:不是所有的硬件可以访问任意地址的数据;某些硬件只能只能获取特定的数据,否则硬件会出现异常
   2.性能原因:访问未对齐需要两次访问,访问对齐需要一次就可以了
   总的来说:结构体对齐就是用空间来换取时间的做法
*/

//修改默认对齐数  和 offsetof(计算变量在首地址的偏移)宏  模拟实现offsetof
//#include<stdio.h>
//#include <stddef.h>//offsetof头文件
#pragma pack(2)//修改默认对齐数为8
//struct student1
//{
//	char one;
//	int two;
//	char three;
//};
#pragma pack()//修改回来
//
//#define OFFSETOF(struct_name,member_name)     (int*)&(((struct_name*)0)->member_name)
//
//int main() {
//	//printf("%d\n", sizeof(struct student1));//8
//	//printf("%d\n", offsetof/*小写给人误以为是函数*/(struct student1, one));
//	//printf("%d\n", offsetof(struct student1, two));
//	//printf("%d\n", offsetof(struct student1, three));
//
//    printf("%d\n", OFFSETOF(struct student1, one));
//    printf("%d\n", OFFSETOF(struct student1, two));
//    printf("%d\n", OFFSETOF(struct student1, three));
//
//	return 0;
//}

/*4.位段 
    1.成员:int   ;  unsigned int  ;signed  int; char 
    2.位段成员名后边有一个冒号和数字
    3.位段的空间是按照4个字节(int)或者1个字节(char)来开辟的

    4.位段不跨平台的:第一int位段当作有符号还是无符号不确定
                     第二位段最大数目不确定(16位机器最大16,32位机器32)
                     第三位段的成员从左向右分配还是从右向左分配尚未定义
                     第四:当一个结构有两个位段,第二个位段比较大,无法容纳第一个位段剩余位时,是舍弃还是利用不确定
    

*/
//#include<stdio.h>
//struct word
//{
//    int a : 2;//成员a占2个bit位
//    int b : 5;//成员b占5个bit位
//    int c : 30;//成员c占30个bit位
//};
//int main() {
//    printf("%d\n", sizeof(struct word));
//    return 0;
//}
//位段可以更好的节省空间,但有跨平台的问题

/*  枚举的优点:
             1.增加代码的可读性和维护性
             2.有类型检查,更为严谨
             3.便于调试
             4.使用方便,一次可定义多个变量

*/
//enum Color
//{
//    RED,
//    GREEN,
//    BLUE,
//
//};
//#include<stdio.h>
//int main() {
//    enum Color one = BLUE;
//    printf("%d\n", RED);
//    printf("%d\n", GREEN);
//    printf("%d\n", sizeof(one));
//    return 0;
//}


//联合体(共用体)  特征是 公用同一块空间
//union body
//{
//    char one;
//    int two;
//}u;
//#include<stdio.h>
//int BigSmall() {
//    u.two = 1;
//    return u.one;
//}
//int main() {
//    //union body test;
//    //printf("%d\n", sizeof(test));//打印结果是4
//
//    //printf("%p\n", &test);
//    //printf("%p\n", &(test.one));
//    //printf("%p\n", &(test.two));//地址一样
//
//    int number = BigSmall();
//    if (number == 1) {
//        printf("小端存储\n");
//    }
//    else {
//        printf("大端存储\n");
//    }
//    return 0;
//}
//联合体大小计算  联合体的大小至少为最大成员的大小 , 当最大成员大小不是最大对齐数的倍数时,要对齐到最大对齐数的倍数


/*
    写一个通讯录  1.可以存放1000个人信息(名字+性别+年龄+电话+地址) 2.增删查改
    AddressBook.h 函数声明    
    AddressBook.c 函数定义
    源.c 通讯录模板
*/
通讯录--静态版本
//#include"AddressBook.h"
通讯录--动态版本  1.通讯录初始化后,能存放三个人信息  放满后可以增加两个人信息
//void menu()
//{
//    printf("############ 1.ADD  #############\n");
//    printf("############ 2.DEL  #############\n");
//    printf("############3.SEARCH#############\n");
//    printf("############4.REVIES#############\n");
//    printf("############5.PRINTF#############\n");
//    printf("############ 0.QUIT #############\n");
//}
//enum Options
//{
//    QUIT,
//    ADD,
//    DEL,
//    SEARCH,
//    REVIES,
//    PRINTF
//};
//int main()
//{
//    int input = 0;
//    AddressBook  ab;//创建通讯录
//    InitializationAddressBook(&ab);//初始化通讯录
//    do {
//        menu();
//        printf("请选择:->\n");
//        scanf("%d", &input);
//        switch (input)
//        {
//        case ADD:
//            //增加人的信息
//            AddPerson(&ab);
//            break;
//        case DEL:
//            //删除
//            DelPerson(&ab);
//            break;
//        case SEARCH:
//            //查找
//            SearchPerson(&ab);
//            break;
//        case REVIES:
//            //修改
//            ReviesPerson(&ab);
//            break;
//        case PRINTF:
//            //打印
//            PrintfPerson(&ab);
//            break;
//        case QUIT:
//            SavePerson(&ab);//保存信息
//            FreePerson(&ab);//动态释放空间
//            printf("退出通讯录\n");
//            break;
//        default:
//            printf("选择错误,请重新选择\n");
//            break;
//        }
//    } while (input);
//    return 0;
//}


//-------------动态内存分配(比固态更灵活,动态内存开辟在堆区)

//malloc
//#include<stdio.h>
//#include<stdlib.h>//malloc(开辟空间值未被初始化) ,calloc(值被初始化)和realloc(动态内存空间调整)头文件
//int main() {
//    //int array[10] = { 0 };//栈区开辟的10个整形空间
//    //int* point = (int*)malloc(10 * sizeof(int));//动态内存开辟  因为malloc返回类型是void*所以强制类型转换
//    //int* point1 = (int*)realloc(NULL, 40);//这个类似与malloc,直接开辟四十个字节
//    int* point = (int*)calloc(10, sizeof(int));
//
//    if (point == NULL) {//开辟失败返回空指针  所以malloc的返回值要做检查
//         //常见的动态内存错误:1.对NULL指针解引用操作
//        perror("main");
//        return 0;
//    }
//
//    //使用
//    int frequency = 0;
//    for (frequency = 0; frequency < 10; frequency++) { //常见的动态内存错误:2.对动态内存空间的越界访问
//        printf("%d \n", point[frequency]);
//        *(point + frequency) = frequency;
//        printf("%d ", point[frequency]);
//        
//    }
//
//    //调整空间  再加点
//    int* start = realloc(point, 10 * sizeof(int));//realloc返回值为调整之后的内存地址(后面有足够的空间直接追加,原来数据不发生变化;如果没有足够的空间,则找一个可以装下的,返回一个新的内存地址)
//    if (start != NULL) {//调整失败返回空指针
//        point = start;
//    }
//  
//    //回收
//    free(point);//malloc和free成对出现  free如果参数是NULL,则函数什么事情都不做
//     //常见的动态内存错误:3.free释放非动态开辟空间或只是放一部分(动态开辟的的指针往后走,释放指针之后的动态空间);对同一块动态开辟的空间多次释放或忘记释放(内存泄漏)
//     //动态开辟的空间,两种回收方式(1.主动释放2.程序结束)
//    point = NULL;//手动置为空指针
//    return 0;
//}

//温馨提示:静态库又叫数据段   常量放在代码段

//柔性数组(C99 结构体最后一个成员是未知大小的数组) 柔性数组前面必须至少有一个成员
//#include<stdio.h>
//#include<stdlib.h>
//struct test
//{
//    int number;
//    //int array[];//大小是未知
//    int array[0];//也可以写成这样
//    
//};
//int main()
//{
//    //printf("%d\n", sizeof(struct test));// 结果是4  sizeof不计算柔性数组的内存
//
//    struct test* point = (struct test*)malloc(sizeof(struct test) + 10 * sizeof(int));
//    point->number = 10;
//    int frequency = 0;
//    for (frequency = 0; frequency < 10; frequency++) {
//        point->array[frequency] = frequency;
//    }
//    struct test* point1 = (struct test*)realloc(point,sizeof(struct test) + 20 * sizeof(int));
//    if (point1 != NULL) {
//        point = point1;
//    }
//    free(point);
//    point = NULL;
//    return 0;
//}
    

//牛刀小试
//1.打印X型图案
//#include<stdio.h>
//int main()
//{
//    int number = 0;
//    while (scanf("%d", &number) != EOF) {
//        int frequency1 = 0;
//        for (frequency1 = 0; frequency1 < number; frequency1++) {
//            int frequency2 = 0;
//            for (frequency2 = 0; frequency2 < number; frequency2++) {
//                if (frequency1 == frequency2) {
//                    printf("*");
//                }
//                else if (frequency1 + frequency2 == number - 1) {
//                    printf("*");
//                }
//                else {
//                    printf(" ");
//                }
//            }
//            printf("\n");
//        }
//    }
//    return 0;
//}

//2.获取月份天数
//#include<stdio.h>
//int main()
//{
//    int year = 0;
//    int month = 0;
//    int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
//    while (scanf("%d %d", &year, &month) != EOF) {
//        int day = days[month];
//        if (month == 2) {
//            if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) {
//                day ++;
//            }
//        }
//        printf("%d\n", day);
//    }
//    return 0;
//}

//3.五科成绩去掉最高和最低求每科平均值
//#include<stdio.h>
//int main()
//{
//    int sum = 0;
//    int score = 0;
//    int frequency = 0;
//    int max = 0;
//    int min = 100;
//    for (frequency = 0; frequency < 7; frequency++) {
//        scanf("%d", &score);
//        sum += score;
//        if (score > max) {
//            max = score;
//        }
//        if (score < min) {
//            min = score;
//        }
//    }
//    printf("%2f\n", (sum - min - max) / 5.0);
//    return 0;
//}

//4.一个升序数列,插入一个数,仍是升序
//#include<stdio.h>
//int main()
//{
//    int array[51] = { 0 };
//    int number = 0;
//    //输入数据
//    scanf("%d", &number);
//    int frequency = 0;
//    for (frequency = 0; frequency < number; frequency++) {
//        scanf("%d", &array[frequency]);
//    }
//    //输入要插入的数据
//    int insert_number = 0;
//    scanf("%d", &insert_number);
//    for (frequency = number - 1; frequency >= 0; frequency--) {
//        if (array[frequency] > insert_number) {
//            array[frequency + 1] = array[frequency];
//        }
//        else {
//            array[frequency + 1] = insert_number;
//            break;
//        }
//    }
//    if (frequency < 0) {
//        array[0] = insert_number;
//    }
//    //输出
//    for (frequency = 0; frequency < number + 1; frequency++) {
//        printf("%d ", array[frequency]);
//    }
//    return 0;
//}

//5.将一个数的二进制位的奇数和偶数位的交换
//#include<stdio.h>
//int main()
//{
//    int number = 10;
//    int middle = ((number & 0xaaaaaaaa) >> 1) + ((number & 0x55555555) << 1);
//    printf("%d\n", middle);
//    return 0;
//}

/*  
    文件:将数据放在电脑硬盘上,做到数据持久化
     磁盘上的文件是文件;在程序设计中有两种(1.程序文件(源程序文件.c)(目标文件.obj)(可执行程序.exe)2.数据文件:程序运行时读写的数据)
     文件名:c:\code(<-文件路径)\test(<-文件主干名).txt(文件后缀)
     一般来说通过FILE的指针来维护FILE结构的变量(文件信息区),定义pf是一个指向FILE类型数据的指针变量,通过文件信息区中的信息来访问该文件
     文件读写之前应先打开文件(fopen),使用结束后应关闭文件(fclose)

*/
//#include<stdio.h>
//struct student
//{
//    int age;
//    char name[20];
//};
//int main()
//{
//    char array[20] = { 0 };
//    struct student one = { 18,"aoteman" };
//    struct student two = { 0 };
//
//    //打开文件
//    //FILE* pf = fopen("D:\\ShiWeiGeng\\新建文件夹 (11)\\VsCode\\Project1\\test.txt", "r");//绝对路径
//    FILE* pf = fopen("test.txt", "r");//相对路径
//    if (pf == NULL) {
//        perror("fopen");
//        return 1;
//    }
//
//    //写文件  -------------------------
//    
//    //流(水流):高度抽象的概念;屏幕,硬盘,U盘,光盘,软盘每个都有自己的读写规则,所以为了降低门槛,用流来进行读写
//    //其实C语言程序,只要运行,就默认打开了3个流   stdin(标准输入流->键盘),stdout(标准输出流->屏幕),stderr(标准错误流->屏幕)
//    
//    //1.字符输出函数	fputc	所有输出流
//    /*fputc('h', pf);
//    fputc('e', pf);
//    fputc('l', pf);
//    fputc('l', pf);
//    fputc('o', pf);*/
//
//    /*fputc('h', stdout);//从屏幕上写入
//    fputc('e', stdout);
//    fputc('l', stdout);
//    fputc('l', stdout);
//    fputc('o', stdout);*/
//
//    //2.文本行输出函数	fputs	所有输出流
//    /*fputs("abcdefg\n", pf);
//    fputs("hijklmn\n", pf);*/
//
//    //3.格式化输出函数	fprintf	所有输出流
//    /*fprintf(pf, "%d %s", one.age, one.name);*/
//    //fprintf写成printf -> fprintf(stdout, "%d %s", one.age, one.name)   参数不同功能不同
//
//    //4.二进制输出	fwrite	文件
//    //fwrite(&one, sizeof(struct student), 1, pf);//字符串用二进制写进去和文本写进去一样
//
//    //读文件---------------------------
//
//    //1.字符输入函数	fgetc	所有输入流  //文件结束或遇到错误会返回EOF
//    /*int number=fgetc(pf);
//    printf("%c\n", number);
//    number = fgetc(pf);
//    printf("%c\n", number);
//    number = fgetc(pf);
//    printf("%c\n", number);*/
//
//    /*int number = fgetc(stdin);//从键盘上读取
//    printf("%c\n", number);
//    number = fgetc(stdin);
//    printf("%c\n", number);
//    number = fgetc(stdin);
//    printf("%c\n", number);*/
//
//    //2.文本行输入函数	fgets	所有输入流
//    //fgets(array, 4, pf);//读取3个,另外一个放\0
//    //printf("%s\n", array);
//    //fgets(array, 4, pf);
//    //printf("%s\n", array);
//
//    //3.格式化输入函数	fscanf	所有输入流
//    /*fscanf(pf, "%d %s", &(two.age), two.name);
//    //fscanf写成scanf-> fscanf(stdin, "%d %s", &(two.age), two.name);
//    printf("%d %s\n", two.age, two.name);*/
//
//    //4.二进制输入	fread	文件
//    /*fread(&two, sizeof(struct student), 1, pf);
//    printf("%d %s\n", two.age, two.name);*/
//
//    //文件的随机读写--------------------------------------------
//    
//    //  1.  fseek根据文件指针的位置和偏移来定位文件指针
//    //  2.ftell返回文件指针起始位置的偏移量
//
//    fseek(pf, 2, SEEK_CUR);//  SEEK_CUR  当前位置   ; SEEK_END  最后位置  ; SEEK_SET  最前位置
//    int number = fgetc(pf);
//    printf("%c\n", number);
//    int number1 = ftell(pf);
//    printf("%c\n", number);
//
//    fseek(pf, -2, SEEK_END);
//    number = fgetc(pf);
//    printf("%c\n", number);
//    number1 = ftell(pf);
//    printf("%c\n", number);
//
//    fseek(pf, 2, SEEK_SET);
//    number = fgetc(pf);
//    printf("%c\n", number);
//    number1 = ftell(pf);
//    printf("%c\n", number);
//
//    //  3.rewind文件指针的位置返回到起始位置
//
//    rewind(pf);
//    number = fgetc(pf);
//    printf("%c\n", number);
//
//
//    //关闭文件-------------------------------
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}//      内存程序   (输入/读取)<----------------------------->(输出/写入)  文件(硬盘)


//sprintf  和sscanf
//struct student
//{
//    char name[20];
//    int age;
//    char gender[10];
//};
//#include<stdio.h>
//int main()
//{
//    struct student one = { "aoteman",18,"weizhi" };
//    struct student middle1 = { 0 };
//    char middle2[100] = { 0 };
//
//    //把一个格式化的数据转化为字符串
//    sprintf(middle2, "%s %d %s", one.name, one.age, one.gender);
//    printf("%s\n", middle2);
//
//    //从middle2中还原一个结构体数据
//    sscanf(middle2, "%s %d %s", middle1.name, &(middle1.age), middle1.gender);
//    printf("%s %d %s\n", middle1.name, middle1.age, middle1.gender);
//    return 0;
//        
//}

/*  文件相关知识
1. 一组函数对比
    scanf      针对标准输入的格式化输入语句  stdin
    fscanf     针对所有输入流的格式化输入语句  stdin/文件
    sscanf     从一个字符串中读取一个格式化数据

    printf     针对标准输出的格式化输入语句  stdout
    fprintf    针对所有输入流的格式化输入语句  stdout/文件
    sprintf    把一个格式化的数据,转换为字符串


 2. 数据在在内存中以二进制形式存储,不加转换输出到外存,就是二进制文件(数值既可以二进制存储也可以ASCII形式存储)
   外存以ASCII形式存储的文件就是文本文件(字符)

   比如10000这个数字  ASCII形式存储,需要五个字节(每个字符一个字节);而二进制形式输出,则只要四个字节

 3.文件缓冲区  想磁盘中输出数据先放到文件缓冲区 装满后才一起放到磁盘  提高了效率


 4.文件读取结束判定  feof(在文件读取中,不能用feof函数返回值直接判断文件是否结束,而是判断读取文件结束还是遇到文件结尾结束)

   fgetc函数在读取结束的时候,会返回EOF  ; 正常读取结束后,返回的是读取到字符串的ASCII值

   fgets函数在读取结束的时候,会返回空指针 ; 正常读取结束后,返回字符串空间的起始地址

   fread函数在读取结束后,返回的是实际读取的个数;如果发现读取的个数小于指定的元素个数,这就是最后一次

*/
//#include<stdio.h>
//int main()
//{
//    FILE* pf_read = fopen("test.txt", "r");
//    if (pf_read == NULL) {
//        return 1;
//    }
//    FILE* pf_write = fopen("my_test.txt", "w");
//    if (pf_write == NULL) {
//        //打开这个文件错误,就把上个文件关闭
//        fclose(pf_read);
//        pf_read = NULL;
//        return 1;
//    }
//
//    //文件打开成功 读写文件
//    int middle = 0;
//    while ((middle = fgetc(pf_read)) != EOF) {
//        //读取成功  写文件
//        fputc(middle, pf_write);
//    }
//    
//    if (feof(pf_read)) {//在设置了与流关联的文件结尾指示符的情况下,将返回非零值。否则,将返回零
//        printf("文件正常结束\n");
//    }
//    else if (ferror(pf_read)) {//在设置了与流关联的错误指示器的情况下,将返回非零值。   否则,将返回零。
//        printf("文件读取失败结束\n");
//    }
//
//    //关闭文件
//    fclose(pf_read);
//    pf_read = NULL;
//    fclose(pf_write);
//    pf_write = NULL;
//
//    return 0;
//}

//atoi 和模拟实现atoi(字符串转化为整数)
//#include<stdio.h>
#include<stdlib.h>//atoi头文件
//#include<assert.h>
//#include<ctype.h>//  isspace  isdigit头文件
//#include<limits.h>//INT_MAX  INT_MIN头文件
//
//enum is_legitimate
//{
//    NOOK,//0  非法
//    OK//1     合法
//};
//
//enum is_legitimate number;//记录my_atoi返回值是否合法
//
//int my_atoi(const char* point) {
//
//    int flag = 1;
//
//    //1.传的空指针
//    /*assert(point != NULL);*/
//    if (point == NULL) {
//        return 0;
//    }
//
//    //2.空字符串
//    if (*point == '\0') {
//        return 0;
//    }
//
//    //3.遇到空白字符
//    while (isspace(*point)) {
//        point++;
//    }
//
//    //4.正负号问题
//    if (*point == '+') {
//        flag = 1;
//        point++;
//    }
//    else if(*point=='-') {
//        flag = -1;
//        point++;
//    }
//
//    //5.处理数字字符
//    long long number1 = 0;
//    while (isdigit(*point)) {
//        number1 = number1 * 10 + flag*(*point - '0');
//        if (number1 > INT_MAX || number1 < INT_MIN) {//预防超出范围
//            return 0;
//        }
//        point++;
//    }
//
//    if (*point == '\0') {
//        number = OK;
//        return (int)number1;
//    }
//    else {
//        //非数字字符
//        return 0;
//    }
//
//}
//int main()
//{
//    const char* point = "  120";
//    int middle = my_atoi(point);
//    if (number == OK) {
//        printf("正常的转换:%d\n", middle);
//    }
//    else {
//        printf("非法转换\n");
//    }
//    
//    return 0;
//}

//找出数组只出现一次的数字
//#include<stdio.h>
//void Find(int array[], int size, int* number1, int* number2)
//{
//
//    //1.把所有数据异或
//    int frequency = 0;
//    int middle = 0;
//    for (frequency = 0; frequency < size; frequency++) {
//        middle ^= array[frequency];
//    }
//
//    //2.计算middle的哪一位为1
//    int find = 0;
//    for (frequency < 0; frequency < 32; frequency++)
//    {
//        if (((middle >> frequency) & 1) == 1) {
//            find = frequency;
//            break;
//        }
//    }
//
//    //3.在find位为1的放在一个组,为0的放在一个组
//    int one = 0;
//    int two = 0;
//    for (frequency = 0; frequency < size; frequency++) {
//        if (((array[frequency] >> find) & 1) == 1) {
//            one ^= array[frequency];
//        }
//        else {
//            two ^= array[frequency];
//        }
//    }
//    *number1 = one;
//    *number2 = two;
//}
//int main()
//{
//    int array[] = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4 };
//    int size = sizeof(array) / sizeof(array[0]);
//    int number1 = 0;
//    int number2 = 0;
//    Find(array, size, &number1, &number2);
//    printf("%d %d\n", number1, number2);
//    return 0;
//}

/*--------------------------预处理(最后的倔强)--------------------------------

    1.   .c文件到.exe文件(一个.c文件通过编译器转换为.obj文件,然后通过链接器(链接库,也可以说成静态库))弄成可执行程序,通过运行环境,获取运行结果

    2.     翻译环境
           
           编译器分为预处理,编译和汇编三个成分
           预处理(预编译):第一完成头文件的包含;
                  第二#define定义的宏和符号的替换;
                  第三注释删除;
                  这些都是文本操作
           编译:把C语言代码转换为汇编代码(1.语法分析
                                         2.词义分析
                                         3.语义分析
                                         4.符号汇总)
           汇编:将汇编代码转换成了二进制指令(机器指令) 生成了符号表

           链接器:把多个目标的头文件进行链接(1.合并段表
                                            2.符号表的合并和重定位)
            
            运行坏境1.程序必须载入内存中,一般由操作系统来完成。在独立坏境中,程序的载入必须手工完成或通过可执行代码完成
                    2.程序执行开始从main函数开始
                    3.系统分配内存空间
                    4.终止程序(正常从main终止或意外终止)
*/

//1.预定义符号
//#include<stdio.h>
//int main()
//{
//    //printf("%s\n", __FILE__);//进行编辑的源文件
//    //printf("%d\n", __LINE__);//文件当前行号
//    //printf("%s\n", __DATE__);//文件被编译的日期
//    //printf("%s\n", __TIME__);//文件被编译的时间
//    //printf("%s\n", __FUNCTION__);//文件被编译的函数名
//    
//    //正确用法--写日记
//    FILE* pf = fopen("test.txt", "a+");//追加数据
//    if (pf == NULL) {
//        perror("fopen\n");
//        return 1;
//    }
//
//    int frequency = 0;
//    for (frequency = 0; frequency < 10; frequency++) {
//        fprintf(pf, "%d : %s %d %s %s \n", frequency, __FILE__, __LINE__, __DATE__, __TIME__);
//    }
//
//    fclose(pf);
//    pf = NULL;
//
//    return 0;
//}

//2.#define定义标识符骚操作   
//#include<stdio.h>
//#define CASE break;case
//#define NUMBER 100  
//int main()
//{
//    
//    int frequency = 0;
//    switch (scanf("%d", &frequency)) {
//    case1:
//        CASE 2 :
//            CASE 3 :
//            break;
//
//    }
//#undef NUMBER//移除宏定义
//    //printf("%d\n", NUMBER);//出错
//    return 0;
//}

//3. #(替换变量名为字符串)和##(将两个符号链接在一起)
//#include<stdio.h>
//#define PRINTF(accept) printf("this letter "#accept" value is%d\n",accept);
//#define LINK(one,two) one##two
//int main()
//{
//    //printf("1.helloworld\n");
//    //printf("2.hello""world\n");//1. 和 2.打印效果相同 
//
//    int number1 = 10;
//    打印  this letter number1  value is  10
//    //PRINTF(number1);//  #accept 表示"number1"
//
//    printf("%d\n", LINK(numbe, r1));
//    return 0;
//}


/*5.宏和函数区别(宏有的地方和函数和相似  C语言也没有区分两者  我们习惯宏全部大写,函数不要全部大写)

   //优点:1.简单的运算中 ; 宏比函数在程序的规模和速度更胜一筹
           2.函数参数是固定类型  ;而宏的参数和类型无关

   //缺点:1.每次使用宏,一部分宏代码就会插入到程序中,增加了程序的长度;而函数代码只出现一个地方,每次调用会调用那个地方同一份代码
           2.宏不能调试
           3.宏由于类型无关,也会不够严谨,可能还会有优先级的问题
           4.宏不可以递归(宏只做简单的文本替换,且只替换一次,如果出现递归定义,就会无法被完全替换,导致后续编译时原宏名被当作函数)  而函数可以

*/

//6.条件编译(可以嵌套)  调试型代码,删了可惜,不删碍事,所以我们可以选择性编译   
//#include<stdio.h>
//#define NUMBER 
//int main()
//{
//
#if 1-2 //if  常量表达式
    printf("有这个数字");
#endif
//
//    //如果NUMBER定义了,下面参与编译
#ifdef NUMBER
    printf("有这个数字\n");
#endif

#if defined NUMBER
    printf("有这个数字\n");
#endif
//
//
//    //如果NUMBER没定义,下面参与编译
//#ifndef NUMBER
//    printf("这个数字没定义\n");
//#endif
//
//#if !defined NUMBER
//    printf("这个数字没定义\n");
//#endif
//
//#if 1==2
//    printf("第一个执行\n");//第一个为真  第二个即使为真也不执行
//#elif 2==3
//    printf("第二个执行\n");
//#else
//    printf("第三个执行\n");
//#endif
//    return 0;
//}

//7.文件包含
/*    
      " " 和 < >包含头文件的本质区别是:查找策略不同  
      " " 1.先从自己代码所在的目录查找 2.如果1找不到,则在库函数的头文件目录查找
      < > 直接去库函数头文件所在的目录下查找

      嵌套文件包含   同一个头文件被重复多次包含(意味这代码被拷贝多份,代码冗余)  如何防止呢?
                    1. #pragma once
                    2. #ifndef __AddressBook_H__
                       #define __AddressBook_H__
                       #endif
*/
#include<stdio.h>//库文件包含,C语言库中提供的函数   <>
#ifndef __AddressBook_H__
#define __AddressBook_H__
#include"stdio.h"//本地文件包含,自定义的函数使用  ""  //这样效率有点低了
int main()
{  
    printf("HelloWorld\n");
    return 0;
}
#endif

 

AddressBook.h文件 :

#pragma once

#include<stdio.h>
#include<stdlib.h>//动态用到的头文件
#include<string.h>

#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELEPHONE 12
#define MAX_ADDRESS 30
#define MAX 1000

//动态版本
#define CAPACITY 3
#define SIZE 2

//联系人定义
typedef struct person
{
	char name[MAX_NAME];
	char gender[MAX_GENDER];
	int age;
	char telephone[MAX_TELEPHONE];
	char address[MAX_ADDRESS];
}person;

//静态通讯录定义
//typedef struct AddressBook
//{
//	person app[MAX];//存放人的信息
//	int size;//记录当前通讯录有效信息个数
//}AddressBook;

//动态通讯录定义
typedef struct AddressBook
{
	person* app;//存放人的信息
	int size;//记录当前通讯录有效信息个数
	int capacity;//记录当前通讯录的最大容量
}AddressBook;


//初始化通讯录
void InitializationAddressBook(AddressBook* point);

//增
void AddPerson(AddressBook* point);

//打印
void PrintfPerson(const AddressBook* point);//打印不修改 代码更健康

//删
void DelPerson(AddressBook* point);

//查
void SearchPerson(AddressBook* point);

//改
void ReviesPerson(AddressBook* point);

//动态释放空间
void FreePerson(AddressBook* point);

//保存通讯录信息
void SavePerson(AddressBook* point);

//加载文件
void LoadAddressBook(AddressBook* point);

//增容函数
void CapacityIncrease(AddressBook* point);

 

AddressBook.c文件 :

#define _CRT_SECURE_NO_WARNINGS
#include"AddressBook.h"

//静态初始化
//void InitializationAddressBook(AddressBook* point) {
//	point->size = 0;
//	memset(point->app, 0, sizeof(point->app));
//}

//动态初始化
void InitializationAddressBook(AddressBook* point) {
	point->app = (person*)malloc(CAPACITY * sizeof(person));
	if (point->app == NULL) {
		perror("InitializationAddressBook");
		return;
	}
	point->size = 0;
	point->capacity = CAPACITY;

	//加载文件
	LoadAddressBook(point);
}

//动态释放
void FreePerson(AddressBook* point) {
	free(point->app);
	point->app = NULL;
	point->size = 0;
	point->capacity = 0;
}

//静态增
//void AddPerson(AddressBook* point) {
//	if (point->size == MAX) {
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//	//增加
//	printf("请输入名字:->");
//	scanf("%s", point->app[point->size].name);
//	printf("请输入性别:->");
//	scanf("%s", point->app[point->size].gender);
//	printf("请输入年龄:->");
//	scanf("%d", &(point->app[point->size].age));//年龄需要取地址
//	printf("请输入电话:->");
//	scanf("%s", point->app[point->size].telephone);
//	printf("请输入地址:->");
//	scanf("%s", point->app[point->size].address);
//
//	point->size++;
//	printf("增加成功\n");
//}
 

//增容函数
void CapacityIncrease(AddressBook* point) {
	if (point->size == point->capacity) {
		person* point1 = (person*)realloc(point->app, (point->capacity + SIZE) * sizeof(person));
		if (point1 != NULL) {
			point->app = point1;
			point->capacity += SIZE;
			printf("增容成功\n");
		}
		else {
			perror("AddPerson");
			printf("添加联系人失败\n");
			return;
		}
	}
}

//动态增
void AddPerson(AddressBook* point) {
	//增容
	CapacityIncrease(point);
	
	//增加
	printf("请输入名字:->");
	scanf("%s", point->app[point->size].name);
	printf("请输入性别:->");
	scanf("%s", point->app[point->size].gender);
	printf("请输入年龄:->");
	scanf("%d", &(point->app[point->size].age));//年龄需要取地址
	printf("请输入电话:->");
	scanf("%s", point->app[point->size].telephone);
	printf("请输入地址:->");
	scanf("%s", point->app[point->size].address);

	point->size++;
	printf("增加成功\n");
}



//打印
void PrintfPerson(AddressBook* point) {
	//标题打印
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "性别", "年龄", "电话", "地址");
	//打印数据
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", 
		    point->app[frequency].name,
			point->app[frequency].gender,
			point->app[frequency].age,
			point->app[frequency].telephone,
			point->app[frequency].address);
	}
}


static int FindName(AddressBook* point, char name[]) {//修饰函数  只在本文件用即可,外部找不到
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		if (strcmp(point->app[frequency].name, name) == 0) {
			return frequency;
		}
	}
	return -1;
}



//删除
void DelPerson(AddressBook* point) {
	if (point->size == 0) {
		printf("通讯录为空,无需删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入删除人的名字:->");
	scanf("%s", name);
	//有没有删除人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("删除人不存在\n");
		return;
	}
	//删除
	int frequency = 0;
	for (frequency = 0; frequency < point->size - 1; frequency++) {
		point->app[frequency] = point->app[frequency + 1];
	}
	point->size--;
	printf("删除成功\n");
}


//查找
void SearchPerson(AddressBook* point) {
	char name[MAX_NAME] = { 0 };
	printf("请输入查找人的名字:->");
	scanf("%s", name);
	//有没有查找人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("查找人不存在\n");
		return;
	}
	else {
		printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "性别", "年龄", "电话", "地址");
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n",
			point->app[who].name,
			point->app[who].gender,
			point->app[who].age,
			point->app[who].telephone,
			point->app[who].address);
	}
}

//改
void ReviesPerson(AddressBook* point) {
	char name[MAX_NAME] = { 0 };
	printf("请输入修改人的名字:->");
	scanf("%s", name);
	//有没有修改人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("修改人不存在\n");
		return;
	}
	else {
		printf("请输入名字:->");
		scanf("%s", point->app[who].name);
		printf("请输入性别:->");
		scanf("%s", point->app[who].gender);
		printf("请输入年龄:->");
		scanf("%s", &(point->app[who].age));//年龄需要取地址
		printf("请输入电话:->");
		scanf("%s", point->app[who].telephone);
		printf("请输入地址:->");
		scanf("%s", point->app[who].address);
		printf("修改成功\n");
	}
}

void SavePerson(AddressBook* point)
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL) {
		perror(SavePerson);
		return;
	}
	//写
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		fwrite(point->app + frequency, sizeof(person), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

//加载文件
void LoadAddressBook(AddressBook* point) {

	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror(LoadAddressBook);
		return;
	}

	//读文件
	person middle = { 0 };
	while (fread(&middle, sizeof(person), 1, pf)) {
		CapacityIncrease(point);//是否考虑增容
		point->app[point->size] = middle;
		point->size++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

}

感谢陪伴,谢谢大家呀

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手冰凉ya

你的鼓励是我最大动力谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值