推广 热搜: 百度  搜索引擎  可以  企业  使用  选择  上海  page  技术  货运 

C语言的一些“骚操作”及其深层理解_c语言 xb,怎么入门Golang

   日期:2024-12-27     作者:g19ip    caijiyuan  
核心提示:int fputc(int ch, FILE *f) { while((USART1-SR0x40)==0); { USART1-DR = (u8) ch; } return ch; } fputc中将

int fputc(int ch, FILE *f)
{
while((USART1->SR&0x40)==0);
{
USART1->DR = (u8) ch;
}

return ch;
}

fputc中将ch通过USART1发出。这样,我们在调用printf的时候,相应的信息就会从USART1打印出来。

“上面你说的这些,我都知道,有什么新鲜的!”确实,通过串口打印信息是我们司空见惯的。那么,下面的fputc你见过吗

int fputc(int ch, FILE *f)
{
LCD_DispChar(x,y,ch);
x++;
if(x>=X_MAX)
{
x=0;y++;

if(y>=Y_MAX)*
{
y=0;
}
}
return ch;
}

这个fputc将字符显示在了液晶上(同时维护了字符的显示位置信息,这样当我们调用printf的时候,信息会直接显示在液晶上。

说白了,fputc 就是对数据进行了定向输出。这样我们可以把 printf 变得更灵活,来应对更多样的应用需求。在振南经历的项目中,曾经有过这样的情况:单片机有多个串口,串口1用于打印调试信息,串口2与ESP8266 WIFI模块通信,串口3与SIM800 GPRS模块通信。3个串口都需要格式化输出,但printf只有一个,这该怎么办?我们解决方法是,修改fputc使得printf可以由3个串口分时复用,具体实现如下

unsigned char us=0;

int fputc(int ch,FILE *f)
{
switch(us)
{
case 0:
while((USART1->SR&0x40)==0);
USART1->DR=(u8)ch;
break;

case 1:
while((USART2->SR&0x40)==0);
USART2->DR=(u8)ch;
break;

case 2:
while((USART3->SR&0x40)==0);
USART3->DR=(u8)ch;
break;
}

return ch;
}

在调用的时候,根据需要将us赋以不同的值,printf就归谁所用了。

#define U_TO_DEBUG us=0;
#define U_TO_ESP8266 us=1;
#define U_TO_SIM800 us=2;

U_TO_DEBUG
printf(“hello world!”);

U_TO_ESP8266
printf(“AT ”);

U_TO_SIM800
printf(“AT ”);

七、关于浮点数的传输

unsigned short a=0x1234;

UART_Send_Byte(((unsigned char *)&a)[0]);

UART_Send_Byte(((unsigned char *)&a)[1]);

也就是将构成整型的若干字节顺序发送即可。当然,接收方一定要知道如何还原数据,也就是说,它要知道自己接收到的若干字节拼在一起是什么类型,这是由具体通信协议来保障的。

unsigned char buf[2];
usnigned short a;

UART_Receive_Byte(buf+0);
UART_Receive_Byte(buf+1);

a=(*(usnigned short *)buf);

OK,关于整型比较容易理解。但换成float,很多人就有些迷糊了。因为float的数值表达方式有些复杂。有些人使用下面的方法来进行浮点的发送

float a=3.14;

char str[10]={0};

ftoa(str,a); //浮点转为字符串 *即3.14*转为"3.14"

UART_Send_Str(str); //**通过串口将字符串发出

很显然,这种方法非常的“业余”。

还有人问我:“浮点小数字前后的数字可以发送,但是小数点怎么发?”这赤裸裸的体现了他对浮点类型的误解。

不要被float数值的表象迷惑,它实质上只不过是4个字节而已,如下图所示

所以,正确的发送浮点数的方法是这样的

float a=3.14;

UART_Send_Byte(((unsigned char *)&a)[0]);

UART_Send_Byte(((unsigned char *)&a)[1]);

UART_Send_Byte(((unsigned char *)&a)[2]);

UART_Send_Byte(((unsigned char *)&a)[3]);

接收者将数据还原为浮点

unsigned char buf[4];
float a;

UART_Receive_Byte(buf+0);

UART_Receive_Byte(buf+1);

UART_Receive_Byte(buf+2);

UART_Receive_Byte(buf+3);

a=*((float *)buf);

其实我们应该发现数据类型的实质:不论是什么数据类型,它的基本组成无非就是内存中存储的若干个字节。只是我们人为的赋予了这些字节特定的编码方式或数值表达。看穿了这些,我们就认识到了数据的本质了,我们甚至可以直接操作数据。

八、关于数据的直接操作

直接操作数据?我们来举个例子:取一个整型数的相反数。一般的实现方法是这样的

int a=10;
int b=-a; //-1*a;

这样的操作可能会涉及到一次乘法运算,花费更多的时间。当我们了解了整型数的实质,就可以这样来作

int a=10;

int b=(~a)+1;

这也许还不足以说明问题,那我们再来看一个例子:取一个浮点数的相反数。似乎只能这样来作

float a=3.14;

float b=a*-1.0;

其实,我们可以这样来作

float a=3.14;
float b;

((unsigned char *)&a)[3]^=0x80;
b=a;

没错,我们可以直接修改浮点在内存中的高字节的符号位。这比乘以-1.0的方法要高效的多。

当然,这些操作都需要你对 C 语言中的指针有炉火纯青的掌握。

九、浮点的四舍五入与比较

我们先说第一个问题:如何实现浮点的四舍五入?很多人遇到过这个问题,其实很简单,只需要把浮点+0.5然后取整即可。OK,第二个问题:浮点的比较。这个问题还有必要好好说一下。首先我们要知道,C语言中的判等,即==,是一中强匹配的行为。也就是,比较双方必须每一个位都完全一样,才认定它们相等。这对于整型来说,是可以的。但是float类型则不适用,因为两个看似相等的浮点数,其实它们的内存表达不能保证每一个位都完全一样。这个时候,我们作一个约定:两个浮点只要它们之差m足够小,则认为它们相等,m一般取10e-6。也就是说,只要两个浮点小数点后6位相同,则认为它们相等。也正是因为这个约定,很多C编译器把float的精度设定为小数点后7位,比如ARMCC(MDK的编译器)。

float a,b;

if(a==b)
… //**错误

if(fabs(a-b) <= 0.000001)
…//**正确

十、出神入化的for循环

for循环我们再熟悉不过了,通常我们使用它都是中规中矩的,如下例

int i;

for(i=0;i<100;i++)
{…}

但是,如果我们对for循环的本质有更深刻的理解的话,就可以把它用得出神入化。

for后面的括号中的东西我称之为“循环控制体”,分为三个部分,如下图所示:A、B、C三个部分,其实随意性很大,可以是任意一个表达式。所以,我们可以这样写一个死循环

for(1;1;1) //1**本身就是一个表达式:常量表达式
{

}

当然,我们经常会把它简化成

for(;😉
{

}

既然循环控制体中的A只是在循环开始前作一个初始化的操作,那我这样写应该也没毛病

int i=0;

for(printf(“Number: ”);i<10;i++)
{
printf(" %d ",i);
}

B是循环执行的条件,而C是循环执行后的操作,那我们就可以把一个标准的if语句写成for的形式,而实现同样的功能

if(strstr(“hello world!”,“abc”))
{
printf(“Find Sub-string”);
}
char *p;

for(p=strstr(“hello world!”,“abc”);p;p=NULL)
{
printf(“Find Sub-string”);
}

以上的例子可能有些鸡肋,“一个if能搞定的事情,我为什么要用for?”,没错。我们这里主要是为了解释for循环的灵活用法。深入理解了它的本质,有助于我们在实际开发中让工作事半功倍,以及看懂别人的代码。

以下我再列举几个for循环灵活应用的例子,供大家回味。例1

char *p;

for(p=“abcdefghijklmnopqrstuvwxyz”; printf§; p++)
printf(“ ”);

提示:printf我们太熟悉了,但有几个人知道printf是有返回值的?输出应该是怎样的

例2

char *p;
unsigned char n;

for(p=“ablmnl45ln”,n=0;((*p==‘l’)?(n++):0),*p;p++);

提示:还记得C语言中的三目运算和逗号表达式吗?n应该等于几

例3

unsigned char *index=“C[XMZA[C[NK[RDEX@”;

char *alphabet="EHUIRZWXABYPOMQCTGSJDFKLNV ";

int i=0;

for(😭(‘@’!=index[i])?1:(printf(“!!Onz ”),0));i++)
{
printf(“%c”,alphabet[index[i]-‘A’]);
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导,让我们一起学习成长

cW1-1712959914313)]
[外链图片转存中…(img-ZZdXfR3W-1712959914313)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go
[外链图片转存中…(img-sdigNb79-1712959914314)]

本文地址:http://ww.kub2b.com/tnews/2120.html     企库往 http://ww.kub2b.com/ ,  查看更多

特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。

 
 
更多>同类生活信息

文章列表
相关文章
最新动态
推荐图文
生活信息
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号