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. the compatibility problem of ie

    ie8hack ie8下的兼容问题处理:背景透明,css3圆角,css3和jquery支持部分css3选择器(例如:nth-child),支持html5的语义化标签,媒体查询@media等. 在htm ...

  2. java中接口interface可以持有多个类的共享常量

    3.接口持有多个类的共享常量  接口另一主要功能,马克-to-win: 可以使用接口来引入多个类的共享常量.所有的这些变量名都将作为常量看待.所有定义在接口中的常量都默认为public.static和 ...

  3. 在ios里面返回上一级报错问题

    $("#backPrev").attr("href","javascript:void(0);").click(function(){    ...

  4. js知识梳理6:关于函数的要点梳理(2)(作用域链和闭包)

    写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者.有发现什么问题的,欢迎留言指出 ...

  5. 简简单单用一下 Hbase

    一.Hbase 介绍 https://hbase.apache.org/book.html#_preface https://blogs.apache.org/hbase/ https://resea ...

  6. ansble通过脚本定时清理k8s日志

    环境:环境k8s1.17,ansble通过脚本定时清理k8s日志 [root@tidb-21 delete-k8s-logs]# lsansib-delete.sh delete-logs.sh [r ...

  7. GIL全局解释器锁、协程运用、IO模型

    GIL全局解释器锁 一.什么是GIL 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念.就好比C是一套语言(语法)标准,但是可以用不 ...

  8. 定时执行任务-springboot

    定时执行任务-springboot 先看两个接口 这两个接口springboot已经帮我们封装好了,我们不需要去手动使用 TaskScheduler //任务调度者 TaskExecutor //任务 ...

  9. 编译实战 | 手摸手教你在Windows环境下运行Redis6.x

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是没事就愿意瞎捣鼓的Hydra. 不知道有没有小伙伴像我一样,平常开发中用的是windows操作系统,有时候想装点什么软件,一看 ...

  10. go interface{}使用

    先上代码 func In(haystack []interface{}, needle interface{}) (bool, error) { sVal := reflect.ValueOf(hay ...