Day01

笔记

1	typedef使用
1.1 起别名 - 简化struct关键字
1.2 区分数据类型
1.3 提高代码移植性
2 void使用
2.1 不可以利用void创建变量 无法给无类型变量分配内存
2.2 用途:限定函数返回值,函数参数
2.3 void * 万能指针 可以不通过强制类型转换就转成其他类型指针
3 sizeof用法
3.1 本质:不是一个函数,是一个操作符
3.2 返回值类型 unsigned int无符号整型
3.3 用途:可以统计数组长度
4 变量的修改方式
4.1 直接修改
4.2 间接修改
4.3 对自定义数据类型做练习
5 内存分区
5.1 运行前
5.1.1 代码区 共享 只读
5.1.2 数据区 存放数据:全局变量 、静态变量、常量
5.1.2.1 已初始化数据区 data
5.1.2.2 未初始化数据区 bss
5.2 运行后
5.2.1 栈 符合先进后出数据结构,编译器自动管理分配和释放,有限容量
5.2.2 堆 容量远远大于栈,不是无限。手动开辟 malloc 手动释放 free
6 栈区
6.1 符合先进后出数据结构
6.2 注意事项:不要返回局部变量的地址,局部变量在函数执行之后就被释放了,释放的内存就没有权限取操作了,如果操作结果未知
7 堆区
7.1 利用malloc在堆区创建数据
7.2 利用free释放堆区
7.3 注意事项:主调函数没有分配内存,被调函数需要用更高级的指针去修饰低级指针,进行分配内存
8 static 和extern 区别
8.1 特点:在运行前分配内存,程序运行结束 生命周期结束 ,在本文件内都可以使用静态变量
8.2 extern 可以提高变量作用域
9 常量
9.1 const修饰的变量
9.1.1 全局变量
9.1.1.1 直接修改 失败 ,间接修改 语法通过,运行失败,受到常量区保护
9.1.2 局部变量
9.1.2.1 直接修改 失败 , 间接修改 成功,放在栈上
9.2 字符串常量
9.2.1 vs 将多个相同字符串常量看成一个
9.2.2 不可以修改字符串常量
9.2.3 ANSI并没有制定出字符串是否可以修改的标准,根据编译器不同,可能最终结果也是不同的
10

Code

  • 01 typedef使用
//#define _CRT_SECURE_NO_WARNINGS   //VS下使用传统库函数 会建议用_s更安全函数,如果不用会报错误 C4996
#include<stdio.h> //标准 i input 输入 o output 输出
#include<string.h> //对字符串处理 strcat strstr strcmp strcpy
#include<stdlib.h> //malloc free //1、typedef使用 可以简化 struct关键字
//可以起别名
//struct Person
//{
// char name[64];
// int age;
//};
//
////语法: typedef 原名 别名
//typedef struct Person myPerson; typedef struct Person
{
char name[64];
int age;
}myPerson; void test01()
{
struct Person p = { "aaa", 10 }; myPerson p2 = { "bbb", 20 }; } //2、区分数据类型
void test02()
{
typedef char * PCHAR;
PCHAR p1, p2;
//char *p1, p2; char *p3, *p4;
} //3、提高移植性
typedef int MYINT;
void test03()
{
MYINT a = 10; MYINT b = 10; } //main 函数 程序入口
int main1(){ //char buf[1024];
//strcpy(buf, "hello world");
//printf("%s\n", buf); system("pause"); //阻塞 请按任意键继续 return EXIT_SUCCESS; //返回成功退出 0
}
  • 02 void使用.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、void 无类型,不可以通过void创建变量,原因无法给void无类型变量分配内存
void test01()
{
//void a = 10;
} //2、用途: 限定函数返回值,限定函数参数
void test02(void)
{
//return 10;
} //3、void * 万能指针 不管几级指针,任意类型指针都是4个字节
void test03()
{
//printf("size of void* = %d\n", sizeof(void *));
void * p = NULL; int * pInt = NULL;
char * pChar = NULL; pChar = (char *)pInt; pChar = p; //万能指针 可以不通过强制类型转换就转成其他类型指针 } int main(){ //test02(10);
test03(); system("pause");
return EXIT_SUCCESS;
}
  • 03 sizeof用法.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、sizeof本质 是不是一个函数???不是函数 ,而是 操作符
void test01()
{
printf("size of int = %d\n", sizeof(int)); double d = 3.14; printf("size of d = %d\n", sizeof d);
} //2、sizeof 返回值类型 无符号整型 unsigned int
void test02()
{
//unsigned int a = 10;
////当unsigned int 和 int做运算,会转换成统一 unsigned int数据类型
//if (a - 20 > 0)
//{
// printf("大于0\n");
//}
//else
//{
// printf("小于0\n");
//} if ( sizeof(int) - 5 >0)
{
printf("大于0\n");
}
else
{
printf("小于0\n");
} } //sizeof 用途:统计数组长度 , 当数组名做函数参数时候,会退化为指针,指向数组中第一个元素的位置
void calculateArray(int arr[])
{
printf("array length = %d\n", sizeof(arr));
} void test03()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; calculateArray(arr);
//printf("array length = %d\n", sizeof(arr)); } int main(){ //test01();
//test02();
test03(); system("pause");
return EXIT_SUCCESS;
}
  • 04 变量的修改方式.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //变量的修改方式
void test01()
{
//1、直接修改
int a = 10;
a = 20; //2、间接修改
int * p = &a;
*p = 30;
printf("a = %d\n", a); } struct Person
{
char a; //0 ~ 3
int b; //4 ~ 7
char c; //8 ~ 11
int d; //12 ~ 15
};
void test02()
{
struct Person p = { 'a', 10, 'b', 20 }; //直接修改 d属性
p.d = 1000; //间接修改 d属性
//struct Person * pp = &p;
////pp->d = 1000; //printf("%d\n", pp);
//printf("%d\n", pp+1); char * pp = &p; *(int*)(pp + 12) = 2000; printf("d属性为: %d\n", *(int*)(pp + 12));
printf("d属性为: %d\n", *(int*)((int*)pp + 3));
} int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 05 栈区.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> int * myFunc()
{
int a = 10;
return &a;
} void test01()
{
//局部变量a早已被释放,因此我们没有权限操作这块内存空间
int * p = myFunc();
printf("*p = %d\n", *p);
printf("*p = %d\n", *p);
} char * getString()
{
char str[] = "hello world";
return str;
} void test02()
{
char * p = NULL;
p = getString();
printf("%s\n", p);
} int main(){ //test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 06 堆区.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> int * getSpace()
{
int * p = malloc(sizeof(int)* 5);
if (p == NULL)
{
return;
}
for (int i = 0; i < 5; i++)
{
p[i] = i + 100;
}
return p;
} void test01()
{
int * p = getSpace();
for (int i = 0; i < 5; i++)
{
printf("%d\n", p[i]);
} //手动开辟 手动释放
free(p);
p = NULL; } void allocateSpace(char * pp)
{
char * temp = malloc(100);
memset(temp, 0, 100);
strcpy(temp, "hello world");
pp = temp;
} void test02()
{
char * p = NULL;
allocateSpace(p);
printf("%s\n", p);
} void allocateSpace2(char ** pp)
{
char * temp = malloc(100);
memset(temp, 0, 100);
strcpy(temp, "hello world"); *pp = temp; } void test03()
{
char * p = NULL;
allocateSpace2(&p);
printf("%s\n", p); if (p != NULL)
{
free(p);
p = NULL;
}
} int main(){
//test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
  • 07 static和extern区别

    • test.c
extern int g_a = 1000; //在C语言下  全局变量前都隐式加了关键字 extern
  • 07 static和extern区别.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //static 静态变量
// 特点:在运行前分配内存,程序运行结束 生命周期结束 ,在本文件内都可以使用静态变量
// 全局作用域 a
static int a = 10; void test01()
{
//局部作用域 b
static int b = 20;
} int main(){ //告诉编译器 下面代码中出现 g_a 不要报错,是外部链接属性,在其他文件中
extern int g_a; printf("g_a = %d\n", g_a); system("pause");
return EXIT_SUCCESS;
}
  • 08 常量区.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、const修饰的变量
//全局变量
const int a = 10; //常量区 ,间接修改 语法通过,运行失败,原因:受到常量区的保护
void test01()
{
//a = 100; int * p = &a;
*p = 100;
printf("%d\n", a); } void test02()
{
const int b = 10; //存放在栈上,通过间接修改是可以成功的 //b = 20; int * p = &b;
*p = 20;
printf("%d\n", b); //在C语言中 const修饰的局部变量 称为伪常量,不可以初始化数组
//int arr[b];
} //2、字符串常量 void test03()
{
char * p1 = "hello world";
char * p2 = "hello world";
char * p3 = "hello world";
printf("%d\n", &"hello world");
printf("%d\n", p1);
printf("%d\n", p2);
printf("%d\n", p3);
} void test04()
{
char * str = "hello world";
str[0] = 'x'; } int main(){
//test01();
//test02();
//test03();
test04();
system("pause");
return EXIT_SUCCESS;
}

Day02

笔记

1	宏函数
1.1 #define MYADD(x,y) ((x) + (y)
1.2 将一些频繁短小的函数 写成宏函数
1.3 宏函数优点:以空间换时间
1.4 普通函数有入栈、出栈时间开销
2 函数调用流程
2.1 局部变量、函数形参、函数返回地址.. 入栈 和 出栈
3 调用惯例
3.1 主调函数和被调函数必须要有一致约定,才能正确的调用函数,这个约定我们称为调用惯例
3.2 调用惯例 包含内容: 出栈方、参数传递顺序、函数名称修饰
3.3 C/C++下默认调用惯例: cdecl 从右到左 ,主调函数管理出栈
4 栈的生长方向和内存存放方向
4.1 栈生长方向
4.1.1 栈底 --- 高地址
4.1.2 栈顶 --- 低地址
4.2 内存存放方向
4.2.1 高位字节数据 --- 高地址
4.2.2 低位字节数据 --- 低地址
4.2.3 小端对齐方式
5 空指针和野指针
5.1 空指针
5.1.1 不能向NULL或者非法内存拷贝数据
5.2 野指针
5.2.1 指针变量未初始化
5.2.2 指针释放后未置空
5.2.3 指针操作超越变量作用域
5.3 空指针可以重复释放、野指针不可以重复释放
6 指针的步长
6.1 +1之后跳跃的字节数
6.2 解引用 解出的字节数
6.3 自定义结构体做步长练习
6.3.1 通过 offsetof( 结构体名称, 属性) 找到属性对应的偏移量
6.3.2 offsetof 引入头文件 #include<stddef.h>
7 指针的间接赋值
7.1 三大条件
7.1.1 一个普通变量+指针变量( 实参+形参)
7.1.2 建立关系
7.1.3 通过* 操作内存
7.2 利用Qt实现 操作地址 修改内存
8 指针做函数参数的输入输出特性
8.1 输入特性
8.1.1 在主调函数中分配内存,被调函数使用
8.1.2 分配在栈上和堆区
8.2 输出特性
8.2.1 在被调函数中分配内存,主调函数使用
9 字符串强化训练
9.1 字符串结束标志 \0
9.2 sizeof 和 strlen
9.3 拷贝字符串 利用三种方式
9.3.1 利用[]
9.3.2 利用指针
9.3.3 while (*dest++ = *src++){}
9.4 翻转字符串
9.4.1 利用[ ]
9.4.2 利用指针
10 sprintf使用
10.1 格式化字符串
10.2 sprintf(目标字符串,格式化内容,占位参数…)
10.3 返回值 有效字符串长度

Code

  • 01 宏函数.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> #define MYADD(x,y) ((x) + (y)) //1、宏函数需要加小括号修饰,保证运算的完整性
//2、通常会将频繁、短小的函数 写成宏函数
//3、宏函数 会比普通函数在一定程度上 效率高,省去普通函数入栈、出栈时间上的开销
// 优点: 以空间 换时间 void test01()
{
printf("%d\n", MYADD(10, 20) * 20 ); // ((10) + (20)) * 20 } int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 02 变量传递分析.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> char * func()
{
char * p = malloc(10); //堆区数据,只要没有释放,都可以使用
int c = 10;//在func中可以使用,test01和main都不可以使用
return p;
} void test01()
{
int b = 10; // 在test01 、func 可以使用,在main中不可以用 func();
} int main(){ int a = 10; //在main 、test01 、 func中都可以使用 test01(); system("pause");
return EXIT_SUCCESS;
}
  • 03 栈的生长方向和内存存放方向.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、栈的生长方向
void test01()
{
int a = 10; //栈底 高地址
int b = 10;
int c = 10;
int d = 10; //栈顶 低地址 printf("%d\n", &a);
printf("%d\n", &b);
printf("%d\n", &c);
printf("%d\n", &d); } //2、内存存放方向
void test02()
{
int a = 0x11223344; char * p = &a; printf("%x\n", *p); //44 低位字节数据 低地址
printf("%x\n", *(p+1)); //33 高位字节数据 高地址
} int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
  • 04 空指针和野指针.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、不能向NULL或者非法内存拷贝数据
void test01()
{
//char *p = NULL;
////给p指向的内存区域拷贝内容
//strcpy(p, "1111"); //err //char *q = 0x1122;
////给q指向的内存区域拷贝内容
//strcpy(q, "2222"); //err } //指针操作超越变量作用域
int * doWork()
{
int a = 10;
int * p = &a;
return p;
} //2、野指针出现情况
void test02()
{
//2.1 指针变量未初始化
/*int * p;
printf("%d\n",*p);*/ //2.2 指针释放后未置空
char * str = malloc(100);
free(str);
//记住释放后 置空,防止野指针出现
//str = NULL; //free(str);
//2.3 空指针可以重复释放、野指针不可以重复释放 //2.4 指针操作超越变量作用域
int * p = doWork();
printf("%d\n", *p);
printf("%d\n", *p);
} int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 05 指针的步长.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <stddef.h> //offsetof的头文件 //1、指针的步长代表 指针+1之后跳跃的字节数
void test01()
{
char * p = NULL;
printf("%d\n", p);
printf("%d\n", p+1); double * p2 = NULL;
printf("%d\n", p2);
printf("%d\n", p2 + 1); } //2、解引用的时候,解出的字节数量
void test02()
{
char buf[1024] = { 0 }; int a = 1000; memcpy(buf + 1, &a, sizeof(int)); char * p = buf;
printf("%d\n", *(int *)(p+1)); } //步长练习,自定义数据类型练习
struct Person
{
char a; // 0 ~ 3
int b; // 4 ~ 7
char buf[64]; // 8 ~ 71
int d; // 72 ~ 75
}; void test03()
{
struct Person p = { 'a', 10, "hello world", 20 }; printf("d属性的偏移量: %d\n", offsetof(struct Person, d)); printf("d属性的值为:%d\n", *(int *)((char *)&p + offsetof(struct Person, d)));
} int main(){
//test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
  • 06 指针的间接赋值.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //间接赋值三大条件
// 一个普通变量 和 指针变量 或 一个实参和一个形参
// 建立关系
// * 操作内存 void changeValue(int *a) // int * a = &a2;
{
*a = 1000;
} void test01()
{
int a = 10;
int * p = NULL; p = &a; *p = 100; int a2 = 10;
changeValue(&a2); printf("%d\n", a2);
printf("%d\n", &a2);
} int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 07 指针做函数参数的输入输出特性.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //输入特性: 主调函数分配内存,被调函数使用
void func(char * p)
{
strcpy(p, "hello world");
} void test01()
{
//在test01中分配了内存,分配在栈上
char buf[1024] = { 0 }; func(buf); printf("%s\n", buf); } void printString(char * str)
{
printf("%s\n", str + 6); } void test02()
{
char * p = malloc(sizeof(char)* 64);
memset(p, 0, 64);
strcpy(p, "hello world");
printString(p); if (p != NULL)
{
free(p);
p = NULL;
}
} //输出特性:在被调函数中分配内存,主调函数使用
void allocateSpace(char ** pp)
{
char * str = malloc(sizeof(char)* 64);
memset(str, 0, 64);
strcpy(str, "helloworld"); *pp = str;
}
void test03()
{
char * p = NULL; allocateSpace(&p); printf("%s\n", p); } int main(){ //test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
  • 08 字符串指针强化训练.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void test01()
{
////字符串结束标志位 \0
//char str1[] = { 'h', 'e', 'l', 'l', 'o' ,'\0'};
//printf("%s\n", str1); //char str2[100] = { 'h', 'e', 'l', 'l', 'o' };
//printf("%s\n", str2); //char str3[] = "hello";
//printf("%s\n", str3);
//printf("sizeof str:%d\n", sizeof(str3)); //6
//printf("strlen str:%d\n", strlen(str3)); //5 //char str4[100] = "hello";
//printf("sizeof str:%d\n", sizeof(str4));
//printf("strlen str:%d\n", strlen(str4)); //char str5[] = "hello\0world";
//printf("%s\n", str5);
//printf("sizeof str5:%d\n", sizeof(str5)); //12
//printf("strlen str5:%d\n", strlen(str5)); //5 char str6[] = "hello\012world";
printf("%s\n", str6);
printf("sizeof str6:%d\n", sizeof(str6)); //12
printf("strlen str6:%d\n", strlen(str6)); //11 } //字符串拷贝实现
//1、利用[] 实现
void copyString01(char * dest , char * src)
{
int len =strlen(src);
for (int i = 0; i < len;i++)
{
dest[i] = src[i];
}
dest[len] = '\0';
} //2、利用字符串指针
void copyString02(char * dest, char * src)
{
while (*src != '\0')
{
*dest = *src; dest++;
src++;
}
*dest = '\0';
} //3
void copyString03(char * dest, char * src)
{
while (*dest++ = *src++){}
} void test02()
{
char * str = "hello world"; char buf[1024]; //copyString01(buf, str);
//copyString02(buf, str);
copyString03(buf, str);
printf("%s\n", buf);
} //字符串翻转
void reverseString01(char * str)
{
//利用[]
int len = strlen(str);
int start = 0;
int end = len - 1; while (start < end)
{
char temp = str[start];
str[start] = str[end];
str[end] = temp; start++;
end--;
} } void reverseString02(char * str)
{
int len = strlen(str);
char * start = str;
char * end = str + len - 1; while (start < end)
{
char temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
} void test03()
{
char str[] = "abcdefg"; //reverseString01(str);
reverseString02(str);
printf("%s\n",str);
} int main(){ //test01();
//test02();
test03(); system("pause");
return EXIT_SUCCESS;
}
  • 09 sprintf的使用.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void test01()
{
char buf[1024];
//memset(buf, 0, 1024);
//sprintf(buf, "今天 %d 年 %d月 %d日", 2018, 6, 30); //printf("%s\n", buf); //2. 拼接字符串
//memset(buf, 0, 1024);
//char str1[] = "hello";
//char str2[] = "world";
//int len = sprintf(buf, "%s%s", str1, str2); //返回值是字符串长度 不包含\0
//printf("buf:%s len:%d\n", buf, len); //3. 数字转字符串
//memset(buf, 0, 1024);
//int num = 100;
//sprintf(buf, "%d", num);
//printf("buf:%s\n", buf); int num = 100;
//设置宽度 右对齐
memset(buf, 0, 1024);
sprintf(buf, "%8d", num);
printf("buf:%s\n", buf);
////设置宽度 左对齐
memset(buf, 0, 1024);
sprintf(buf, "%-8d", num);
printf("buf:%sa\n", buf);
//转成16进制字符串 小写
memset(buf, 0, 1024);
sprintf(buf, "0x%x", num);
printf("buf:%s\n", buf); //转成8进制字符串
memset(buf, 0, 1024);
sprintf(buf, "0%o", num);
printf("buf:%s\n", buf); } int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}

Day03

笔记

1	calloc 和 realloc
1.1 calloc 和malloc 都是在堆区分配内存
1.2 与malloc不同的是,calloc会将空间初始化为0
1.3 calloc(个数,大小)
1.4 realloc 重新分配内存
1.4.1 如果重新分配的内存比原来大,那么不会初始化新空间为0
1.4.2 先看后续空间,如果足够,那么直接扩展
1.4.3 如果后续空闲空间不足,那么申请足够大的空间,将原有数据拷贝到新空间下,释放掉原有空间,将新空间的首地址返回
1.4.4 如果重新分配的内存比原来小,那么释放后序空间,只有权限操作申请空间
2 sscanf的使用
%*s或%*d 跳过数据
%[width]s 读指定宽度的数据
%[a-z] 匹配a到z中任意字符(尽可能多的匹配)
%[aBc] 匹配a、B、c中一员,贪婪性
%[^a] 匹配非a的任意字符,贪婪性
%[^a-z] 表示读取除a-z以外的所有字符
2.1 练习
2.1.1 将 ip 分别截取到 num1 到 num4中
2.1.2 将 123456#zhangtao@abcde 中截取中间的zhangtao有效数据
3 查找子串
3.1 实现mystrstr 自己查找子串功能
4 指针的易错点
4.1 越界
4.2 指针叠加会不断改变指针指向
4.3 返回局部变量地址
4.4 同一块内存释放多次(不可以释放野指针)
5 const使用场景
5.1 const使用 修饰形参 防止误操作
6 二级指针做函数参数的输入输出特性
6.1 二级指针做函数参数的输入特性
6.1.1 创建在堆区
6.1.2 创建在栈区
6.2 二级指针做函数参数的输出特性
6.2.1 被调函数分配内存,主调函数使用
7 二级指针练习-文件操作
7.1 读取配置文件信息 ,并且将信息存放到 数组中
7.2 注意: 释放堆区,关闭文件
8 位运算
8.1 按位取反 ~ 0变1 1 变0
8.2 按位与 & 全1为1 一0为0
8.3 按位或 | 全0为0 一1为1
8.4 按位异或 ^ 相同为0 不同为1
9 位移运算
9.1 左移运算 << X 乘以2 ^ X
9.2 右移运算 >> X 除以 2 ^X
9.2.1 有些机器用0填充高位
9.2.2 有些机器用1填充高位
9.2.3 如果是无符号,都是用0填充
10

Code

  • 01 calloc和realloc.c
//calloc
void test01()
{
//int * p = malloc(sizeof(int)* 10); int * p = calloc(10, sizeof(int)); //calloc 分配在堆区,与malloc不同的是 calloc会初始化数据为0 for (int i = 0; i < 10;i++)
{
printf("%d\n", p[i]);
} if ( p != NULL)
{
free(p);
p = NULL;
} } //realloc 重新分配内存
void test02()
{
int * p = malloc(sizeof(int)* 10); for (int i = 0; i < 10;i++)
{
p[i] = i + 100;
} for (int i = 0; i < 10; i++)
{
printf("%d\n", p[i]);
} printf("%d\n", p); //如果重新分配的内存比原来大,那么不会初始化新空间为0
p = realloc(p, sizeof(int)* 20); printf("%d\n", p); for (int i = 0; i < 20; i++)
{
printf("%d\n", p[i]);
} //如果重新分配的内存比原来小,那么释放后序空间,只有权限操作申请空间
p = realloc(p, sizeof(int)* 5);
printf("%d\n", p);
printf("%d\n", p[0]);
printf("%d\n", p[5]);
} int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
  • 02 sscanf的使用.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1、%*s或%*d 跳过数据
void test01()
{
char * str = "12345abcde"; char buf[1024] = { 0 };
sscanf(str, "%*d%s", buf); printf("%s\n", buf); }
void test02()
{
char * str = "abcde12345"; //忽略遇到空格或者 \t 代表忽略结束 char buf[1024] = { 0 }; sscanf(str, "%*[a-z]%s", buf);
//sscanf(str, "%*s%s", buf); printf("%s\n", buf);
} //%[width]s 读指定宽度的数据
void test03()
{
char * str = "12345abcde"; char buf[1024] = { 0 }; sscanf(str, "%6s", buf);
printf("%s\n", buf);
} //%[a-z] 匹配a到z中任意字符(尽可能多的匹配)
void test04()
{
char * str = "12345abcdeaaa"; char buf[1024] = { 0 }; sscanf(str,"%*d%[a-c]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf); } void test05()
{
char * str = "12345abcdeaaa"; char buf[1024] = { 0 }; sscanf(str, "%[0-9]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf);
} //%[aBc] 匹配a、B、c中一员,贪婪性
void test06()
{
char * str = "abcCdef";
char buf[1024] = { 0 }; sscanf(str, "%[abC]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf);
} //%[^a] 匹配非a的任意字符,贪婪性
void test07()
{
char * str = "abcCdef";
char buf[1024] = { 0 }; sscanf(str, "%[^C]", buf);
printf("%s\n", buf); } //%[^a-z] 表示读取除a-z以外的所有字符
void test08()
{
char * str = "abcCdef123456";
char buf[1024] = { 0 }; sscanf(str, "%[^0-9]", buf);
printf("%s\n", buf);
} //练习1
void test09()
{
char * ip = "127.0.0.1"; int num1 = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0; sscanf(ip, "%d.%d.%d.%d", &num1, &num2, &num3, &num4); printf("%d\n", num1);
printf("%d\n", num2);
printf("%d\n", num3);
printf("%d\n", num4); } //练习2
void test10()
{
char * str = "abcdef#zhangtao@123456"; char buf[1024] = { 0 }; sscanf(str, "%*[^#]#%[^@]", buf); printf("%s\n", buf); } //1. 已给定字符串为: helloworld@itcast.cn,请编码实现helloworld输出和itcast.cn输出。
void test11()
{
char * str = "helloworld@itcast.cn"; char buf1[1024] = { 0 };
char buf2[1024] = { 0 }; sscanf(str, "%[a-z]%*[@]%s", buf1, buf2); printf("%s\n", buf1);
printf("%s\n", buf2); } int main(){ //test01();
//test02();
//test03();
//test04();
//test05();
//test06();
//test07();
//test08();
//test09();
//test10();
test11();
system("pause");
return EXIT_SUCCESS;
}
  • 03 查找子串.c
/*
算法优化
memcmp(str , subStr ,3 ) == 0;
*/
int myStrstr(char * str , char * subStr)
{
int num = 0;
while (*str != '\0')
{
if (*str != *subStr)
{
num++;
str++;
continue;
} //创建两个临时指针 做二次对比
char * tmpStr = str;
char * tmpSubstr = subStr; while (*tmpSubstr != '\0')
{
if (*tmpStr != *tmpSubstr)
{
//匹配失败
num++;
str++;
break;
}
tmpStr++;
tmpSubstr++;
}
if (*tmpSubstr == '\0')
{
//匹配成功
return num;
} } return -1;
} void test01()
{
char * str = "abdnfcdefgdfasdfaf"; int ret = myStrstr(str, "dnf"); if ( ret != -1)
{
printf("找到了子串,位置为:%d\n", ret);
}
else
{
printf("未找到子串\n");
} } int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 04 指针易错点.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void test01()
{
char * p = malloc(sizeof(char)* 64);
char * pp = p; //通过创建临时指针操作内存,防止出错
for (int i = 0; i < 10;i++)
{
*pp = i + 97;
printf("%c\n", *pp);
pp++; //更改指针位置,释放出错
} if (p!= NULL)
{
free(p);
} } int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 05 const的使用场景.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct Person
{
char name[64]; // 0 ~ 63
int age; // 64 ~ 67
int Id; // 68 ~ 71
double score; // 72 ~79
}; //将struct Person p 改为 struct Person * p 节省资源
//const使用 修饰形参 防止误操作
void showPerson(const struct Person *p)
{
//p.age = 100;
//p->age = 100;
//printf("姓名: %s 年龄: %d 学号 %d 分数 %f\n", p.name, p.age, p.Id, p.score);
printf("姓名: %s 年龄: %d 学号 %d 分数 %f\n", p->name, p->age, p->Id, p->score);
} void test01()
{
struct Person p = { "Tom", 18, 1, 60 }; showPerson(&p); } int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}
  • 06 二级指针做函数参数的输入特性.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void printArray(int ** pArray , int len)
{
for (int i = 0; i < len;i++)
{
printf("%d\n", *pArray[i]);
} } void test01()
{
//创建在堆区
int ** pArray = malloc(sizeof(int *)* 5); //在栈上创建5个数据
int a1 = 10;
int a2 = 20;
int a3 = 30;
int a4 = 40;
int a5 = 50; pArray[0] = &a1;
pArray[1] = &a2;
pArray[2] = &a3;
pArray[3] = &a4;
pArray[4] = &a5; //打印数组
printArray(pArray, 5); //释放堆区数据
if (pArray != NULL)
{
free(pArray);
pArray = NULL;
} } void freeSpace(int **pArray , int len)
{
for (int i = 0; i < len;i++)
{
free(pArray[i]);
pArray[i] = NULL;
} } void test02()
{
//创建在栈区
int * pArray[5]; for (int i = 0; i < 5;i++)
{
pArray[i] = malloc(4);
*(pArray[i]) = 10 + i;
} printArray(pArray, 5); //释放堆区
freeSpace(pArray,5); } int main(){ //test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 07 二级指针做函数参数的输出特性.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void allocateSpace(int ** p)
{
int * temp = malloc(sizeof(int)* 10);
for (int i = 0; i < 10;i++)
{
temp[i] = 100 + i;
}
*p = temp; } void printArray(int ** pArray, int len)
{
for (int i = 0; i < len;i++)
{
printf("%d\n", (*pArray)[i]);
}
} void freeSpace(int ** pArray)
{
if (*pArray != NULL)
{
free(*pArray);
*pArray = NULL;
}
} void test01()
{
int * p = NULL;
allocateSpace(&p); printArray(&p, 5); freeSpace(&p, 5); if (p == NULL)
{
printf("空指针\n");
}
else
{
printf("野指针\n");
}
} int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 08 二级指针练习-文件读写.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //获取有效行数
int getFileLines(FILE * pFile)
{
if (pFile == NULL)
{
return -1;
} char buf[1024] = { 0 };
int lines = 0;
while (fgets(buf,1024,pFile) != NULL)
{
//printf("%s", buf);
lines++;
} //将文件光标置首
fseek(pFile, 0, SEEK_SET);
return lines; } //读取数据放入到pArray中
void readFileData(FILE * pFile, int len, char ** pArray)
{
if (pFile == NULL)
{
return;
}
if (len <= 0 )
{
return;
}
if ( pArray ==NULL)
{
return;
}
char buf[1024] = { 0 };
int index = 0;
while (fgets(buf, 1024, pFile) != NULL)
{
/*
aaaaaaaaaa
bbbb
cccccc
*/
int currentLen = strlen(buf) +1 ;
char * currentStrP = malloc(sizeof(char)* currentLen);
strcpy(currentStrP, buf);
pArray[index++] = currentStrP; memset(buf, 0, 1024);
} } void showFileData(char ** pArray, int len)
{
for (int i = 0; i < len;i++)
{
printf("%d行的数据为 %s", i + 1, pArray[i]);
} } void test01()
{
//打开文件
FILE * pFile = fopen("./test.txt", "r");
if (pFile == NULL)
{
printf("文件打开失败\n");
return;
} //统计有效行数
int len = getFileLines(pFile);
//printf("文件的有效行数为:%d\n", len);
char ** pArray = malloc(sizeof(char *)* len); //读取文件中的数据并且放入到 pArray中
readFileData(pFile, len, pArray); //读取数据
showFileData(pArray , len); //释放堆区内容
for (int i = 0; i < len; i++)
{
if (pArray[i] != NULL)
{
free(pArray[i]);
pArray[i] = NULL;
}
}
free(pArray);
pArray = NULL;
//关闭文件
fclose(pFile);
} int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}
  • 09 位运算.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //按位取反 ~
void test01()
{
int num = 2; // 010 取反
// 101 源码
//计算机用补码方式存数据 110 + 1 = 111 printf("~num = %d\n", ~num); // -3 } //按位与 &
void test02()
{
int num = 123;
if ( (num & 1 ) == 1 )
{
printf("num为奇数\n");
}
else
{
printf("num为偶数\n");
} } //按位或 |
void test03()
{
int num1 = 5;
int num2 = 3;
printf("num1 | num2 = %d\n", num1 | num2); // 7 } //按位异或 ^
void test04()
{
int num1 = 5;
int num2 = 9; /*int tmp = num1;
num1 = num2;
num2 = tmp;*/ num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2; //num1 = num1 + num2;
//num2 = num1 - num2;
//num1 = num1 - num2; printf("num1 = %d\n", num1);
printf("num2 = %d\n", num2); } //左移运算 <<
void test05()
{
int num = 20; // 20 * 2 ^ 2;
printf("%d\n", num <<= 2);
}
//右移运算 >>
void test06()
{
int num = 20; // 20 / 2
printf("%d\n", num >>= 1);
} int main(){ //test01();
//test02();
//test03();
//test04();
//test05();
test06();
system("pause");
return EXIT_SUCCESS;
}

Day04

笔记

1	一维数组名
1.1 除了两种特殊情况外,都是指向数组第一个元素的指针
1.1.1 特殊情况1 sizeof 统计数组长度
1.1.2 特殊情况2 对数组名取地址,数组指针,步长整个数组长度
1.2 数组名是指针常量,指针的指向不可以修改的,而指针指向的值可以改
1.3 传参数时候,int arr[] 可读性更高
1.4 数组索引下标可以为负数
2 数组指针的定义方式
2.1 先定义出数组类型,再通过类型定义数组指针变量
2.1.1 typedef int(ARRARY_TYPE)[5]; //ARRARY_TYPE 代表存放5个int类型元素的数组 的数组类型
2.2 先定义数组指针类型,再通过类型定义数组指针变量
2.2.1 typedef int(*ARRARY_TYPE)[5];
2.3 直接定义数组指针变量
2.3.1 int(* p )[5] = &arr;
3 二维数组名
3.1 二维数组名 除了两种特殊情况外,是指向第一个一维数组的 数组指针
3.2 两种特殊情况
3.2.1 sizeof 统计二维数组大小
3.2.2 对数组名称取地址 int(*p)[3][3] = &arr
3.3 二维数组做函数参数
3.3.1 //void printArray(int (*array)[3], int row, int col)
3.3.2 //void printArray(int array[][3], int row ,int col)
3.3.3 void printArray(int array[3][3], int row ,int col) 可读性比较高
3.4 数组指针 和 指针数组?
3.4.1 数组指针: 指向数组的指针
3.4.2 指针数组: 由指针组成数组
4 指针数组排序
4.1 选择排序
4.1.1 例如从小到大
4.1.2 开始认定最小值下标为i,从j = i+1的位置起找真实最小值下标,如果计算的真实最小值下标与i不等,互换元素
4.2 利用选择排序实现 指针数组 从大到小排序
4.2.1 字符串对比
4.2.2 if ( strcmp(pArr[max],pArr[j]) == -1)
5 结构体基本概念
5.1 加typedef 可以给结构体起别名
5.2 不加typedef ,可以直接创建一个结构体变量
5.3 结构体声明 可以是匿名
5.4 在栈上创建和在堆区创建结构体
5.5 在栈上和堆区创建结构体变量数组
6 结构体深浅拷贝
6.1 系统提供的赋值操作是 浅拷贝 – 简单值拷贝,逐字节拷贝
6.2 如果结构体中有属性 创建在堆区,就会出现问题,在释放期间,一段内存重复释放,一段内存泄露
6.3 解决方案:自己手动去做赋值操作,提供深拷贝
7 结构体嵌套一级指针练习
7.1 在堆区创建一个 结构体指针数组
7.1.1 malloc(sizeof(struct Person *) *3 )
7.2 在堆区创建出结构体变量
7.2.1 malloc(sizeof(struct Person))
7.3 在堆区创建出具体姓名
7.3.1 malloc(sizeof(char )*64);
7.4 打印数据
7.5 释放数组

Code

  • 01 一维数组名.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// int arr[] 可读性更高
void printArray(int arr[] , int len) //int arr[]等价于 int * arr
{
for (int i = 0; i < len;i++)
{
printf("%d\n", arr[i]);
}
} void test01()
{ //一维数组名是不是指针?
int arr[5] = { 1, 2, 3, 4, 5 }; printf("%d\n", sizeof(arr)); //有两种特殊情况,一维数组名不是 指向第一个元素的指针
//1、sizeof
//2、对数组名取地址 得到数组指针 步长是整个数组长度 //printf("%d\n", &arr);
//printf("%d\n", &arr + 1); //int * p = arr;
int len = sizeof(arr) / sizeof(int);
printArray(arr, len); //arr数组名 它是一个指针常量 指针的指向不可以修改的,而指针指向的值可以改 int * const a ;
//arr[0] = 1000;
//arr = NULL; //数组索引 可不可以为负数 答案:可以
int * p = arr;
p = p + 3;
printf("%d\n",p[-1]); //给人看的
printf("%d\n", *(p - 1)); //给机器看的
} int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}
  • 02 数组指针的定义方式.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //数组指针的定义方式
//1、先定义数组类型,再通过类型定义数组指针
void test01()
{
int arr[5] = { 1, 2, 3, 4, 5 }; typedef int(ARRARY_TYPE)[5];//ARRARY_TYPE 代表存放5个int类型元素的数组 的数组类型 /*ARRARY_TYPE arr2;
for (int i = 0; i < 5;i++)
{
arr2[i] = 100 + i;
}
for (int i = 0; i < 5; i++)
{
printf("%d\n", arr2[i]);
}*/ ARRARY_TYPE * arrP = &arr;
// *arrP == arr == 数组名
for (int i = 0; i < 5;i++)
{
printf("%d\n", (*arrP)[i]);
} } void test02()
{
//先定义数组指针类型,再通过类型定义数组指针
int arr[5] = { 1, 2, 3, 4, 5 }; typedef int(*ARRARY_TYPE)[5]; ARRARY_TYPE arrP = &arr; for (int i = 0; i < 5; i++)
{
printf("%d\n", (*arrP)[i]);
} } void test03()
{
//直接定义数组指针变量 int arr[5] = { 1, 2, 3, 4, 5 }; int(* p )[5] = &arr; for (int i = 0; i < 5;i++)
{
printf("%d\n", (*p)[i]);
} } int main(){ //test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
  • 03 二维数组名.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void test01()
{
//可读性高
int arr[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9}
}; /*int arr2[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int arr3[][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };*/ //除了两种特殊情况外 ,二维数组名称是 指向第一个一维数组 数组指针
//特殊情况1 sizeof
//特殊情况2 对数组名取地址 &arr 获取的是二维数组的 数组指针 int(*p)[3][3] = &arr;
//printf("%d\n", sizeof(arr)); int(*pArray)[3] = arr; //访问二维数组中的 6 这个元素
printf("%d\n", arr[1][2]); //给人看
printf("%d\n", *(*(pArray + 1) + 2)); //给机器看 } //void printArray(int (*array)[3], int row, int col)
//void printArray(int array[][3], int row ,int col)
void printArray(int array[3][3], int row ,int col) //array[3][3] 等价于 一维数组指针 int (*array)[3]
{
for (int i = 0; i < row; i ++)
{
for (int j = 0; j < col;j ++)
{
//printf("%d ", array[i][j]);
printf("%d ", *(*(array + i) + j));
}
printf("\n");
}
} //二维数组做函数参数
void test02()
{
int arr[3][3] = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
}; printArray(arr, 3, 3); } int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
  • 04 指针数组排序.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void mySort(int arr[] , int len)
{ for (int i = 0; i < len;i++)
{
int min = i; //记录最小值的下标为i
for (int j = i + 1; j < len;j++)
{
if (arr[min]> arr[j])
{
//更新真实最小值下标
min = j;
}
}
//判断真实最小值下标 是否与开始认定的i相等,如果不等,交换元素
if (i != min)
{
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
} } void printArray(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
} void test01()
{
//从小到大 排序 利用选择排序
int arr[] = { 2, 5, 1, 3, 4 }; int len = sizeof(arr) / sizeof(int);
mySort(arr, len); printArray(arr, len); } //void selectSort(char ** pArr, int len)
void selectSort(char * pArr[] , int len)
{
for (int i = 0; i < len;i++)
{
int max = i;
for (int j = i + 1; j < len;j++)
{
//if (pArr[max] < pArr[j])
if ( strcmp(pArr[max],pArr[j]) == -1)
{
max = j;
}
}
if ( i != max)
{
char * tmp = pArr[i];
pArr[i] = pArr[max];
pArr[max] = tmp;
}
} } void printArray2(char ** pArr, int len)
{
for (int i = 0; i < len;i++)
{
printf("%s\n", pArr[i]);
}
} void test02()
{
//对指针数组进行排序,排序的算法利用 选择排序 从大到小
char * pArray[] = { "aaa", "fff", "bbb", "ddd", "ccc", "eee" }; int len = sizeof(pArray) / sizeof(char*);
selectSort(pArray, len); printArray2(pArray, len);
} int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 05 结构体的基本使用.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //struct Person
//{
// char name[64];
// int age;
//};
//typedef struct Person myPerson; typedef struct Person
{
char name[64];
int age;
}myPerson; void test01()
{
struct Person p = { "Tom", 18 };
myPerson p2 = { "Jerry", 20 };
} struct Person2
{
char name[64];
int age;
}myPerson2 = { "aaa", 20 }; void test02()
{
printf("姓名:%s 年龄%d\n", myPerson2.name, myPerson2.age);
} //匿名结构体
struct
{
char name[64];
int age;
}myPerson3 = { "bbb", 30 }; void test03()
{
printf("姓名:%s 年龄%d\n", myPerson3.name, myPerson3.age);
} //结构体创建
void test04()
{
//创建在栈上
struct Person p = { "aaa", 10 }; printf("姓名:%s 年龄: %d\n", p.name, p.age); //创建在堆区
struct Person * p2 = malloc(sizeof(struct Person));
strcpy(p2->name, "bbb");
p2->age = 20; printf("姓名:%s 年龄: %d\n", p2->name, p2->age); if (p2 != NULL)
{
free(p2);
p2 = NULL;
}
} void printArray( struct Person personArray[] ,int len)
{
for (int i = 0; i < len;i++)
{
printf("姓名 : %s ,年龄 : %d \n", personArray[i].name, personArray[i].age);
}
}
//结构体变量数组创建
void test05()
{
//在栈上分配内存
struct Person persons[] =
{
{ "aaa", 10 },
{ "bbb", 20 },
{ "ccc", 30 },
{ "ddd", 40 },
};
int len = sizeof(persons) / sizeof(struct Person);
//printArray(persons, len); //在堆区分配内存
struct Person * pArray = malloc(sizeof(struct Person) * 4);
for (int i = 0; i < 4;i++)
{
sprintf(pArray[i].name, "name_%d", i + 1);
pArray[i].age = 18 + i;
} printArray(pArray, 4); if (pArray != NULL)
{
free(pArray);
pArray = NULL;
} } int main(){
//test02();
//test03();
//test04();
test05(); system("pause");
return EXIT_SUCCESS;
}
  • 06 结构体深浅拷贝.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct Person
{
char name[64];
int age;
}; void test01()
{
struct Person p1 = { "Tom", 18 }; struct Person p2 = { "Jerry", 20 }; p1 = p2; printf("p1的姓名:%s 年龄 :%d\n", p1.name, p1.age); printf("p2的姓名:%s 年龄 :%d\n", p2.name, p2.age); } struct Person2
{
char * name; int age;
}; void test02()
{
struct Person2 p1;
p1.name = malloc(sizeof (char)* 64);
strcpy(p1.name, "Tom");
p1.age = 18; struct Person2 p2;
p2.name = malloc(sizeof (char)* 128);
strcpy(p2.name, "Jerry");
p2.age = 28; //p1 = p2; //系统提供的赋值操作是简单的浅拷贝 ,我们需要做手动赋值,提供深拷贝 //////// 手动赋值 ////////
//先释放原来堆区的内容
if (p1.name != NULL)
{
free(p1.name);
p1.name = NULL;
}
//在堆区创建内存
p1.name = malloc(strlen(p2.name) + 1);
strcpy(p1.name, p2.name);
p1.age = p2.age; //////////////////// printf("p1的姓名:%s 年龄 :%d\n", p1.name, p1.age); printf("p2的姓名:%s 年龄 :%d\n", p2.name, p2.age); if (p1.name != NULL)
{
free(p1.name);
p1.name = NULL;
} if (p2.name != NULL)
{
free(p2.name);
p2.name = NULL;
} } int main(){ //test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 07 结构体嵌套一级指针练习.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct Person
{
char * name;
int age;
}; struct Person ** allocateSpace()
{
struct Person ** temp = malloc(sizeof(struct Person *) * 3); for (int i = 0; i < 3;i++)
{
//创建结构体内存
temp[i] = malloc(sizeof(struct Person)); //将结构体姓名 创建在堆区
temp[i]->name = malloc(sizeof(char)* 64); //给姓名赋值
sprintf(temp[i]->name, "name_%d", i + 1); temp[i]->age = 18 + i;
} return temp;
} void printPerson(struct Person ** pArray, int len)
{
for (int i = 0; i < len;i++)
{
printf("姓名: %s 年龄: %d\n", pArray[i]->name, pArray[i]->age);
} } void freeSpace(struct Person ** pArray , int len)
{
if ( pArray == NULL)
{
return;
}
if (len <= 0)
{
return;
} for (int i = 0; i < 3;i++)
{
if (pArray[i]->name != NULL)
{
printf("%s被释放了\n", pArray[i]->name);
free(pArray[i]->name);
pArray[i]->name = NULL;
} free(pArray[i]);
pArray[i] = NULL;
} free(pArray);
pArray = NULL;
} void test01()
{
struct Person ** pArray = NULL; pArray = allocateSpace(); //打印数组
printPerson(pArray, 3); //释放内存
freeSpace(pArray,3);
pArray = NULL;
} int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}

Day05

Code

  • 01 结构体嵌套二级指针练习.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct Teacher
{
char * name;
char ** students;
}; void allocateSpace(struct Teacher*** teachers)
{
if (teachers == NULL)
{
return;
} //开辟内存
struct Teacher ** ts = malloc(sizeof(struct Teacher *) * 3); //给每个老师分配内存
for (int i = 0; i < 3;i++)
{
ts[i] = malloc(sizeof(struct Teacher)); //给老师的姓名分配内存
ts[i]->name = malloc(sizeof(char)* 64); //给老师起名称
sprintf(ts[i]->name, "Teacher_%d", i + 1); //给学生的数组分配内存
ts[i]->students = malloc(sizeof(char *)* 4); //给学生的姓名开辟内存 以及赋值
for (int j = 0; j < 4;j++)
{
ts[i]->students[j] = malloc(sizeof(char)* 64);
sprintf(ts[i]->students[j], "%s_Student_%d", ts[i]->name, j + 1);
}
} *teachers = ts;
} void printTeachers(struct Teacher** pArray)
{
if (pArray == NULL)
{
return;
} for (int i = 0; i < 3;i++)
{
printf("%s\n", pArray[i]->name);
for (int j = 0; j < 4;j++)
{
printf(" %s\n", pArray[i]->students[j]);
}
} } void freeSpace(struct Teacher ** pArray)
{
if (pArray == NULL)
{
return;
} for (int i = 0; i < 3;i++)
{
//先释放老师姓名
if (pArray[i]->name != NULL)
{
free(pArray[i]->name);
pArray[i]->name = NULL;
} //释放学生姓名
for (int j = 0; j < 4;j++)
{
if (pArray[i]->students[j] != NULL)
{
free(pArray[i]->students[j]);
pArray[i]->students[j] = NULL;
}
}
//释放学生的数组
if (pArray[i]->students != NULL)
{
free(pArray[i]->students);
pArray[i]->students = NULL;
} //释放老师
if (pArray[i] != NULL)
{
free(pArray[i]);
pArray[i] = NULL;
} } //释放老师数组
if (pArray != NULL)
{
free(pArray);
pArray = NULL;
} } void test01()
{
struct Teacher ** pArray = NULL;
//开辟内存
allocateSpace(&pArray); //打印数组
printTeachers(pArray); //释放数组
freeSpace(pArray);
pArray = NULL; } int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}
  • 02 结构体偏移量.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <stddef.h> struct Teacher
{
char a; //0 ~ 3
int b; //4 ~ 7
}; void test01()
{ struct Teacher t1;
struct Teacher *p = &t1; printf("b的属性偏移量为:%d\n", (int)&(p->b) - (int)p); printf("b的属性偏移量为:%d\n", offsetof(struct Teacher, b)); } //通过偏移量 操作内存
void test02()
{
struct Teacher t1 = { 'a', 10 }; printf("t1.b = %d\n", *(int *)((char *)&t1 + offsetof(struct Teacher, b))); printf("t1.b = %d\n", *(int *)((int *)&t1 + 1 )); } struct Teacher2
{
char a;
int b;
struct Teacher c;
}; void test03()
{
struct Teacher2 t1 = { 'a', 10, 'b', 20 }; int offset1 = offsetof(struct Teacher2, c);
int offset2 = offsetof(struct Teacher, b); printf("%d\n", *(int*)((char*)&t1 + offset1 + offset2)); printf("%d\n", (( struct Teacher * )((char*)&t1 +offset1))->b ); } int main(){
//test01();
//test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
  • 03 内存对齐.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> #pragma pack(show) //查看当前对齐模数 ,对齐模数是可以改的,改成2的N次方 //第一个属性开始 从0开始偏移
//第二个属性开始 要放在 该类型的大小 与 对齐模数比 取小的值 的整数倍
//所有属性都计算完后,再整体做二次偏移,将整体计算的结果 要放在 结构体最大类型 与对齐模数比 取小的值的 整数倍上 typedef struct _STUDENT{ int a; //0 ~ 3 0 ~ 3
char b; //4 ~ 7 4
double c; //8 ~ 15 5 ~ 12
float d; //16 ~ 19 13 ~ 16
}Student; void test01()
{
printf("sizeof student = %d\n", sizeof(Student)); } //结构体嵌套结构体时候,子结构体放在该结构体中最大类型 和对齐模数比 的整数倍上即可
typedef struct _STUDENT2{
char a; // 0 ~ 7
Student b; // 8 ~ 31
double c; //32 ~ 39
}Student2; void test02()
{
printf("sizeof student = %d\n", sizeof(Student2));
} int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 04 文件读写回顾.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //按照字符读写文件:fgetc(), fputc() void test01()
{
//写文件 FILE * f_write = fopen("./test01.txt", "w+"); if (f_write == NULL)
{
return;
} char buf[] = "this is first test";
for (int i = 0; i < strlen(buf);i++)
{
fputc(buf[i], f_write);
} fclose(f_write); //读文件
FILE * f_read = fopen("./test01.txt", "r");
if (f_read == NULL)
{
return;
}
char ch;
while ( (ch = fgetc(f_read)) != EOF ) // EOF Enf of File
{
printf("%c", ch);
}
fclose(f_read);
} //按照行读写文件:fputs(), fgets()
void test02()
{
//写文件
FILE * f_write = fopen("./test02.txt", "w");
if (f_write == NULL)
{
return;
}
char * buf[] =
{
"锄禾日当午\n",
"汗滴禾下土\n",
"谁知盘中餐\n",
"粒粒皆辛苦\n",
}; for (int i = 0; i < 4;i++)
{
fputs(buf[i], f_write);
} fclose(f_write); //读文件
FILE * f_read = fopen("./test02.txt", "r");
if (f_read == NULL)
{
return;
}
while ( !feof(f_read ))
{
char buf[1024] = { 0 };
fgets(buf, 1024, f_read);
printf("%s", buf);
} fclose(f_read);
} //按照块读写文件:fread(), fwirte()
struct Hero
{
char name[64];
int age;
};
void test03()
{
//写文件 wb二进制方式
FILE * f_write = fopen("./test03.txt", "wb");
if (f_write == NULL)
{
return;
} struct Hero heros[4] =
{
{ "亚瑟" , 18 },
{ "赵云", 28 },
{ "妲己", 19 },
{ "孙悟空", 99 },
}; for (int i = 0; i < 4;i++)
{
//参数1 数据地址 参数2 块大小 参数3 块个数 参数4 文件指针
fwrite(&heros[i], sizeof(struct Hero), 1, f_write);
} fclose(f_write); //读文件
FILE * f_read = fopen("./test03.txt", "rb"); // read binary
if (f_read == NULL)
{
return;
} struct Hero temp[4];
//参数1 数据地址 参数2 块大小 参数3 块个数 参数4 文件指针
fread(&temp, sizeof(struct Hero), 4, f_read); for (int i = 0; i < 4;i++)
{
printf("姓名:%s 年龄:%d \n", temp[i].name, temp[i].age);
}
fclose(f_read);
} //按照格式化读写文件:fprintf(), fscanf()
void test04()
{
//写文件
FILE * f_write = fopen("./test04.txt", "w");
if (f_write == NULL)
{
return;
} fprintf(f_write, "hello world %d年 %d月 %d日", 2018, 7, 5); //关闭文件
fclose(f_write); //读文件
FILE * f_read = fopen("./test04.txt", "r");
if (f_read == NULL)
{
return;
} char buf[1024] = { 0 };
while (!feof(f_read))
{
fscanf(f_read, "%s", buf);
printf("%s\n", buf);
} fclose(f_read);
} //按照随机位置读写文件
void test05()
{
FILE * f_write = fopen("./test05.txt", "wb");
if (f_write == NULL)
{
return;
} struct Hero heros[4] =
{
{ "亚瑟", 18 },
{ "赵云", 28 },
{ "妲己", 19 },
{ "孙悟空", 99 },
}; for (int i = 0; i < 4; i++)
{
//参数1 数据地址 参数2 块大小 参数3 块个数 参数4 文件指针
fwrite(&heros[i], sizeof(struct Hero), 1, f_write);
} fclose(f_write); //读取妲己数据
FILE * f_read = fopen("./test05.txt", "rb");
if (f_read == NULL)
{
//error 宏 //printf("文件打开失败\n");
perror("文件打开失败");
return;
} //创建临时结构体
struct Hero temp; //改变文件光标位置
fseek(f_read, sizeof(struct Hero) *2, SEEK_SET); fseek(f_read, -(long)sizeof(struct Hero) * 2, SEEK_END); rewind(f_read); //将文件光标置首 fread(&temp, sizeof(struct Hero), 1, f_read); printf("姓名: %s 年龄: %d\n", temp.name, temp.age); fclose(f_read); } int main(){
//test01();
//test02();
//test03();
//test04();
test05(); system("pause");
return EXIT_SUCCESS;
}
  • 05 文件读写注意事项.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //注意事项1
void test01()
{
FILE * f_read = fopen("./test.txt", "r");
if (f_read == NULL)
{
return;
} char ch; #if 0
//aaaaaaaaaEOF
// |
while ( !feof(f_read))
{
ch = fgetc(f_read); if (feof(f_read))
{
break;
} printf("%c", ch);
}
#endif while ( ( ch = fgetc(f_read)) != EOF)
{
printf("%c", ch);
} fclose(f_read); } //注意事项2
struct Hero
{
char * name; //如果属性开辟到堆区,不要存指针到文件中,要将指针指向的内容存放到文件中
int age;
}; int main(){
test01();
//printf(" aaaaa\n");
//printf("%caaaaa\n",EOF);
system("pause");
return EXIT_SUCCESS;
}
  • 06 配置文件读写案例
  • 头文件--config.h
 #define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //配置信息 结构体
struct ConfigInfo
{
char key[64];
char value[64];
}; //获取有效行数
int getFileLines(char * filePath); //检测当前行是否是有效信息
int isValidLines(char *str); //解析文件
void parseFile(char * filePath, int lines , struct ConfigInfo ** configinfo); //根据key获取对应value
char * getInfoByKey(char * key, struct ConfigInfo * configinfo, int len); //释放内存
void freeConfigInfo(struct ConfigInfo * configinfo);
  • 源文件--config.c
#include "config.h"

int getFileLines(char * filePath)
{
FILE * file = fopen(filePath, "r");
if (file == NULL)
{
return -1;
} char buf[1024] = { 0 };
int lines = 0;
while ( fgets(buf,1024,file) != NULL)
{
if (isValidLines(buf))
{
lines++;
}
memset(buf, 0, 1024);
} return lines; fclose(file);
} int isValidLines(char *str)
{
if (strchr(str, ':') == NULL)
{
return 0; //返回假 代表无效行
} return 1;
} //解析文件
void parseFile(char * filePath, int lines, struct ConfigInfo ** configinfo)
{
struct ConfigInfo * info = malloc(sizeof(struct ConfigInfo) * lines); if (info == NULL)
{
return;
} FILE * file = fopen(filePath, "r");
if (file == NULL)
{
return;
}
char buf[1024] = { 0 };
int index = 0;
while ( fgets(buf,1024,file ) != NULL)
{
if (isValidLines(buf))
{
//有效信息 才去解析
//清空 key和value数组
//heroName:aaaa\n
memset(info[index].key, 0, 64);
memset(info[index].value, 0, 64);
char * pos = strchr(buf, ':');
strncpy(info[index].key, buf, pos - buf);
strncpy(info[index].value, pos + 1, strlen(pos + 1)-1);
index++;
}
memset(buf, 0, 1024);
}
*configinfo = info;
} char * getInfoByKey(char * key, struct ConfigInfo * configinfo, int len)
{
for (int i = 0; i < len;i++)
{
if (strcmp(key, configinfo[i].key) == 0)
{
return configinfo[i].value;
}
} return NULL;
} //释放内存
void freeConfigInfo(struct ConfigInfo * configinfo)
{
if (configinfo != NULL)
{
free(configinfo);
configinfo = NULL;
}
}
  • 源文件--06 配置文件读写案例.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "config.h" int main(){ char * filePath = "./config.txt";
int len = getFileLines(filePath);
printf("文件的有效行数为:%d\n", len); struct ConfigInfo * configInfo = NULL;
parseFile(filePath, len, &configInfo); //测试根据key获取value
printf("heroId = %s\n", getInfoByKey("heroId", configInfo, len));
printf("heroName = %s\n", getInfoByKey("heroName", configInfo, len));
printf("heroAtk = %s\n", getInfoByKey("heroAtk", configInfo, len));
printf("heroDef = %s\n", getInfoByKey("heroDef", configInfo, len));
printf("heroInfo = %s\n", getInfoByKey("heroInfo", configInfo, len)); //释放空间
freeConfigInfo(configInfo);
configInfo = NULL; system("pause");
return EXIT_SUCCESS;
}

Day08

笔记

1	链表
1.1 引出- 数组缺陷
1.1.1 数组是一个静态空间,一旦分配内存,就不可以动态扩展,空间可能分配多或者分配的少,操作不精准
1.1.2 对于头部的插入删除效率低
1.2 链表
1.2.1 由节点组成
1.2.2 而节点由 数据域 和 指针域组成
1.2.2.1 数据域是维护数据的
1.2.2.2 指针域 维护下一个节点的位置
1.2.3 链表可以解决数组的缺陷
1.2.4 链表的分类
1.2.4.1 静态链表、动态链表
1.2.4.2 单向链表、双向链表、单向循环链表、双向循环链表
2 静态链表和动态链表
2.1 静态链表分配在栈上
2.2 动态链表分配到堆区
2.3 实现链表的初始化以及遍历功能
3 链表基本使用
3.1 带头节点链表和不带头节点链表
3.1.1 带头好处:带着头节点的链表永远固定了头节点的位置
3.2 初始化链表 init_LinkList
3.3 遍历链表 foreach_LinkList
3.4 插入链表 insert_LinkList 利用两个辅助指针 实现插入
3.5 删除链表 delete_LinkList 利用两个辅助指针 实现删除
3.6 清空链表 clear_LinkList 将所有有数据节点释放掉,可以在使用
3.7 销毁链表 destroy_LinkList 将整个链表释放掉,不可以再使用
4 函数指针
4.1 函数名本质就是一个函数指针
4.2 可以利用函数指针 调用函数
5 函数指针定义方式
5.1 //1、先定义出函数类型,再通过类型定义函数指针
5.1.1 typedef void(FUNC_TYPE)(int, char);
5.2 //2、定义出函数指针类型,通过类型定义函数指针变量
5.2.1 typedef void( * FUNC_TYPE2)(int, char);
5.3 //3、直接定义函数指针变量
5.3.1 void(*pFunc3)(int, char) = func;
5.4 函数指针和指针函数
5.4.1 //函数指针 指向了函数的指针
5.4.2 //指针函数 函数返回值是指针的函数
5.5 函数指针数组
5.5.1 void(*pArray[3])();
6 函数指针做函数参数(回调函数)
6.1 利用回调函数实现打印任意类型数据
6.2 提供能够打印任意类型数组函数
6.3 利用回调函数 提供查找功能
7 作业:超难
7.1 提供一个函数,实现对任意类型的数组进行排序,排序规则利用选择排序,排序的顺序 用户可以自己指定
8

Code

  • 01 静态链表.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //节点的结构体
struct LinkNode
{
int num; //数据域
struct LinkNode * next; //指针域
}; void test01()
{
//创建节点
struct LinkNode node1 = { 10, NULL };
struct LinkNode node2 = { 20, NULL };
struct LinkNode node3 = { 30, NULL };
struct LinkNode node4 = { 40, NULL };
struct LinkNode node5 = { 50, NULL }; //建立关系
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node5; //遍历链表
struct LinkNode * pCurrent = &node1; while (pCurrent != NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
} } int main(){
test01(); system("pause");
return EXIT_SUCCESS;
}
  • 02 动态链表.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct LinkNode
{
int num;
struct LinkNode * next;
}; void test01()
{
//创建节点
struct LinkNode * node1 = malloc(sizeof(struct LinkNode));
struct LinkNode * node2 = malloc(sizeof(struct LinkNode));
struct LinkNode * node3 = malloc(sizeof(struct LinkNode));
struct LinkNode * node4 = malloc(sizeof(struct LinkNode));
struct LinkNode * node5 = malloc(sizeof(struct LinkNode)); //给数据域赋值
node1->num = 100;
node2->num = 200;
node3->num = 300;
node4->num = 400;
node5->num = 500; //建立关系
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = NULL; //遍历链表
struct LinkNode * pCurrent = node1;
while (pCurrent!=NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
} free(node1);
free(node2);
free(node3);
free(node4);
free(node5);
node1 = NULL;
node2 = NULL;
node3 = NULL;
node4 = NULL;
node5 = NULL;
} int main(){ test01(); system("pause");
return EXIT_SUCCESS;
}

03 链表的基本操作

  • 头文件--linkList.h
#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct LinkNode
{
int num;
struct LinkNode * next;
}; //初始化链表
struct LinkNode * initLinkList(); //遍历链表
void foreach_LinkList(struct LinkNode * pHeader); //插入链表
void insert_LinkList(struct LinkNode * pHeader, int oldVal, int newVal); //删除链表
void delete_LinkList(struct LinkNode * pHeader, int val); //清空链表
void clear_LinkList(struct LinkNode * pHeader); //销毁链表
void destroy_LinkList(struct LinkNode * pHeader);
  • 源文件--lintList.c
#include "linkList.h"

//初始化链表
struct LinkNode * initLinkList()
{
//创建头节点
struct LinkNode * pHeader = malloc(sizeof(struct LinkNode)); if (pHeader == NULL)
{
return NULL;
} //初始化头节点
//pHeader->num = -1; //头节点 不维护数据域
pHeader->next = NULL; //记录尾节点位置,方便插入新的数据
struct LinkNode * pTail = pHeader;
int val = -1;
while (1)
{
//让用户初始化几个节点,如果用户输入的是-1,代表插入结束
printf("请初始化链表,如果输入-1代表结束\n");
scanf("%d", &val); if (val == -1)
{
break;
} //如果输入不是-1 插入节点到链表中
struct LinkNode * newNode = malloc(sizeof(struct LinkNode));
newNode->num = val;
newNode->next = NULL; //更改指针的指向
pTail->next = newNode;
//更新新的尾节点的指向
pTail = newNode; } return pHeader;
} //遍历链表
void foreach_LinkList(struct LinkNode * pHeader)
{
if (pHeader == NULL)
{
return;
} struct LinkNode * pCurrent = pHeader->next; //指定第一个有真实数据的节点 while (pCurrent!=NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
} } //插入链表
void insert_LinkList(struct LinkNode * pHeader, int oldVal, int newVal)
{
if (pHeader == NULL)
{
return;
} //创建两个临时的节点
struct LinkNode * pPrve = pHeader;
struct LinkNode * pCurrent = pHeader->next; while (pCurrent != NULL)
{
if (pCurrent->num == oldVal)
{
break;
}
//如果没找到对应的位置,辅助指针向后移动
pPrve = pCurrent;
pCurrent = pCurrent->next;
} //创建新节点
struct LinkNode * newNode = malloc(sizeof(struct LinkNode));
newNode->num = newVal;
newNode->next = NULL; //建立关系
newNode->next = pCurrent;
pPrve->next = newNode; } //删除链表
void delete_LinkList(struct LinkNode * pHeader, int val)
{
if (pHeader==NULL)
{
return;
} //创建两个辅助指针变量
struct LinkNode * pPrev = pHeader;
struct LinkNode * pCurrent = pHeader->next; while (pCurrent != NULL)
{
if (pCurrent->num == val)
{
break;
}
//没有找到数据,辅助指针向后移动
pPrev = pCurrent;
pCurrent = pCurrent->next;
} if (pCurrent == NULL) //没有找到用户要删除的数据
{
return;
} //更改指针的指向进行删除
pPrev->next = pCurrent->next; //删除掉待删除的节点
free(pCurrent);
pCurrent = NULL; } //清空链表
void clear_LinkList(struct LinkNode * pHeader)
{
if (pHeader == NULL)
{
return;
} struct LinkNode * pCurrent = pHeader->next; while (pCurrent != NULL)
{
//先保存住下一个节点的位置
struct LinkNode * nextNode = pCurrent->next; free(pCurrent); pCurrent = nextNode;
} pHeader->next = NULL; } //销毁链表
void destroy_LinkList(struct LinkNode * pHeader)
{ if (pHeader == NULL)
{
return;
} //先清空链表
clear_LinkList(pHeader); //再释放头节点 free(pHeader);
pHeader = NULL; }
  • 源文件--03 链表的基本操作.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "linkList.h" void test01()
{
//初始化链表
struct LinkNode * pHeader = initLinkList(); //遍历链表
printf("遍历链表结果为:\n");
foreach_LinkList(pHeader); //插入链表
// 10 1000 2000 20 3000 30 500
insert_LinkList(pHeader, 20, 1000);
insert_LinkList(pHeader, 20, 2000);
insert_LinkList(pHeader, -1, 500);
insert_LinkList(pHeader, 30, 3000);
printf("插入链表后,遍历链表结果为:\n");
foreach_LinkList(pHeader); //删除链表
// 10 20 30 500
delete_LinkList(pHeader, 2000);
delete_LinkList(pHeader, 3000);
delete_LinkList(pHeader, 1000);
delete_LinkList(pHeader, -1);
printf("删除链表后,遍历链表结果为:\n");
foreach_LinkList(pHeader); //清空链表
clear_LinkList(pHeader);
printf("清空链表后,遍历链表结果为:\n");
insert_LinkList(pHeader, 111, 111);
insert_LinkList(pHeader, 222, 222);
insert_LinkList(pHeader, 333, 333);
foreach_LinkList(pHeader); //销毁链表
destroy_LinkList(pHeader);
pHeader = NULL; } int main(){ test01(); //printf("%d\n", test01); system("pause");
return EXIT_SUCCESS;
}
  • 04 函数指针的定义方式
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void func(int a ,char c)
{
printf("hello world\n");
} void test01()
{
//1、先定义出函数类型,再通过类型定义函数指针
typedef void(FUNC_TYPE)(int, char); FUNC_TYPE * pFunc = func;
//pFunc(10, 'a'); //2、定义出函数指针类型,通过类型定义函数指针变量
typedef void( * FUNC_TYPE2)(int, char); FUNC_TYPE2 pFunc2 = func;
//pFunc2(20, 'b'); //3、直接定义函数指针变量
void(*pFunc3)(int, char) = func;
pFunc3(30, 'c'); //函数指针 和 指针函数 区别?
//函数指针 指向了函数的指针
//指针函数 函数返回值是指针的函数
} //函数指针的数组
void func1()
{
printf("func1 调用了\n");
} void func2()
{
printf("func2 调用了\n");
} void func3()
{
printf("func3 调用了\n");
} void test02()
{
void(*pArray[3])(); pArray[0] = func1;
pArray[1] = func2;
pArray[2] = func3; for (int i = 0; i < 3;i++)
{
pArray[i]();
}
} int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 05 函数指针做函数参数.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //提供一个打印函数,可以打印任意类型的数据
void printText( void * data , void(*myPrint)(void *) )
{
myPrint(data); } void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
} void test01()
{
int a = 10;
printText(&a, myPrintInt);
} struct Person
{
char name[64];
int age;
}; void myPrintPerson(void * data)
{
struct Person * p = data;
printf("姓名: %s 年龄: %d\n", p->name, p->age);
} void test02()
{
struct Person p = { "Tom", 18 }; printText(&p, myPrintPerson); } int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
  • 06 回调函数案例.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //提供一个函数,实现可以打印任意类型的数组 void printAllArray(void * pArray , int eleSize, int len , void(*myPrint)(void*) )
{
char * p = pArray; for (int i = 0; i < len;i++)
{
//获取数组中每个元素的首地址
char * eleAddr = p + eleSize * i;
//printf("%d\n", *(int *)eleAddr);
//交还给用户做打印操作
myPrint(eleAddr);
} } void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
} void test01()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int len = sizeof(arr) / sizeof(int);
printAllArray(arr, sizeof(int), len, myPrintInt);
} struct Person
{
char name[64];
int age;
}; void myPrintperson(void * data)
{
struct Person * p = data;
printf("姓名:%s 年龄:%d \n", p->name, p->age);
} //查找数组中的元素是否存在
//参数1 数组首地址 参数2 每个元素的大小 参数3 数组元素个数 参数4 查找数据
int findArrayEle(void * pArray, int eleSize, int len, void * data , int(*myCompare)(void* ,void* ) )
{
char * p = pArray; for (int i = 0; i < len;i++)
{
//每个元素的首地址
char * eleAddr = p + eleSize * i; //if ( 数组中的变量的元素 == 用户传入的元素)
if ( myCompare(eleAddr,data) )
{
return 1;
}
} return 0; } int myComparePerson(void * data1,void * data2)
{
struct Person * p1 = data1;
struct Person * p2 = data2; //if ( strcmp( p1->name , p2->name) == 0 && p1->age == p2->age)
//{
// return 1;
//}
//return 0; return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age; } void test02()
{
struct Person personArray[] =
{
{ "aaa", 10 },
{ "bbb", 20 },
{ "ccc", 30 },
{ "ddd", 40 },
};
int len = sizeof(personArray) / sizeof(struct Person);
printAllArray(personArray, sizeof(struct Person), len, myPrintperson); //查找数组中指定的元素是否存在
struct Person p = { "ccc", 30 }; int ret = findArrayEle(personArray, sizeof(struct Person), len, &p, myComparePerson); if (ret)
{
printf("找到了元素\n");
}
else
{
printf("未找到\n");
}
} int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}

Day07

笔记 Day07笔记(动态库--> __declspec(dllexport)int mySub(int a, int b)

1	链表反转作业
1.1 利用3个辅助指针,实现链表反转
1.2 提供返回链表节点个数 函数
2 回调函数作业-实现对任意数据类型数组进行排序
2.1 利用选择排序实现大框架
2.2 对比功能交还给用户指定
2.3 利用回调函数技术来实现对比
3 预处理指令
3.1 头文件 #include
3.1.1 <> “”区别
3.1.2 <> 包含系统头
3.1.3 “” 包含自定义头
3.2 宏
3.2.1 宏常量
3.2.1.1 不重视作用域
3.2.1.2 没有数据类型
3.2.1.3 利用 #undef 卸载宏
3.2.2 宏函数
3.2.2.1 将频繁、短小函数写成宏函数
3.2.2.2 优点:以空间换时间
3.3 条件编译
3.3.1 #ifdef #else #endif 测试存在
3.3.2 #ifndef #else #endif 测试不存在
3.3.3 #if #else #endif 自定义条件编译
3.4 特殊宏
3.4.1 __FILE__ 宏所在文件路径
3.4.2 __LINE__ 宏所在行
3.4.3 __DATE__ 宏编译日期
3.4.4 __TIME__ 宏编译时间
4 静态库配置
4.1 右键项目->属性 ->常规->配置类型 ->静态库
4.2 生成项目 生成.lib文件
4.3 将.lib和 .h交给用户
4.4 测试
5 动态库配置
5.1 右键项目->属性 ->常规->配置类型 ->动态库 .dll
5.2 生成项目 生成 .lib .dll
5.3 静态库中生成的.lib和动态库生成的.lib是不同的,动态库中的.lib只会放变量的声明和 导出函数的声明,函数实现体放在.dll中
5.4 导出函数/外部函数 : __declspec(dllexport)int mySub(int a, int b);
5.5 测试
5.5.1 #pragma comment( lib,"./mydll.lib")
6 递归函数
6.1 本质:函数自身调用自身
6.2 注意事项:递归函数必须有结束条件,函数有出口
7 面向接口封装案例
7.1 甲乙两方设计接口
7.2 甲方实现代码
7.3 乙方实现代码
7.4 接口对接
8

Code

  • 01 回调函数作业-实现对任意类型数组排序.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> void selectSort( void * pAddr , int elesize , int len , int(*myCompare)(void * ,void *) )
{
char * temp = malloc(elesize); for (int i = 0; i < len;i++)
{
int minOrMax = i; //定义最小值 或者 最大值 下标
for (int j = i + 1; j < len;j++)
{
//定义出 j元素地址
char * pJ = (char*)pAddr + elesize * j;
char * pMinOrMax = (char*)pAddr + elesize * minOrMax;
//if ( pAddr[j] < pAddr[minOrMax]) /* 从大到小
if ( *num1 > *num2)
{
return 1;
}
return 0;
*/ if ( myCompare(pJ,pMinOrMax ))
{
minOrMax = j; //更新最小值或者最大值下标
}
} if ( i != minOrMax)
{
//交换i和minOrMax 下标元素
char * pI = (char*)pAddr + i*elesize; char * pMinOrMax = (char*)pAddr + minOrMax * elesize; memcpy(temp, pI, elesize);
memcpy(pI, pMinOrMax, elesize);
memcpy(pMinOrMax, temp, elesize); } } if (temp != NULL)
{
free(temp);
temp = NULL;
} } int myCompareInt(void * data1, void * data2)
{
int * num1 = data1;
int * num2 = data2; if ( *num1 > *num2)
{
return 1;
}
return 0;
} void test01()
{
int arr[] = { 10, 30, 20, 60, 50, 40 }; int len = sizeof(arr) / sizeof(int);
selectSort(arr, sizeof(int), len, myCompareInt); for (int i = 0; i < len;i++)
{
printf("%d\n", arr[i]);
} } struct Person
{
char name[64];
int age;
}; int myComparePerson(void * data1, void * data2)
{
struct Person * p1 = data1;
struct Person * p2 = data2; //if ( p1->age < p2->age)
//{
// return 1;
//}
//return 0;
//按照年龄 升序排序
return p1->age < p2->age; } void test02()
{
struct Person pArray[] =
{
{ "aaa", 10 },
{ "bbb", 40 },
{ "ccc", 20 },
{ "ddd", 30 },
};
int len = sizeof(pArray) / sizeof(struct Person);
selectSort(pArray, sizeof(struct Person), len, myComparePerson); for (int i = 0; i < len;i++)
{
printf("姓名:%s 年龄:%d\n", pArray[i].name, pArray[i].age);
} } int main(){
//test01();
test02(); system("pause");
return EXIT_SUCCESS;
}
  • 02 预处理指令.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //1 、头文件包含 "" <>区别
// ""包含自定义头文件
// <> 包含系统头文件 //2、 宏 常量
// 不重视作用域
// 可以利用 undef卸载宏
// 宏常量 没有数据类型的 //宏 函数
// 将短小、频繁使用的函数写成宏函数
// 加括号保证运算完整性
// 优点:以空间换时间 void test01()
{
#define MAX 1024
//#undef MAX
} //3、 条件编译
//#define DEBUG
#ifdef DEBUG
void func()
{
printf("Debug版本发布\n");
}
#else
//自定义条件编译
#if 0
void func()
{
printf("Release1版本发布\n");
}
#else
void func()
{
printf("Release2版本发布\n");
}
#endif #endif //特殊宏
void doWork(char * p)
{
if (p == NULL)
{
printf("文件: %s 第 %d 行 出错了\n", __FILE__, __LINE__);
printf("日期:%s\n", __DATE__);
printf("时间:%s\n", __TIME__);
return;
} }
void test02()
{
doWork(NULL); } int main(){ //printf("MAX = %d\n", MAX);
//func();
test02(); system("pause");
return EXIT_SUCCESS;
}

03 静态库测试

  • 头文件 -- mylib.h
#pragma  once 

//实现一个加法 ,返回两个数相加的结果
int myAdd(int a, int b);
  • 源文件 --03 静态库测试.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> int main(){ printf("%d\n", myAdd(10, 20)); system("pause");
return EXIT_SUCCESS;
}
  • 04 动态库测试.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> #pragma comment( lib,"./mydll.lib") int main(){ printf("10 - 20 = %d\n", mySub(10, 20)); system("pause");
return EXIT_SUCCESS;
}
  • 05 递归函数.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //利用递归实现字符串逆序遍历
void reversePrint(char * p)
{
if (*p =='\0')
{
return;
} reversePrint(p + 1); printf("%c\n", *p); } void test01()
{
char * str = "abcdefg";
reversePrint(str);
} int fibonacci(int pos)
{
if (pos == 1 || pos ==2)
{
return 1;
} return fibonacci(pos - 1) + fibonacci(pos - 2);
} void test02()
{
//斐波那契数列
// 1 1 2 3 5 8 13 21 34 55...
printf("第9为数字为:%d\n", fibonacci(9));
printf("第10为数字为:%d\n", fibonacci(10));
printf("第20为数字为:%d\n", fibonacci(20)); } int main(){ //test01();
test02();
system("pause");
return EXIT_SUCCESS;
}

06 面向接口封装案例

  • 头文件--
#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h> struct Player
{
char name[64]; //玩家姓名
int level; //玩家等级
int exp; //玩家经验
}; //初始化游戏 参数1 玩家指针 参数2 玩家姓名
void INIT_GAME_COMPANY1(void ** player, char * name); //副本战斗 返回战斗是否胜利 1、代表胜利 0、代表失败 参数1 玩家 参数2 游戏副本难度
int FIGHT_GAME_COMPANY1(void * player, int gameDiff); //查看玩家信息
void PRINT_GAME_COMPANY1(void * player); //离开游戏
void EXIT_GAME_COMPANY1(void * player); //判断游戏是否胜利
int isWin(int winRate, int diff);
  • 源文件 -- gameCompany1.c
#include "gameCompany1.h"

//初始化游戏  参数1  玩家指针   参数2  玩家姓名
void INIT_GAME_COMPANY1(void ** player, char * name)
{
struct Player * player1 = malloc(sizeof(struct Player)); if (player1 == NULL)
{
printf("初始化失败\n");
return;
} *player = player1; strcpy(player1->name, name);
player1->level = 0;
player1->exp = 0; } //副本战斗 返回战斗是否胜利 1、代表胜利 0、代表失败 参数1 玩家 参数2 游戏副本难度
int FIGHT_GAME_COMPANY1(void * p, int gameDiff)
{
struct Player * player = p; int addExp = 0; //增加经验值 switch (gameDiff)
{
case 1:
addExp = isWin(90, 1);
break;
case 2:
addExp = isWin(50, 2);
break;
case 3:
addExp = isWin(30, 3);
break;
default:
break;
} //累积经验到玩家身上
player->exp += addExp;
player->level = player->exp / 10; if (addExp == 0)
{
return 0;
}
else
{
return 1;
} } //判断游戏是否胜利
int isWin(int winRate, int diff)
{
int random = rand() % 100 + 1; // 1 ~ 100
if (random <= winRate)
{
return diff * 10;
}
else
{
return 0;
}
} //查看玩家信息
void PRINT_GAME_COMPANY1(void * p)
{
struct Player * player = p;
printf("玩家<%s> ------ 当前等级:<%d>级 ----- 当前经验: <%d> \n", player->name, player->level, player->exp);
} //离开游戏
void EXIT_GAME_COMPANY1(void * player)
{
if ( player==NULL)
{
return;
} free(player);
player = NULL;
}
  • 06 面向接口封装案例.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "gameCompany1.h"
#include <time.h> //初始化游戏 参数1 玩家指针 参数2 玩家姓名
typedef void(*INIT_GAME)(void ** player , char * name); //副本战斗 返回战斗是否胜利 1、代表胜利 0、代表失败 参数1 玩家 参数2 游戏副本难度
typedef int(*FIGHT_GAME)(void * player, int gameDiff); //查看玩家信息
typedef void(*PRINT_GAME)(void * player); //离开游戏
typedef void(*EXIT_GAME)(void * player); void playGame(INIT_GAME init, FIGHT_GAME fight, PRINT_GAME printGame, EXIT_GAME exitGame)
{
//初始化游戏
void * player = NULL;
printf("请输入姓名:\n");
char userName[64];
scanf("%s", userName); init(&player, userName); //副本难度 变量
int diff = -1; while (1)
{
getchar();
system("cls");
printf("请选择游戏难度:\n");
printf("1、简单\n");
printf("2、中等\n");
printf("3、困难\n"); scanf("%d", &diff); getchar(); //取走换行符 //战斗
int ret = fight(player, diff);
if (ret == 0)
{ printf("游戏失败\n");
break;
}
else
{
printf("挑战成功,玩家当前信息如下:\n"); printGame(player);
} } //关闭游戏
exitGame(player);
} int main(){ srand((unsigned int)time(NULL)); playGame(INIT_GAME_COMPANY1, FIGHT_GAME_COMPANY1, PRINT_GAME_COMPANY1, EXIT_GAME_COMPANY1); system("pause");
return EXIT_SUCCESS;
}

mydll

  • 头文件--mydll.h
#pragma  once

//实现两个数相减 函数
//内部函数
//int mySub(int a, int b); //外部函数 导出函数
//生成 .lib 和 .dll
// 静态库中生成的.lib和动态库生成的.lib是不同的,动态库中的.lib只会放变量的声明和 导出函数的声明,函数实现体放在.dll中
__declspec(dllexport)int mySub(int a, int b);
  • 源文件--mydll
#include "mydll.h"

int mySub(int a, int b)
{
return a - b;
}

mylib

  • 头文件--mylib.h
#pragma  once 

//实现一个加法 ,返回两个数相加的结果
int myAdd(int a, int b);
  • mylib.c
#include "mylib.h"

int myAdd(int a, int b)
{
return a + b;
}

02-C高级编程的更多相关文章

  1. multiple definition of `err_sys' 《UNIX环境高级编程》

    本文地址:http://www.cnblogs.com/yhLinux/p/4079930.html 问题描述: [点击此处直接看解决方案] 在练习<UNIX环境高级编程>APUE程序清单 ...

  2. Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程

    Shell高级编程视频教程-跟着老男孩一步步学习Shell高级编程实战视频教程 教程简介: 本教程共71节,主要介绍了shell的相关知识教程,如shell编程需要的基础知识储备.shell脚本概念介 ...

  3. (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  4. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. UNIX环境高级编程---标准I/O库

    前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压 ...

  6. 跟着老男孩一步步学习Shell高级编程实战

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://oldboy.blog.51cto.com/2561410/1264627 本sh ...

  7. UNIX环境高级编程——管道读写规则和pipe Capacity、PIPE_BUF

    一.当没有数据可读时O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止. O_NONBLOCK enable:read调用返回-1,errno值为EAGAI ...

  8. 解读经典《C#高级编程》第七版 Page68-79.对象和类型.Chapter3

    前言 新年好,本篇开始进入第三章,<对象和类型>,深刻理解C#的对象,对于使用好.Net类库非常重要. 01 类和结构 从使用角度看,结构和类的区别很小,比如,将结构定义转换为类,只需要将 ...

  9. 解读经典《C#高级编程》第七版 Page50-68.核心C#.Chapter2

    前言 本篇讲述Main方法,控制台,注释,预处理指令,编程规范等.这些概念比较琐碎,为避免长篇大论,主要以列举要点的方式来说明. 01 Main方法 Main方法并不是所有应用类型的入口方法,它只是控 ...

  10. 解读经典《C#高级编程》第七版 Page45-50.核心C#.Chapter2

    前言 本篇讲述枚举和名称空间. 01 枚举 首先需要明确枚举的概念:枚举是用户定义的整数类型.使用枚举的目标是,使用一组容易记忆的名称,来使得代码更容易编写和维护. 我们对比枚举的定义和类的定义,会发 ...

随机推荐

  1. c++字符串替换

    #include <string> #include <iostream> using namespace std; string m_replace(string strSr ...

  2. VSCode使用Settings Sync同步配置和插件

    转载参考地址:https://www.cnblogs.com/zzhaolei/p/12028241.html 1.需求 自己平常工作,一般在公司用公司的电脑,在家里就是自己的,但是vscode如果配 ...

  3. input type='file'限制上传文件类型

    前端与后台数据进行对接时,就避免不了要使用ajax进行http请求,常用的请求就两个post与get:然而常见的post请求的需求是文件上传,可能我一说到文件上传大家都觉得so  easy啊,没什么嘛 ...

  4. ABP源码分析 - 约定注册(3)

    入口 //ConfigureServices foreach (var module in Modules) { if (module.Instance is AbpModule abpModule) ...

  5. Python入门-程序测试

    1.功能测试 常规测试 #常规测试代码,一个模块写功能,一个模块调用功能 #=============模块1:gongneng_ceshi def func(v1, v2): return v1* v ...

  6. SpringMVC获取请求参数-集合类型

    1.创建User实体类 ```java public class User { private String username; private int age; public String getU ...

  7. Python数据展示 - 生成表格图片

    前言 前一篇文章介绍了推送信息到企业微信群里,其中一个项目推送的信息是使用Python自动生成的表格,本文来讲讲如何用Python生成表格图片. 选一个合适库 Python最大的优点就是第三方库丰富, ...

  8. 『现学现忘』Git基础 — 3、Git介绍

    目录 1.Git的历史 2.Git的特点 3.Git在项目协作开发中所解决的问题 1.Git的历史 Git是目前世界上最先进的分布式版本控制系统,开源.免费. Git 是 Linus (林纳斯)为了帮 ...

  9. Codeforces Round #700 (Div. 2) --- B(思维)

    传送门 有必要提醒自己一下, 先把题读利索了(手动捂脸) 题目 B. The Great Hero   The great hero guards the country where Homer li ...

  10. 删库到跑路?还得看这篇Redis数据库持久化与企业容灾备份恢复实战指南

    本章目录 0x00 数据持久化 1.RDB 方式 2.AOF 方式 如何抉择 RDB OR AOF? 0x01 备份容灾 一.备份 1.手动备份redis数据库 2.迁移Redis指定db-数据库 3 ...