进阶 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 思否icon-default.png?t=N7T8https://segmentfault.com/a/1190000038746268指针函数和函数指针_指针函数 函数指针-CSDN博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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){//最后一个链表节点的指针为NULL
	 printf("%d\r\n",H->data);
	 H = H->next;
	}
	printf("\n");
	return 0;
}

实验现象


对结构体指针指向的字符串进行修改的放法

指针与字符串-CSDN博客icon-default.png?t=N7T8https://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   50

struct 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;		
}
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Zotero是一款用于学术研究的引用管理工具,它帮助研究人员轻松地收集、组织和引用各种学术资源。而Zotero Pro Max是Zotero的高级会员服务。下面我将介绍一下Zotero Pro Max的一些优势。 首先,Zotero Pro Max为用户提供了更大的存储空间。普通的Zotero帐户只能免费获取一定量的存储空间,而Zotero Pro Max会员则可以享受更大的存储限额,这对于大规模的研究项目来说非常有用。 其次,Zotero Pro Max会员可以享受更高级的协作功能。普通的Zotero帐户允许用户与少数合作者共享文献库,而Zotero Pro Max会员可以与更多的合作者共享,并且可以控制谁可以做出更改和编辑提交。这对于多人合作的研究项目来说非常便利。 第三,Zotero Pro Max会员还能够获取更快的同步速度和更优质的技术支持。普通Zotero用户在使用同步功能时可能会遇到同步延迟的问题,但是Zotero Pro Max会员可以享受更快速的同步速度,能更有效地提高工作效率。此外,Pro Max会员还能够快速获得技术支持,如果遇到使用上的问题,可以更及时地获得帮助和解决方案。 综上所述,Zotero Pro Max通过提供更大的存储空间、更高级的协作功能、快速的同步速度和优质的技术支持,为学术研究人员提供了更加强大和便利的工具。有了这些优势,Zotero Pro Max会员可以更好地管理和利用学术资源,提高研究效率,使研究工作更加顺利进行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值