C语言大师:编得狂,骂得响(笔记1)

023.long,long long与CPU架构的那些事儿_哔哩哔哩_bilibili

intel公司在上世纪创的8086型号CPU是第一个32位CPU用x86来表示

后来升级到64位取名“x86-64”替代为x64


024.size_t类型与sizeof的使用_哔哩哔哩_bilibili

x86:

long=int 4位

long long 8位

x64:

long 8位

long long 8位

自我调整

025.实际开发过程中真正的整型:微软与跨平台的int标准_哔哩哔哩_bilibili

主要讲了在企业实际开发中不会使用int char什么的,会使用下节的头文件

补充了long等的使用(实际开发基本不用)

026.★企业实际开发的整型声明:stdint.h的引用_哔哩哔哩_bilibili

按住Ctrl点进stdint.h头文件可以查看图(2)

图(2)

027.历史项目遗留_哔哩哔哩_bilibili

在现在的项目来说基本上是要确定宽度使用uint32_t这些,但对于linux等以前的项目要面向更多的机器比如上世纪的32位16位机器,就会使用int来确保兼容性。

028.隐式与显式类型转换_哔哩哔哩_bilibili

Type Conversion类型转换

如果这样转换的话 uSmallNum会先被转化被32位然后 = 赋值过程中被转化为16位给sSmallNum,做了一次显性的再做了一次隐性的

029.固定宽度整数类型的格式化宏输出inttypes.h_哔哩哔哩_bilibili

030.least和fast整型的企业用途与区别_哔哩哔哩_bilibili

在<inttypes.h>中用可读性更高的代码来代替%d %u &lld等可读性差的代码 --> %"PRId16"  %"PRIdFast32"

int_fast32_t中的fast代表最低使用32位,但是为了效率可以使用更高位数的64位

int_least16_t中的least代表最低使用16位

142.企业使用[迭代方法]来替代递归_哔哩哔哩_bilibili------2024.12.8

用这样的迭代方法来代替递归,因为递归会占用大量的内存空间,在企业中不实用,尽量避免。

045.bool类型与实际案例_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>

int main()
{
	bool is_game_over = false;
	bool is_game_won = true;
	printf("%d\n", is_game_over);
	printf("%d\n", is_game_won);
}

引用stdbool.h头文件

false就是0 true就是1

046.char范围与无符号char_哔哩哔哩_bilibili

一个字节是8位 

char占一个字节 2^8=256 范围是  -128~127

uchar是0~255 常用于处理数据

uint8_t是0~255常常用于计算,因为它占空间很小

(目前就先知道有这么个用途,后面再讲)

047.常量const与#define宏_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PI 3.14   //宏定义
//const int MAX_USERS = 100;
int main()
{
	const int MAX_USERS = 100;  //普通常量定义
	//const也可以放在全局
	printf("MAX_USERS: %d\n", MAX_USERS);

	printf("PI: %f\n", PI);
}

宏定义(define)是作为全局的常量

const可以全局也可以局部

章节小节:048.第二章结束语_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main()
{
	int16_t num16 = 848;
	uint16_t num16u = 7946;
	int8_t num8 = 127;
	uint8_t num8u = 255;
	int32_t num32 = 4565445;
	uint32_t num32u = 4565445 * 2;
	float numf = 655.26;
	double numd = 454.1655;
	printf("%"PRId16"\n", num16);
	printf("%"PRIu16"\n", num16u);
	printf("%d\n", num16);
	printf("float / int :%f\n", numf / num16);
	printf("int / float :%f\n", num16 / numf);
}

未看:040.float和double有效精度对比原理与计算_哔哩哔哩_bilibili

随便看的数组一节:

uint32_t num[5] = { 0 }所有的五个元素都为0

uint32_t num[5] = { 1 }第一个元素是1 其他四个元素为0

156.指针_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARINGS
#include <stdio.h>
#include <stdlib.h>

int main()
{
	int building_floors[5] = { 101,102,103,104,105 };
	int* ptr_floor_103 = &building_floors[2];
	int* ptr_floor_105 = &building_floors[4];

	printf("building_floors[2]的地址是:%p\n", &building_floors[2]);
	printf("ptr_floor_103所指向的地址是:%p\n", ptr_floor_103);
	printf("ptr_floor_103所指向地址的值是:%d\n", *ptr_floor_103);

}

157.指针与修改_哔哩哔哩_bilibili

可以通过邻居来访问自己家的地址

printf("building_floors[3]的地址是:%p\n", &building_floors[3]);
printf("building_floors[3]的地址是:%p\n", &building_floors[2]+ 1);

指针只需要获取地址并且修改指针的值就可以修改原本的值,这是外挂最基本的原理

找到你的基址就可以修改你

反外挂可以通过指针一层套一层来隐藏基址(多级指针)

int *ptr_floor_106 = ptr_floor_103;
*ptr_floor_106 = 106;    

如果要写的比较标准

printf("楼层的地址是:%p,楼层号:%d\n",(void*)ptr_to_floor, *ptr_to_floor);

158.指针星号的企业风格规范以及容易引发的问题_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARINGS
#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 999;
	int* p, q;   //int *p   int q  
	//不推荐一行定义多个变量,容易产生歧义,除非有明确注释
	//可以做法:int *p,*q;   也不推荐,建议多行定义
	p = &a;
	q = &a;
	printf("p:%d\n", *p);
	printf("q:%d\n", q);
}

这里q不是指针是一个int类型变量,不建议一行多定义。

int* p和 int *p只是风格问题   frank的是后者更强调是个指针。

159.指针的意义与作用究竟是什么:外部服务操作_哔哩哔哩_bilibili

指针作用和意义:通过外部来访问自己本身,不需要自己动手。类似于:代购

指针的应用:windows的快捷方式

160.野指针的初步介绍_哔哩哔哩_bilibili

野指针:指向了一个无效的内存地址或者已经释放的内存地址的指针。

非常危险。

野指针会访问一个不可描述的内存空间,会导致不可预测的行为。

161.空指针的初步介绍_哔哩哔哩_bilibili

空指针:通常指没有指向任何有效内存空间的指针。

162.空指针的初始化_哔哩哔哩_bilibili

左值一定是一个变量名。

一个空指针的小案例:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

int main()
{
	uint32_t* ptr_to_floor = NULL;
	uint8_t check = 0;
	uint32_t number = 100;
	printf("输入 0 为空指针,输入 1 不为空指针:\n");
	scanf("%"PRId8"", &check);
	if (check) {
		ptr_to_floor = &number;
	}
	if (ptr_to_floor == NULL) {
		printf("没有指定的楼层的地址!\n");
	}
	else {
		printf("楼层的地址是%p,楼层号是%d\n", (void*)ptr_to_floor, *ptr_to_floor);
	}


}

并且诸如此类的错误是  scanf中的%d可能使用不当,比如uint8_t类型应使用%hhd或者%"PRId8"。

163.从代码上尝试认识野指针_哔哩哔哩_bilibili

空指针是一个正常的现象,但野指针是一个问题,它需要处理掉。

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

int main()
{
	uint32_t floor = 103;
	uint32_t* ptr_to_floor = &floor;
	printf("当前指向的地址:%p,值:%d\n", (void*)ptr_to_floor, *ptr_to_floor);
	{
		//创建一个新的作用域
		uint32_t temp_floor = 104;
		ptr_to_floor = &temp_floor;
		printf("当前指向的地址:%p,值:%d\n", (void*)ptr_to_floor, *ptr_to_floor);
	}
	printf("当前指向的地址:%p,值:%d\n", (void*)ptr_to_floor, *ptr_to_floor);
	//在作用域外他仍然是指向的104,这已经严重污染了程序的安全性
	//假如这个作用域内是机密数据,通过未及时释放的指针就可以访问到内部的机密数据了

}

运行结果: 

当前指向的地址:0000007539D8FA94,值:103
当前指向的地址:0000007539D8FAD4,值:104
当前指向的地址:0000007539D8FAD4,值:104

164.数组的首地址与指针的算数运算_哔哩哔哩_bilibili

int size = sizeof(numbers) / sizeof(number[0]);

sizeof(numbers)这个部分计算整个数组所占用的1内存大小,单位是字节。

sizeof(numbers[0])计算第一个元素的大小,字节。

知识点:

int占四个字节也就是4byte [bait] 1byte = 8bit  [bit]   bit就是位,也叫比特位

int是32位 = 4字节

%td   ptrdiff_t类型: 计算指针之间的距离

int *start_ptr = &number[0];
int *end_ptr = &number[size-1];
printf("数组首尾之间的距离是:%td\n\n",end_ptr - start_ptr);

指针相减返回的类型是ptrdiff_t类型,在64位中是int64的类型,在32位中是int类型(在vs2022上)

#ifdef _WIN64
    typedef unsigned __int64 size_t;
    typedef __int64          ptrdiff_t;      //64位    %td格式化输出
    typedef __int64          intptr_t;
#else
    typedef unsigned int     size_t;
    typedef int              ptrdiff_t;          //32位
    typedef int              intptr_t;                                                                      源自:vcruntime.h

  • t:代表 "pointer difference"(指针差异)。它用于指示该格式符是用来打印指针差异类型的数据,通常是 ptrdiff_t 类型。t 主要是为了区分它与其他整型格式符(如 %d)而设计的。

  • d:表示 "decimal"(十进制)。它表示该值应以十进制形式输出

165.续上集:指针的算数运算与比较运用_哔哩哔哩_bilibili

166.再探size_t与数组与指针的使用_哔哩哔哩_bilibili

以上两集悲惨丢失笔记,下面简写一点

以上两集悲惨丢失笔记,下面简写一点

以上两集悲惨丢失笔记,下面简写一点

for循环中++i 理论上比 i++ 性能更好

因为i++是先存储i的值然后再加1并返回

++i 是先把i+1并返回但

在迭代器和对象复杂的情况下可能会明显一些 推荐使用++i

目前的编译器会自动优化掉两者的区别

ptr += 1;不会使得指针移动

ptr = ptr +1;和ptr++;才会使指针永久移动

size_t是用来表示大小、长度和索引

64位:u64

32位:u32    

%zu专门用来输出size_t

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>

int main(void)
{
	int32_t array[] = { 10, 20 ,30, 40, 50, 60, 70, 80, 90, 100 };
	size_t array_size = sizeof(array) / sizeof(array[0]);
	int32_t* ptr = array;
	printf("数组的原始数据:\n array[] = {");
	for (size_t i = 0; i < array_size; i++)
	{
		printf(" %d ", array[i]);
	}
	printf("}\n\n");
	//通过指针对数组每个值进行操作
	printf("数组的修改数据:\n array[] = {");
	size_t i = 0;
	for (i; i < array_size; ++i)
	{
		*(ptr + i) += 5;
		printf(" %d ", *(ptr + i));
	}
	printf("}\n\n");
	printf("永久移动指针来遍历->数组的修改数据:\n array[] = {");
	int32_t *start_ptr = array;
	int32_t *end_ptr = &array[array_size - 1];
	for (ptr; ptr <= end_ptr; ptr++)
	{
		printf(" %d ", *ptr);
	}
	ptr--;
	printf("}\n\n");
	//移动后的指针指向
	printf("目前指针指向的是: %d\n", *ptr);


}

167.案例:指针查找特定元素的索引并返回_哔哩哔哩_bilibili

不使用函数来完成案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

int main(void)
{
	int32_t value[] = { 10,20,30,40,50,60,70,80,90,100 };
	size_t value_size = sizeof(value) / sizeof(value[0]);
	int32_t* ptr = value;
	
	int32_t target_value = 90;
	int32_t target_ptr = NULL;

	size_t index = 0;
	bool found = false;
	for (size_t i= 0; i < value_size; ++i)
	{
		if (*(ptr + i) == target_value) {
			target_ptr = ptr;
			index = i;
			found = true;
		}
	}
	if (found) {
		printf("元素 %d 的index: %zu\n", target_value, index);
	}
	else {
		printf("元素 %d 未找到index !", target_value);
	}
}

使用函数来完成案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

bool found_index_of_value(const int32_t* array, size_t size, int32_t target_value, size_t* index);
int main(void)
{
	int32_t value[] = { 10,20,30,40,50,60,70,80,90,100 };
	size_t value_size = sizeof(value) / sizeof(value[0]);
	int32_t* ptr = value;
	
	int32_t target_value = 90;
	int32_t target_ptr = NULL;

	size_t index = 0;
	bool found = false;
	
	
	if (found_index_of_value(ptr, value_size, target_value, &index)) {
		printf("元素 %d 的index: %zu\n", target_value, index);
	}
	else {
		printf("元素 %d 未找到index !", target_value);
	}
}
bool found_index_of_value(const int32_t* array, size_t size, int32_t target_value, size_t* index)
{
	for (size_t i = 0; i < size; ++i)
	{
		if (*(array + i) == target_value) {
			*index = i;
			return true;
		}
		
	}
	return false;
}

知识点:  妙用&与指针

bool found_index_of_value(const int32_t* array, size_t size, int32_t target_value, size_t* index)

该函数定义中使用的是size_t *index  在main里是size_t index,通过在函数中*index=i来使得main中的index的值发生改变,且不需要return

详细案例:170-1节↓

170-1.函数的值传递与地址引用传递_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

void add_ten_by_value(int32_t* value);

int main(void)
{
	int32_t my_value = 10;
	printf("原本的值:%d\n", my_value);

	add_ten_by_value(&my_value);
	printf("经过add_ten_by_value调用后的值: %d\n", my_value);
}
void add_ten_by_value(int32_t* value)
{
	*value += 5;
}

 知识点:

通过地址传递(pass by reference引用传递)来实现了在外部函数中对main内的变量进行修改值

171.案例:员工薪资系统:指针作为函数返回值_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#define EMPLOYEES_COUNT 5
void view_all_employees_salaries(uint32_t* salaries);
void increase_salaries(uint32_t* salaries);
uint32_t calculate_bonus(uint32_t salary);
uint32_t* find_highest_salaries(uint32_t* salaries);
int main(void)
{
	/*
	* 员工薪资管理系统
	* -查看所有员工的薪资
	* -批量加工资
	* -计算员工的年终奖
	* -查找最高薪资的员工(获取他的地址)
	*/
	uint32_t salaries[EMPLOYEES_COUNT] = { 5000,5600,7000,7700,10000 };
	view_all_employees_salaries(salaries);
	increase_salaries(salaries);
	view_all_employees_salaries(salaries);
	uint32_t *highest = find_highest_salaries(salaries);
	printf("员工中最高薪资是: %" PRIu32 "\n", *highest);
	printf("最高薪资的年终奖是: %" PRIu32 "\n", calculate_bonus(*highest));
}
void view_all_employees_salaries(uint32_t* salaries)
{
	/*
	int32_t salaries[] 和 int32_t *salaries 在 C 语言中没有本质的区别,二者都被当作指针处理。
	选择使用哪种写法通常是个人偏好或代码风格的问题。int32_t salaries[] 语法上更像是数组的感觉,而 int32_t *salaries 明确地表明它是一个指针。
	在大多数情况下,它们是可以互换的。
	*/
	printf("所有员工的薪资: ");
	for (size_t i = 0; i < EMPLOYEES_COUNT; ++i)
	{
		printf("%" PRIu32 " ", salaries[i]);
	}
	printf("\n");
}
void increase_salaries(uint32_t* salaries)
{
	for (size_t i = 0; i < EMPLOYEES_COUNT; ++i)
	{
		salaries[i] += 10000;
	}
}
uint32_t calculate_bonus(uint32_t salary)
{
	return salary / 10;
}
uint32_t* find_highest_salaries(uint32_t* salaries)
{
	uint32_t* highest = salaries;
	for (size_t i = 1; i < EMPLOYEES_COUNT; ++i)
	{
		if (salaries[i] > *highest) {
			highest = &salaries[i];
		}
	}
	return (uint32_t*)highest;
}

172.游戏案例:指针的练习_哔哩哔哩_bilibili

环境可以泯灭人才

在 C 语言中,std 并不是一个语言关键字,而是通常指代 标准库(Standard Library)的简称 

关于c语言中的static: 

  • 局部变量static 使得局部变量的生命周期持续整个程序运行,而不仅仅是函数的执行周期,且该变量仅初始化一次。
  • 全局变量/函数static 限制了全局变量和函数的作用域,使它们只在定义它们的文件内部可见,外部无法访问。

使用 static 可以帮助你控制变量和函数的可见性和生命周期,进而实现更好的模块化和封装。

#include <stdio.h>

void counter() {
    static int count = 0;  // static局部变量
    count++;
    printf("Counter: %d\n", count);
}

int main() {
    counter();  // 输出 Counter: 1
    counter();  // 输出 Counter: 2
    counter();  // 输出 Counter: 3
    return 0;
}

 在这个例子中,count 是一个 static 局部变量,它在多次调用 counter() 函数时保持其状态,并且每次调用时都会继续从上一次调用结束的地方开始,而不是重新初始化为 0。

 案例:升级寻找宝藏的小游戏

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

#define EXP_PER_LEVEL 100
#define MAX_LEVEL 10

const char* get_treasure_hints(uint32_t* level);
void increase_exp(uint32_t* exp, uint32_t exp_increment);
bool check_level_up(uint32_t* level, uint32_t* exp);
int main(void)
{
	uint32_t player_exp = 0;
	uint32_t player_level = 0;
	printf("%s\n\n", get_treasure_hints(&player_level));
	increase_exp(&player_exp, 50);
	if (check_level_up(&player_level, &player_exp))
	{
		printf("%s\n\n", get_treasure_hints(&player_level));
	}
	increase_exp(&player_exp, 50);
	if (check_level_up(&player_level, &player_exp))
	{
		printf("%s\n\n", get_treasure_hints(&player_level));     //0->1
	}
	increase_exp(&player_exp, 75);
	if (check_level_up(&player_level, &player_exp))
	{
		printf("%s\n\n", get_treasure_hints(&player_level));
	}
	increase_exp(&player_exp, 120);							   //1->2
	if (check_level_up(&player_level, &player_exp))
	{
		printf("%s\n\n", get_treasure_hints(&player_level));
	}
	increase_exp(&player_exp, 120);							   //2->3
	if (check_level_up(&player_level, &player_exp))
	{
		printf("%s\n\n", get_treasure_hints(&player_level));
	}
}
void increase_exp(uint32_t* exp, uint32_t exp_increment)
{
	*exp += exp_increment;
}
bool check_level_up(uint32_t* level, uint32_t* exp)
{
	if (*exp >= EXP_PER_LEVEL && *level <= MAX_LEVEL)
	{
		*exp -= EXP_PER_LEVEL;
		(*level)++;
		printf("恭喜你升级,当前等级: %" PRIu32 "\n", *level);
		return true;
	}
	return false;
}
const char* get_treasure_hints(uint32_t* level)
{
	static const char* treasure_hints[MAX_LEVEL] = {
		"提示 1: 欢迎新玩家加入游戏,请沿标记路线前往村长的家!",
		"提示 2: 前往村庄郊外猎杀皮皮鸡,并将皮皮鸡的肉带回村落!",
		"提示 3: 古老的诅咒逐渐松动,你感觉到了一丝不安,于是决定动身前往古龙密山前去一探究竟!",
		"提示 4: 在古龙密山的一处洞穴中,一柄沾染了古龙精血的锈剑向你发出了呼唤!"
	};
	return treasure_hints[*level];
}

缺陷1:increase的经验超过1级(如:200)就会导致提示失误,但在我这个案例中,必须寻找到一个宝藏才可能升级,直接升两级及以上有些不合乎常理。

 173.练习:更新分数_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
void update_score(uint32_t* player_score, uint32_t score)
{
	*player_score += score;
}
void double_score(uint32_t* player_score)
{
	*player_score *= 2;
}
uint32_t* compare_score(uint32_t* player1_score, uint32_t* player2_score)
{
	return (*player1_score > *player2_score) ? player1_score : player2_score;
}
void view_score(uint32_t player_score)
{
	printf("当前玩家的分数是: %" PRIu32 "\n",player_score);
}
int main(void)
{
	uint32_t player1_score = 600;

	uint32_t player2_score = 1100;

	update_score(&player1_score, 100);
	view_score(player1_score);

	double_score(&player2_score);
	view_score(player2_score);

	printf("分数更高的是: %" PRIu32 "\n", *(compare_score(&player1_score, &player2_score)));
}

174.游戏案例:收藏奖杯_哔哩哔哩_bilibili

知识点:

const char* add_achievement(const char* achievement)

在这个语句中,第一个const表示返回的一个字符串不可以被修改。例如: char* new = add_achievement("第一名"),假如return的是"恭喜你获得第一名",这个"恭喜你获得第一名"就不可以被修改,但是new可以被修改。只是确保了return值不可修改

第二个const就是在函数内部不可以修改achievement字符串

游戏案例:黑神话悟空成就

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

#define MAX_ACHIEVEMENTS_COUNT 10
const char* achievements[MAX_ACHIEVEMENTS_COUNT];

size_t achievements_count = 0;

void add_achievement(const char* new_achievement);
void print_achievements();
void view_achievement_details(size_t index);
void print_achievements_details();
int main(void)
{
	add_achievement("下降凡尘第一难");
	add_achievement("敲敲打打第二难");
	add_achievement("山中斗狼第三难");
	add_achievement("吸存运用第四难");
	add_achievement("真个壮怀第五难");
	add_achievement("长蛇隐迹第六难");
	add_achievement("得心应手第七难");
	add_achievement("余韵远传第八难");

	print_achievements();
	print_achievements_details();
}
void add_achievement(const char* new_achievement)
{
	if (achievements_count < MAX_ACHIEVEMENTS_COUNT)
	{
		achievements[achievements_count++] = new_achievement;
	}
	else {
		printf("Error!玩家当前成就已超出最大成就数量,请仔细检查!\n");
	}
}
void print_achievements()
{
	
	printf("\n\n=====================================================\n");
	puts("玩家成就列表:");
	for (size_t i = 0; i < achievements_count; ++i)
	{
		printf("%zu: %s\n", i + 1, achievements[i]);
	}
	printf("=====================================================\n");
}
void print_achievements_details()
{

	printf("\n\n=====================================================\n");
	puts("玩家成就详细列表:");
	for (size_t i = 0; i < achievements_count; ++i)
	{
		view_achievement_details(i + 1);
	}
	printf("=====================================================\n");
}
void view_achievement_details(size_t index)
{
	static const char* achievements_details[MAX_ACHIEVEMENTS_COUNT] = {
		"听罢老猴子的故事,该起程了。",
		"配好披挂,狼虫不怕。",
		"名是假的,不必留情。",
		"生前吃人,死后人吃。",
		"旧酿泡新物,死后奔前途!",
		"秀士述了隐情,且先饶他一命。",
		"好对手,配得好兵器。",
		"三钟响,幽魂藏。"
	};
	printf("%zu: %s", index, achievements[index - 1]);
	printf("\t\t\t\t\t\t");
	printf("%s\n", achievements_details[index - 1]);
}

176.初识结构体C语言大师:编得狂,骂得响[后篇]_哔哩哔哩_bilibili

struct Person {
	char name[50];
	size_t age;
	float height;
	float weight;
};
struct Person zjne = { "周江鸟恩", 20, 180, 80 };
printf("%s\n", zjne.name);

177.创建结构体变量与访问方式_哔哩哔哩_bilibili

typedef struct Person {
	char name[50];
	size_t age;
	float height;
	float weight;
} Person;
Person zjne = { "zjne", 1000, 1.0, 999 };
printf("%s is %zu years old , %.2f height and %.2f weight\n", 
	zjne.name, zjne.age, zjne.height, zjne.weight);

Person *ptr_zjne = &zjne;
printf("%s is %zu years old , %.2f height and %.2f weight\n", 
	ptr_zjne->name, ptr_zjne->age, ptr_zjne->height, ptr_zjne->weight);

两种方式   普通的和指针的

178.匿名结构体、函数参数为结构体_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct Student {
	char name[50];
	uint32_t id;
	float score;
} Student;
void print_stu(Student stu);
void update_score_by_reference(Student* stu, float new_score);
void update_score_by_value(Student stu, float new_score);
int main(void)
{
	Student stu = { "SlasherMan", 10001, 64.0 };
	puts("Before update: ");
	print_stu(stu);
	puts("After update by value: ");
	update_score_by_value(stu, 100);
	print_stu(stu);
	puts("After update by reference: ");
	update_score_by_reference(&stu, 100);
	print_stu(stu);
}
void print_stu(Student stu) {
	printf("Student Name: %s\n", stu.name);
	printf("Student ID: %" PRIu32 "\n", stu.id);
	printf("Student Name: %.2f\n\n", stu.score);
}
void update_score_by_value(Student stu, float new_score)
{
	stu.score = new_score;
}
void update_score_by_reference(Student* stu, float new_score)
{
	stu->score = new_score;
}

输出结果:

Before update:
Student Name: SlasherMan
Student ID: 10001
Student Name: 64.00

After update by value:
Student Name: SlasherMan
Student ID: 10001
Student Name: 64.00

After update by reference:
Student Name: SlasherMan
Student ID: 10001
Student Name: 100.00
 

// 通过指针传递结构体,可以改变结构体的值

而通过值传递只可以在函数中生成一个局部变量stu,并不是main函数里的stu

179.值语义初始化结构体变量_哔哩哔哩_bilibili

知识点:

“值语义”(Value Semantics)是指一个变量或对象的值被传递或赋值时,会复制该值,而不是引用它。也就是说,当你将一个变量的值赋给另一个变量时,新的变量会得到原值的副本,修改副本不会影响原始值。

这与“引用语义”(Reference Semantics)相对,引用语义中赋值的是引用或地址,改变其中一个对象会影响到另一个

值语义:安全,降低意外改数据的风险

get_point执行后立即销毁,想去修改函数中10,20是很难的,反外挂的一层

typedef struct Point {
	int32_t x;
	int32_t y;
} Point;
Point get_point();

int main(void)
{
	Point my_point = get_point();
	printf("my_point:( %d, %d )\n", my_point.x, my_point.y);
}
Point get_point()
{
	Point p;
	p.x = 10;
	p.y = 20;
	return p;     //return 一个结构体副本
}

在这个案例中,通过值语义将get_point中的p return给my_point实现值语义初始化结构体变量 

180.结构体数组_哔哩哔哩_bilibili

typedef struct Point {
	int32_t x;
	int32_t y;
} Point;


int main(void)
{
	Point my_point[2] = {
		{1,2},
		{3,4}
	};
	for (size_t i = 0; i < 2; i++)
	{
		printf("my_point %zu:( %d, %d )\n", i, my_point[i].x, my_point[i].y);
	}

}

181.嵌套结构体_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct Address {
	char street[50];
	char city[50];
	char country[50];
} Address;
typedef struct Person {
	char name[50];
	uint32_t age;
	Address address;
} Person;

int main(void)
{
	Person SlasherMan = {
		"SlasherMan",
		19,
		{"XXXXXXXX", "YYYYYYYYYYYYYY", "ZZZZZZZZZZZZZZZ"}
	};
	printf("Name: %s\n", SlasherMan.name);
	printf("Age: %" PRIu32 "\n", SlasherMan.age);
	printf("Address:\n");
	printf("Street: %s, city: %s, country: %s\n", SlasherMan.address.street, SlasherMan.address.city, SlasherMan.address.country);
	Person* ptr = &SlasherMan;
	printf("Name: %s\n", ptr->name);
	printf("Age: %" PRIu32 "\n", ptr->age);
	printf("Address:\n");
	printf("Street: %s, city: %s, country: %s\n", ptr->address.street, ptr->address.city, ptr->address.country);
}

182.Enumeration枚举_哔哩哔哩_bilibili

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

typedef enum {
	MONDAY,         // 0
	TUESDAY,
	WEDNESDAY,
	THURESDAY,
	FRIDAY,
	SATURDAY,
	SUNDAY = 1000000
} Weekday;
const char* get_weekday_name(Weekday day);
int main()
{
	Weekday day = SUNDAY;
	printf("SUNDAY: %d\n", day);
	printf("FRIDAY: %d\n", FRIDAY);
	printf("The first one is %s\n", get_weekday_name(MONDAY));
}
const char* get_weekday_name(Weekday day)
{
	switch (day)
	{
	case MONDAY: return "MONDAY";
	default:
		break;
	}
}

知识点:

枚举( Enumeration)是一个数据类型,当你想define多个连续的变量时可以使用

可以自定义设置值 默认从0依次+1下面一个是上面的值+1

如果这里的SATURDAY是10000则 SUNDAY就是10001

183.Union联合_哔哩哔哩_bilibili

知识点:

联合Union

它允许在相同的内存位置存储不同的数据类型。

联合体的所有成员共享一块内存空间,大小等于其最大成员的大小。

这就意味着在任一时刻,联合体只能存储一个成员的值。

一个变量可能存储多种类型的数据,但是在一个给定时刻里,只使用其中一中的数据类型,这样可以节省内存。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>

typedef union{
	int int_value;
	float float_value;
	char* string_value;
} Data;

typedef enum {
	INT,
	FLOAT,
	STRING
}DataType;

typedef struct TypedData {
	DataType type;
	Data data;
} TypedData;
void print_data(TypedData data);
int main(void)
{
	TypedData data1 = { INT, {.int_value = 10} };
	TypedData data2 = { FLOAT, {.float_value = 2.75} };
	TypedData data3 = { STRING, {.string_value = "Hello! Union."} };
	print_data(data1);
	print_data(data2);
	print_data(data3);
}
void print_data(TypedData data)
{
	switch (data.type)
	{
	case INT: 
		printf("Integer: %d\n", data.data.int_value);
		break;
	case FLOAT:
		printf("Float: %.2f\n", data.data.float_value);
		break;
	case STRING:
		printf("String: %s\n", data.data.string_value);
		break;
	
	}
}

 难点:

 联合体,枚举,结构体的连用

 难点:架构 

知识点:

表驱动法 通过预计算和存储结果,避免了重复的计算。这样程序可以通过查找表中的预存值来快速返回结果。

比如提前计算好1-9的平方值并存储在一个数组中,想查找谁的平方值就可以直接返回不需要重复计算。

在我们的这个案例中可以是class是0就输出是"Warrior"战士 ,  这个意思

修改后的案例:失落城堡2简单案例:

  • 头文件部分

game_types.h        

#ifndef GAME_TYPES_H
#define GAME_TYPES_H

typedef enum {
	Dual_Blades,				//双刀
	Staff,						//法杖
	Bow,						//弓箭
	Musket,						//火枪
} ItemType;

typedef enum {
	Goblin,				//哥布林种类
	Stone,				//石怪种类
	Dragon,				//魔龙种类
} EnemyType;


#endif // !GAME_TYPES_H

 game_ablities.h

#ifndef GAME_ABILITIES_H
#define GAME_ABILITIES_H

#include <inttypes.h>
#include <stdbool.h>
typedef union {
	int32_t strength;			//力量
	float mana;					//魔法值
	float power;				//弓箭力量
	float fire_power;			//火力值
} Ability;
#endif // !GAME_ABILITIES_H

game_structs.h

#ifndef GAME_STRUCTS_H
#define GAME_STRUCTS_H
#include "game_types.h"
#include "game_abilities.h"
typedef struct {
	char name[50];
	ItemType item;
	Ability ability;
	uint32_t exp;
	uint32_t level;
	uint32_t health;
} Player;
typedef struct {
	char name[50];
	Ability ability;
	EnemyType enemy;
	size_t difficulty;
} Enemy;

#endif // !GAME_STRUCTS_H

game_functions.h

#ifndef GAME_FUNCITONS_H
#define GAME_FUNCITONS_H
#include "game_structs.h"
void create_player(Player* player, char* name, ItemType item);

Enemy generate_enemy(char* name, EnemyType enemy_type, size_t difficulty);

void battle(Player* player, Enemy* enemy);

void print_player_info(const Player player);

void print_enemy_info(const Enemy enemy);	
#endif // !GAME_FUNCITONS_H
  • 源文件部分 

game_functions.c

#define _CRT_SECURE_NO_WARNINGS
#include "game_functions.h"
#include <stdio.h>
#include <string.h>

void create_player(Player* player, char* name, ItemType item) {
	strcpy(player->name, name);
	player->exp = 0;
	player->health = 100;
	player->level = 1;
	player->item = item;
	switch (item)
	{
	case Dual_Blades: 
		player->ability.strength = 1;
		break;
	case Staff: 
		player->ability.mana = 1;
		break;
	case Musket:
		player->ability.fire_power = 1;
		break;
	case Bow:
		player->ability.power = 1;
		break;
	}
}

Enemy generate_enemy(char* name, EnemyType enemy_type, size_t difficulty) {
	Enemy enemy;
	strcpy(enemy.name, name);
	switch (enemy_type)
	{
	case Goblin: 
		enemy.ability.strength = difficulty * 10;
		break;
	case Stone:
		enemy.ability.strength = difficulty * 20;
		break;
	case Dragon:
		enemy.ability.mana = difficulty * 100;
		break;
	}
	return enemy;
}

void battle(Player* player, Enemy* enemy) {
	printf("%s encounter a %s, then they begin to battle!\n", player->name, enemy->name);
	
	//假设每次玩家获胜
	player->exp += 50;
	printf("%s has defeated the %s and gains 50 exp!\n", player->name, enemy->name);
}

void print_player_info(const Player player) {
	printf("玩家 %s 数据如下:\n", player.name);
	printf("所持武器: ");
	switch (player.item)
	{
	case Dual_Blades: 
		puts("双刀");
		break;
	case Staff:
		puts("法杖");
		break;
	case Musket:
		puts("火枪");
		break;
	case Bow:
		puts("弓箭");
		break;
	}
	printf("生命值: %" PRIu32 ", 经验值: %" PRIu32 ", 等级: %" PRIu32 "\n", player.health, player.exp, player.level);
}

void print_enemy_info(const Enemy enemy) {
	printf("怪物信息.......\n");
}

 run_app.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include "game_functions.h"
#include "game_structs.h"
int main(void)
{
	Player player1;
	create_player(&player1, " 无敌至尊我的世界大王", Musket);
	Enemy Goblin_scout = generate_enemy("哥布林战士", Goblin, 4);
	battle(&player1, &Goblin_scout);
	print_enemy_info(Goblin_scout);
	print_player_info(player1);
}
  • 简单的输出 

代码输出: 

185.第09章节结束语_哔哩哔哩_bilibili

黑客帝国:若有完美,必有谎言

世界上没有完美的东西,c语言的东西也不可能完全讲完

理解 消化 训练 必不可少

186.字符串_哔哩哔哩_bilibili

char string[5] = "Hello";

printf("%s\n", string);

这里应该是6个长度,因为还有 '\0' 

建议:不定义长度arr[],或者长度非常长

数组类型的可以被修改 如果加了const就不可以修改

指针类型默认就不可以被修改

char* ptr = "Hello";

ptr[0] = 'A';

这样编译器不会报错但是会输出卡住

187.strcpy_s的用法_哔哩哔哩_bilibili

char str[100] = "hello";

// H e l l o \0 \0 \0 \0 \0 \0 \0

数组的会默认是0 ,字符串默认是 \0 

指针定义字符串前面加const 规范

const char* ptr = "Hello";

strcpy_s的用法

#include <stdio.h>
#include <string.h>

#define SIZE 50
int main(void)
{
	const char src[] = "Hello";

	char dest[SIZE];

	strcpy_s(dest, SIZE, src);

	printf("src = %s\n", src);
	printf("dest = %s\n",dest);
}

 strcpy_s需要传入三个参数,与strcpy不同,strcpy如果目标字符串小于源字符串则会出现溢出,而加入了_s之后,在二者中间需传入目标数组的大小size。

 188.选修(难、非入门):VS编译器捕获字符串异常_哔哩哔哩_bilibili

strcpy_s(dest, sizeof(dest), src);
printf("sizeof(dest) = %zu\n", sizeof(dest));
printf("sizeof(dest)/ sizeof(dest[0]) = %zu\n",

输出:

sizeof(dest) = 50
sizeof(dest)/ sizeof(dest[0]) = 50

在字符串中,sizeof(string)是字符串长度,因为是按字节来的,char类型就只占一个字节而已所以和sizeof(dest)/sizeof(dest[0])大小一样

 在实际开发过程中是有着异常捕捉,通过图中的一些操作,并调整Debug模式到Release模式来捕捉

189.strlen_哔哩哔哩_bilibili

strlen计算字符串长度,不包含 \0

char dest[50] = {}; //或者 { 0 }
strcpy_s(dest, sizeof(dest), "Hello")

这样初始化字符串很安全

190.strcat_s_哔哩哔哩_bilibili

字符串拼接

#include <stdio.h>
#include <string.h>

#define SIZE 50
int main(void)
{
	char dest[50] = { 0 };
	strcpy_s(dest, sizeof(dest), "Hello");
	printf("Before: %s\n", dest);

	char* src = ", World!\n";
	
	rsize_t dest_size = sizeof(dest) - strlen(dest) - 1;
	strcat_s(dest, dest_size, src);

	printf("After: %s", dest);
}

191.sprintf_s_哔哩哔哩_bilibili

C语言标准 —— C89(C90)、C99、C11、C17、C2X_c2x标准-CSDN博客

c99就是1999年修订的 c11就是2011年修订的

在c11中将微软写的strcat_s sprintf_s等_s的添加到了标准中,也有没添加的,比如strtok_s

#include <stdio.h>
#include <string.h>

int main(void)
{
	char dest[50] = { 0 };
	int number = 10;
	double pi = 3.141592653589793238f;
	int ret = sprintf_s(dest, sizeof(dest), "Integer: %d, Double: %.2f", number, pi);
	if (ret >= 0)
	{
		printf("%s\n", dest);
	}
}

 sprintf_s将格式化的字符串复制到dest中,有一个返回值ret,如果ret >=0则success

192.strncpy_s_哔哩哔哩_bilibili

strncpy_s和strcpy_s类似只不过多了一个最大可复制数

#include <stdio.h>
#include <string.h>

int main(void)
{
	char dest[50] = { 0 };

	const char* src = "Hello, World!\n";

	int max_copiable_number = 11;
	errno_t result = strncpy_s(dest, sizeof(dest), src, max_copiable_number);
	if (result == 0)
	{
		printf("%s", dest);
	}
	return 0;
}

 

193.strncat_s_哔哩哔哩_bilibili

#include <stdio.h>
#include <string.h>

int main(void)
{
	char dest[20];
	strcpy_s(dest, sizeof(dest), "Slasher");
	char* src = ",World!\n";
	size_t max_append = 6;
	int result = strncat_s(dest, sizeof(dest), src, max_append);
	if (result == 0)
	{
		printf("Concatenated string: %s", dest);
	}
	else {
		printf("Error concatenating string!\n");
	}

}

strncat_s :追加字符串,有字数限制

194.gets_s_哔哩哔哩_bilibili

gets没有字符长度限制,在c11删除,增加gets_s

#include <stdio.h>
#include <string.h>

int main(void)
{
	char str[100];
	printf("Please enter your string: ");
	if (gets_s(str, sizeof(str)) == NULL) {
	
		printf("Error input!");
	}
	else {
		printf("Your entered string: %s\n", str);
	}
}

 195.strtok_s_哔哩哔哩_bilibili

在编程中,"token"(标记)通常指的是代码中最小的语法单元,它是源代码在编译或解释过程中被识别并处理的基本元素。

strtok_s用于将字符串按要求delimit(分隔符)来分割。

#include <stdio.h>
#include <string.h>

int main(void)
{
	char str[] = "Minecraft is a sandbox game";
	char* delim = " ";
	char* context = NULL;

	char* token = strtok_s(str, delim, &context);
	while (token != NULL)
	{
		printf("Token: %s\n", token);
		token = strtok_s(NULL, delim, &context);
	}
}

 strtok_s中的context 是保存strtok_s函数在其内部的上下文指针,用于指向下一个小字符串

它是一个二级指针,并且规定是要有具体的指向,在这里我们定义一个一级指针context,在传值时传入context的地址作为二级指针,这样这个二级指针就指向了一级指针就有了明确的指向。

196.strcmp_哔哩哔哩_bilibili

但凡具有字符串修改的基本都有_s

strlen strcmp这种不修改的没有

ASCII 表 | 菜鸟教程

strcmp是两个字符串相减

apple<banana

hello<hey

按照ASCII一个字符一个字符对比

#include <stdio.h>
#include <string.h>

int main(void)
{
	const char str1[] = "Minecraft is a sandbox game";    //a97 = A65 +32
	const char str2[] = "Minecraft is a sandbox game";
	const char str3[] = "Minecraft is A sandbox game";  
	const char str4[] = "Minedraft is a sandbox game";
	const char *arr[] = { str1,str2,str3,str4 };
	char** ptr = arr;
	for(ptr; *ptr<=str4; ptr++)
	{ 
		if (strcmp(str1, *ptr) == 0)
		{
			printf("There are equal!\n");
		}
		if (strcmp(str1, *ptr) > 0)
		{
			printf("The value of the first is greater than the second.\n");
		}
		if (strcmp(str1, *ptr) < 0)
		{
			printf("The value of the first is less than the second.\n");
		}
	}
	ptr--;


	
}

这里是一个不严谨的指针二级字符串指针 

char** 是指向指针的指针,而 { str1, str2, str3, str4 } 是一个初始化列表,它是用于初始化数组或结构体的。然而,char** 不能直接这样初始化,它需要指向一个指针数组(即 char* 数组)。所以,char** 不能直接用来存储多个指向 char 的指针

 197.strncmp_哔哩哔哩_bilibili

#include <stdio.h>
#include <string.h>

int main(void)
{
	const char str1[] = "Minecraft is a sandbox game";    //a97 = A65 +32
	const char str2[] = "Minecraft is a sandbox game";
	const char str3[] = "Minecraft is A sandbox game";  
	const char str4[] = "Minedraft is a sandbox game";
	const char *arr[] = { str1,str2,str3,str4 };
	char** ptr = arr;
	size_t num = 5;
	for(ptr; *ptr<=str4; ptr++)
	{ 
		if (strncmp(str1, *ptr, num) == 0)
		{
			printf("There are equal!\n");
		}
		if (strncmp(str1, *ptr, num) > 0)
		{
			printf("The value of the first is greater than the second.\n");
		}
		if (strncmp(str1, *ptr, num) < 0)
		{
			printf("The value of the first is less than the second.\n");
		}
	}
	ptr--;


	
}

 strncmp可以比较前maxCount个字符 

198.strchr与strrchr_哔哩哔哩_bilibili

#include <stdio.h>
#include <string.h>

int main(void)
{
	//strchr
	//strrchr    //str_reverse_char   reverse:逆转
	const char str[] = "SlasherMan";
	char to_find = 'a';
	char* ptr_str = strchr(str, to_find);
	printf("ptr_str: %s\n", ptr_str);
	printf("Finding it! '%c'of index: %td\n", to_find, ptr_str - str);

	char* ptr_strr = strrchr(str, to_find);
	printf("ptr_strr: %s\n", ptr_strr);
	printf("Finding it! reverse'%c'of index: %td\n", to_find, ptr_strr - str);
}

strchr找到字符并返回字符及其后面的字符串

strrchr从后面找

 199.strstr_哔哩哔哩_bilibili

strstr:

//strstr查找子字符串在不在父字符串之中
//如果在返回在父字符串中指向子字符串位置的指针

#include <stdio.h>
#include <string.h>

int main(void)
{
	//strstr查找子字符串在不在父字符串之中
	//如果在返回在父字符串中指向子字符串位置的指针

	const char text[] = "SlasherMan is a handsome boy";
	const char sub[] = "handsome";   //Subprogram子程序
	char* result = strstr(text, sub);
	if (result != NULL)
	{
		printf("\"%s\" is in \"%s\"\n", sub, text);
	}
	else {
		printf("Not Found!\n");
	}
}

200.strspn与strcspn_哔哩哔哩_bilibili

//strspn strcspn
//strspn(str1, str2)  检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标
//strcspn(str1, str2) 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符

#include <stdio.h>
#include <string.h>

int main(void)
{
	//strspn strcspn
	//strspn(str1, str2)  检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标
	//strcspn(str1, str2) 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符
	const char* str1 = "Hello, World! SlasherMan";
	const char* str2 = "Hello,World";
	
	size_t len = strspn(str1, str2);
	printf("len: %zu\n", len);

	const char* name = "filenam???e.md";
	const char* invaild_name = "/\\<>? \"':*";
	len = strcspn(name, invaild_name);
	if (len < strlen(name))
	{
		printf("invaild name!");
	}
	else {
		printf("This name is vaild");
	}
	
}

201.再次废话_哔哩哔哩_bilibili2025/1/18

关于字符串指针和指针指向字符串数组

#include <stdio.h>
#include <string.h>

int main(void)
{
	//推荐使用const char str[]
	char str[] = "Hello";
	char* ptr = str;
	ptr[2] = 'S';
	printf("指针指向字符串: %s\n", ptr);
	printf("指针指向字符串: %s\n\n", str);

	const char const_str[] = "Hello";
	char* ptr_const_str = const_str;
	ptr[2] = 'S';
	printf("指针指向const字符串: %s\n", ptr_const_str);
	printf("指针指向const字符串: %s\n\n", ptr_const_str);

	char* ptr_str = "World";
	printf("字符串指针:%s\n", ptr_str);
	ptr_str[0] = "A";
	printf("字符串指针:%s\n", ptr_str);

}

总结:

        使用指针指向变量字符串数组时可以通过指针修改字符串

        使用字符串指针时不可以修改(默认const)

        使用指针指向const字符串数组时修改无效

202.案例(略难):关于string.h_哔哩哔哩_bilibili 2025/1/19

写一个案例实现四个功能

  • 给一句话替换里面的某个单词
  • 统计字符
  • 单词计数,一句话到底有多少个单词
  • 独特的单词提取
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TEXT_SIZE 100
#define WORD_SIZE 50
#define DELIMS " /.,?!\n"

//给一句话替换里面的某个单词
//统计字数
//单词计数,一句话到底有多少个单词
//独特的单词提取

void replaceText(const char* text, const char* oldWord, const char* newWord, const char* result);
int countChar(const char* text, char Target);
int countWords(const char* text);
void extractUniqueWords(const char* text, char uniqueWords[][WORD_SIZE], int* uniqueCounts);
int main(void)
{
	char text[TEXT_SIZE] = "This is a simple text, the text is for testing.";

	char replacedText[TEXT_SIZE] = { 0 };

	char* oldWord = "text";

	char* newWord = "example";

	char countCharTarget = 's';

	char uniqueWords[TEXT_SIZE][WORD_SIZE] = { 0 };

	int uniqueCounts = 0;

	replaceText(text, oldWord, newWord, replacedText);
	printf("replacedText: %s\n", replacedText);

	int charCount = countChar(text, countCharTarget);
	printf("'%c' appears %d times\n", countCharTarget, charCount);

	int wordsCount = countWords(text);
	printf("This text has %d words\n", wordsCount);

	extractUniqueWords(replacedText, uniqueWords, &uniqueCounts);
	puts("Total words:");
	for (size_t i = 0; i < uniqueCounts; ++i)
	{
		printf("%s\n", uniqueWords[i]);
	}
	return 0;
}
void replaceText(const char* text, const char* oldWord, const char* newWord, char* result)
{
	char buffer[TEXT_SIZE] = {0};
	const char* pos = text;
	const char* temp = text;
	size_t oldLen = strlen(oldWord);
	while ((temp = strstr(pos, oldWord)) != NULL)
	{
		strncat_s(buffer, TEXT_SIZE, pos, temp - pos);
		strcat_s(buffer, TEXT_SIZE, newWord);
		pos = temp + oldLen;
	}
	strcat_s(buffer, TEXT_SIZE, pos);
	strcpy_s(result, TEXT_SIZE, buffer);
}
int countChar(const char* text, char target)
{
	int count = 0;
	while (*text)
	{
		if (*text == target)
		{
			count++;
		}
		text++;
	}
	return count;
}
int countWords(const char* text)
{
	int count = 0;
	char buffer[TEXT_SIZE] = { 0 };
	strcpy_s(buffer, TEXT_SIZE, text);

	char* context = NULL;

	char* token = strtok_s(buffer, DELIMS, &context);
	while (token != NULL)
	{
		count++;
		token = strtok_s(NULL, DELIMS, &context);
	}
	return count;
}
void extractUniqueWords(const char* text, char uniqueWords[][WORD_SIZE], int* uniqueCounts)
{
	char buffer[TEXT_SIZE] = { 0 };
	char* context = NULL;
	strcpy_s(buffer, TEXT_SIZE, text);
	char* token = strtok_s(buffer, DELIMS, &context);
	while (token != NULL)
	{
		int found = 0;
		for (int j = 0; j < *uniqueCounts; ++j)
		{
			if (strcmp(token, uniqueWords[j]) == 0)
			{
				found = 1;
				break;
			}
			
		}
		if (!found)
		{
			strcpy_s(uniqueWords[*uniqueCounts], WORD_SIZE, token);
			(*uniqueCounts)++;
		}
		token = strtok_s(NULL, DELIMS, &context);
	}
}

203.第10章结束语_哔哩哔哩_bilibili

204.输入输出初步认识_哔哩哔哩_bilibili

  1. 输入流的数据首先被暂存到一个叫做缓冲区的内存区域。
  2. 缓冲区作用:提高数据的存储效率。它是批量读取而不是一个字节一个字节读取,设定一个缓冲区,一下子交给下一级(比如:内存 等)
  3. 缓冲区类似快递打包站

stdin:

        当通过从键盘上输入数据的时候,数据首先被操作系统接收,然后存储在标准输入的缓冲区当中等待被程序读取。

stdout:

        将输入发送到外部,发送到显示屏,文件等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值