进阶 pro max
最近搞了许多有趣的东西,比如自制rtos,速成数模电,学了一点点的AD,看着视频弄了HAL库,以及定时器和串口中断配合实现接收任意长度(不超过缓冲值)数据,还有配置hal库的freertos+fafts ,今天到货了前两天买的硬件十万个为什么,又下单了H750VB板子,这两天过敏又犯了, 无语,一字难受。
总结一下大二暑假学的东西
定时器判断长短按
定时器和串口判断一帧数据
bootloader
三极管,mos管
防反接电路
dcdc电源原理,ldo,电容滤波原理
元器件的认识,磁珠电感电容电阻各种二极管
esp8266获取天气
C语言结构体,常用函数,关键字指针等知识点总结。
手写i2c读rom,学习spi读写flash和sd卡
做完辅助穿衣机器人的程序
嘉立创画完温湿度检测的板子
keil调试方法
hal库的使用
can
步进电机
一点点的AD
在此更新一些C语言杂乱知识点和疑难杂症........
函数指针和指针函数
函数指针
本质是指针,指向一个函数 。
qsort
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));// base -> 需要排序的数组的起始地址
// num -> 数组内元素的个数(数组的大小)
// size -> 一个元素的大小(单位是字节)
// int (*compar)(const void*,const void*) -> compar(一个函数指针),类型是 int (*)(const void*,const void*)
c - qsort函数使用方法总结(详细全面+代码) - 个人文章 - SegmentFault 思否https://segmentfault.com/a/1190000038746268指针函数和函数指针_指针函数 函数指针-CSDN博客
https://blog.csdn.net/u010280075/article/details/88914424?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172407382416800182122635%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172407382416800182122635&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_positive~default-1-88914424-null-null.nonecase&utm_term=%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88&spm=1018.2226.3001.4450C语言---qsort 函数详解与实现(快速排序任何类型)-CSDN博客
https://blog.csdn.net/2302_78684687/article/details/137467421
大家也都能了解,这个函数的重点就在于 compar 这一函数指针。
问题是怎么使用qsort来对一个数组进行排序 ,上面是对qsort四个参数的介绍,其中数组起始地址就是数组名(这点在上一篇文章中指针和数组的关系中讲过,江科大的指针视频中也提到过)。
后面是数组的元素个数和每个元素的大小
char shuzu[]={1,2,3,4,5,6,77,8}
//数组元素个数
sizeof(shuzu) / sizeof(char) ;
//每个元素的字节大小
sizeof(shuzu[0]);
sizeof(char) 和 sizeof(shuzu[0]) 是一样的。
最最重点的就是第四个参数是一个函数,函数名可以自由定义,但是参数必须和qsort给的如出一辙,即为
char sum_int(const void* p1, const void* p2)
{
// return (int)(*(int*)p1 - *(int*)p2); //->升序return (int)(*(char *)p2 - *(char*)p1); //->降序
}
qsort函数根据函数指针所指向的函数的返回值和 0的关系做出判断从而进行排序,所以至于是从大到小还是从小到达,可以自己决定。
在 sum_int() 函数中需要将参数p1和p2转义为数组的定义类型。p1p2是指针,数值等于地址 。所以(char*)的转义符前面还有一个*,代表p1p2所指向地址的数值 。
最近看的回调函数也和函数指针相关。
指针函数
本质是函数,返回一个指针 。返回指针的函数叫指针函数
#include <stdio.h>
#include <stdlib.h>
char *get_num ( char *n,const char *m);
int main()
{char a1[100]= "hello";char a2[]= "world";printf("%s", get_num(a1,a2));
}char *get_num ( char *n,const char *m)
{char *p=n;while(*n != '\0') { n++; }while(*m != '\0') { *n++ = *m++ ;} *n = '\0';return p;
}
结果为helloworld
定义 函数 get_num()实现字符串的拼接并返回拼接好的字符串的起始地址。
原来pritf函数打印字符串的时候输入字符串的地址就行,比如
char a2[]= "world";printf("%s", a2);
a2 是数组名 ,也就是数组地址 。
在 函数 get_num() 内部用循环将指针 n 指向到数组 n 的末尾 ,之后从数组 a 的末尾开始用循环取m的值一位一位的的赋值给n指导数组m结束,实现字符串的拼接。
浮点数在内存中的存储
C语言---浮点数在内存中的存储_c浮点数存储-CSDN博客https://blog.csdn.net/2302_80826557/article/details/137436685?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172407546616800172520370%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172407546616800172520370&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-1-137436685-null-null.nonecase&utm_term=%E6%B5%AE%E7%82%B9%E6%95%B0%E5%9C%A8%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84%E5%AD%98%E5%82%A8&spm=1018.2226.3001.4450在浮点数进行判断的时候,在浮点数后边加一个f
如下图所示
int main()
{float f1=2.2;if(f1 == 2.2) printf("2.2 = 2.2\r\n");else printf("no xiangdeng\r\n");if(f1 == 2.2f) printf("2.2f = 2.2f\r\n");else printf("no xiangdeng\r\n"); }
可见,对f1进行判断的时候加一个f才能输出正确的结果。
安规电容
安规电容在电路中用于过滤干扰信号,X电容用于抑制差模干扰,Y电容用于抑制共模干扰 。
数组与指针
对于以下代码,猜猜在存储区一共存储了几个 hello
#include <stdio.h>
#include <stdlib.h>int main()
{char *s1="hello";char *s2="hello";char a1[]="hello";char a2[]="hello";printf("%p %p %p %p",s1,s2,a1,a2);}
其中 *s1 指向的 hello 是一个字符串常量,在静态存储区(静态存储区直到执行结束内存才被回收),s1和s2指向的内容一样所以地址一样。
a1 和 a2是局部变量,放在栈里可以随时修改,所以a1和a2是两个不一样的hello,总的来说看来是变量存储的位置不同从而带来的影响。
a1代表的是数组名也就是数组的地址是地址常量存储在静态存储区,地址常量不能被修改。也就是说 a1++;是错误的,地址上面的数值可以修改,比如 *a1[0] = 'H';是对的。
s1是一个指针,指针是可以变的,指向的是hello的地址,也可以变成其他字符串的地址。 所以 s1++;是合法的,再次打印 *s1 得到的结果为 "ello" ,字符串常量hello是不能被修改的,比如
*s1[0] = 'H' 会报错。
其中指针和数组占用的内存也不一样。
数组占用的内存为字符个数乘以变量类型字节长度。
指针大小为系统位数除以8 。
二维数组
数据结构 | 二维数组的元素地址。【按行或按列存储】时,行列对换坐标,元素的起始地址保持不变-CSDN博客https://blog.csdn.net/weixin_52700125/article/details/134883240?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172407755216800184153816%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172407755216800184153816&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-134883240-null-null.nonecase&utm_term=%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E7%9A%84%E5%9C%B0%E5%9D%80&spm=1018.2226.3001.4450
元素的地址等于起始地址加上元素在数组中排在元素前面的所有元素所占的内存大小。对于二维数组分为行和列。比如数组定义为a[2][4],那么a[1][1]前面的元素包括a[0][1],a[0][2],a[0][3],a[0][4] 。
二维数组如何进行传参
#include <stdio.h>
#include <stdlib.h>
int get_num(int n,int m,int (*p)[m]);
int main()
{
int a[3][3]={{1,4,7},{3,7,1},{9,4,2}};
int sum ;sum = get_num(3,3,a);
printf("%d\n",sum);
return 0;}数组指针 int (*p)[m]
(*p)代表数组名,[m],代表他每一行有m个列。int get_num (int n,int m,int (*p)[m])
{int sum = 0;int i,j;for(i=0;i<n;i++){for(int j=0;j<m;j++){sum += p[i][j];printf("%d\r\n",*(*(p+i)+j)); //先找行再找列,再取值} }return sum;
}
运行结果
指针常量和常量指针
常量指针(本质是指针) 指针指向的地址的内容不能被改变 (可以更改指针指向的地址来改变指针对应的数值) 指向常量的指针 。
指针常量(本质是常量) 指针指向的内容的地址不能被改变 (可以更改指针指向的内容来改变指针对应的数值) 指针本身是个常量 。
常量指针和指针常量的区别_指针常量和常量指针的区别-CSDN博客https://blog.csdn.net/weixin_46280821/article/details/126652869?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172407987416800213061084%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172407987416800213061084&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_positive~default-1-126652869-null-null.nonecase&utm_term=%E5%B8%B8%E9%87%8F%E6%8C%87%E9%92%88%E5%92%8C%E6%8C%87%E9%92%88%E5%B8%B8%E9%87%8F&spm=1018.2226.3001.4450
数组指针和指针数组
数组指针与指针数组的区别_数组指针和指针数组的区别-CSDN博客https://blog.csdn.net/super_demo/article/details/19679053?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172408134016800222843329%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172408134016800222843329&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-19679053-null-null.nonecase&utm_term=%E6%95%B0%E5%80%BC%E6%8C%87%E9%92%88%E5%92%8C%E6%8C%87%E9%92%88%E6%95%B0%E7%BB%84&spm=1018.2226.3001.4450数组指针 指向一个数组的指针
数组指针经常和二维数组连用。
链表
手撕链表
数据结构之手撕链表(讲解➕源代码)-CSDN博客https://blog.csdn.net/2302_76941579/article/details/133846023?ops_request_misc=%257B%2522request%255Fid%2522%253A%25225ECE9487-2A28-4479-852E-93DC3E8A40C1%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=5ECE9487-2A28-4479-852E-93DC3E8A40C1&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-133846023-null-null.142^v100^pc_search_result_base9&utm_term=%E6%89%8B%E6%92%95%E9%93%BE%E8%A1%A8&spm=1018.2226.3001.4187
#include <stdio.h>typedef int datatype;//定义结构体
/*您提供的代码片段是一个C语言中的结构体定义,使用了`typedef`关键字来
定义一个结构体类型`listnode`和一个指向该结构体的指针类型`linklist`。
这种定义方式在数据结构中常用,特别是在处理链表时,可以简化代码中的类型
声明。结构体`listnode`包含两个成员:一个整型数据`data`和一个指向下一个
`listnode`结构体的指针`next`。通过`typedef`关键字,`listnode`成为
了结构体类型的别名,而`linklist`成为了指向该结构体的指针类型的别名。
这样,在后续的代码中,可以直接使用`listnode`和`linklist`来声明变量,
而不需要每次都写出完整的结构体类型和指针类型。这种定义方式在C语言中是合法的,并且是一种常见的编程技巧,
用于提高代码的可读性和可维护性.*/typedef struct node{
int data;
struct node *next;
}listnode,*linklist;linklist list_create();//列表创建函数
int head_insert(linklist H,datatype data);//列表头部插入函数
int list_show(linklist H);//列表遍历打印函数int main(){linklist H;if ((H = list_create()) ==NULL ){ return 0;} head_insert(H, 50);head_insert(H, 50);head_insert(H, 100);head_insert(H, 20);head_insert(H, 50);list_show(H);return 0;
}linklist list_create()
{linklist H;if((H = (linklist)malloc(sizeof(listnode))) == NULL){return H;} H->data = 0;H->next = NULL;return H;
}
//没有释放掉申请的内存,需要注意
int head_insert(linklist H,datatype data)
{ linklist p;if(H == NULL) {return -1;}if((p = (linklist)malloc(sizeof(listnode))) == NULL){ printf("malloc failed");return -1;}p->data = data;p->next = H ->next;H->next = p;return 0;
}int list_show(linklist H)
{if(H == NULL || H ->next == NULL){return -1;}H = H->next;while(H != NULL){//最后一个链表节点的指针为NULLprintf("%d\r\n",H->data);H = H->next;}printf("\n");return 0;
}
实验现象
对结构体指针指向的字符串进行修改的放法
指针与字符串-CSDN博客https://blog.csdn.net/qq_43680827/article/details/122929776?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172416196116800182767696%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=172416196116800182767696&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-122929776-null-null.142%5Ev100%5Epc_search_result_base9&utm_term=%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%92%8C%E6%8C%87%E9%92%88&spm=1018.2226.3001.4187直接对指针定义结构体,指针指向的是存放在静态存储区的字符串的地址,
对结构体指针指向的字符串直接进行修改失败的原因也是静态存储区的常量不能被修改
比如 s1.name[0] = 'H' ; 是错误的 。
自己申请一段堆上的空间
并不是实现对精彩存储区常量的修改,而是实现了申请内存的方法修改结构体指针指向的存在堆区的字符串的内容。所以结构体指针指向的字符串还是不能直接定义,需要用其他方法,原因还是直接定义就是定义静态常量不能修改。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 50struct student{int num;char *name;
};int main(){
struct student s1 ={1,"guangtouqiang"};
struct student s2;
s2.num = 2;//s1.name[0] = 'G';if((s2.name = malloc(N*sizeof(char))) == NULL) { return 0;}//s2.name = "xiongda";
strcpy(s2.name,"xiongda");
s2.name[0] = 'X';printf("%d %s\r\n",s1.num,s1.name);
printf("%d %s\r\n",s2.num,s2.name);free(s2.name);
s2.name == NULL;
}