来深入一下长度不受限制的字符串函数
//1. strlen字符串长度函数
#include
#include
int main() {
if (strlen("abc") - strlen("abcdef") > 0) {
printf(">\n");//返回值是无符号类型 size_t(unsigned int) 所以打印结果是>
}
else {
printf("<\n");
}
return 0;
}
// 模拟实现strlen函数
#include
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
#include
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
#include
int main() {
char array1[30] = "hello \0#########";
char array2[] = "world";
strcat(array1, array2);
printf("%s\n", array1);//调试你会发现 '\0' 也追加进去了 并且在 \0 后面追加的
return 0;
}
//模拟实现 strcat
#include
#include
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
#include
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
#include
int main() {
char array1[20] = "abcdef";
char array2[] = "ghk";
strncpy(array1, array2, 5);//不够五个 将\0拷贝过去
printf("%s\n", array1);
return 0;
}
//2.strncat 追加函数
#include
#include
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
#include
#include
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
#include
int main() {
char array[] = "[email protected]";
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
#include//strerror头文件
#include //errno头文件 全局错误码
#include //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
#include //isdigit头文件
int main() {
char judge = '#';
int number = isdigit(judge);//isdigit 如果是数字字符0-9返回非零值 不是返回0
printf("%d\n", number);
return 0;
}
//字符转换函数
#include
#include//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
#include//memcpy头文件
#include
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
#include//内存函数头文件
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
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
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
#include //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
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
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
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 函数声明
#pragma once
#include
#include//动态用到的头文件
#include
#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;
}
源.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;
}
//-------------动态内存分配(比固态更灵活,动态内存开辟在堆区)
#include
#include//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
#include
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
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
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
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
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
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
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
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
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
//#include//atoi头文件
#include
#include// isspace isdigit头文件
#include//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
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
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
#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
#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
#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//库文件包含,C语言库中提供的函数 <>
#ifndef __AddressBook_H__
#define __AddressBook_H__
#include"stdio.h"//本地文件包含,自定义的函数使用 "" //这样效率有点低了
int main()
{
printf("HelloWorld\n");
return 0;
}
#endif