[原创] 这两天遇到的两个问题,在这里分享一下^_^

anqi90   2015-1-1 00:31 楼主
2014年的最后一天了,发个帖纪念一下~可能都是常识,高手请自动忽略~~~



其实这两个问题是前几天遇到的,在这里总结一下吧


-----------------------------------------
第一个是一个项目里面要控制芯片引脚的反转来模拟spi通信。


但是不加延时的话处理的就太快了,估计要有十几MHz的频率,芯片引脚翻转肯定达不到这样的速度。


而且又因为是在linux下的,常用的几个延时函数,比如usleep、nanosleep,甚至是select,虽然介绍中说是可以精确到微秒,但是实际上受限于linux系统,最短的延时时间一般是10ms,有些嵌入式系统上是1ms,但是这样仍然太慢了


所以就只能像以前在单片机上直接写死循环那样做延时了。


一开始我是想这样写的:


void delay()   
{   
    int i, time = 100000;   
   
    for(i = 0; i < time; i++);   
}   



但是编译之后会发现,延时并没有生效。因为Makefile里打开了-O2的优化选项,编译器认为这个for循环没有什么意义,就直接优化掉了。
那么如何不让编译器把它优化掉呢


当然,把-O2选项关掉就行了,但是这毕竟不是解决问题的办法,Makefile是影响整个工程的编译的,因为这一行延时的需求就影响到整个工程毕竟不太合适


经过查找之后发现,volatile这个前缀可以告诉编译器这个变量是不确定的,有可能被意外的改变,每次使用都要从内存中读取,处理之后必须写入到内存中去,而不能只在缓存中操作或者是直接优化掉。
所以把上面声明变量i的时候加上volatile这个前缀,问题也就解决了。
关于这个前缀的详细介绍大家可以搜一下,百度百科里介绍的就很清楚了,这里就不废话了。


------------------------------------------------------------
第二个问题是关于printf


起因是我想在程序里写入文件的时候加一个显示进度的功能,其实就是在写入过程中每写入一定字节就printf刷新一遍内容
只是在最后不是常用的\n, 而是在开始printf的时候\r,回到当前行的行首,覆盖之前的内容
代码类似于下面:


    while(size > 0){   
        delay();   
        size -= 128;   
        if( (len - size) % 1024 == 0 ){   
            printf("\r len = %d, %d copied ", len, len - size);   
        }   
    }   



但是实际运行中会发现,光标很不稳定,按理说应该是始终在行尾,但是实际上总是在乱跳,像下面这样:
clip0044.gif


这就很奇怪了,为此我去掉了每次的\r,看看到底是怎么printf的,结果是这样:
clip0046.gif


好像并不是每次printf都会输出,而是过一段时间输出一堆东西出来。虽说最终的结果是一样的,但是过程中显然有什么和我们预想的是不一样的。


根据这个效果,感觉就是printf会有一个缓冲区,满了之后才会输出到屏幕上一样。


于是就放狗搜了一下 “printf 缓冲区”,果然就是这样的。
printf其实并不会直接向屏幕上输出信息,而是输出到一个输出缓冲区中,而要想让缓冲区向屏幕输出有四种情况:
1)缓冲区满了,也就是上面这个情况
2)printf输出了\n,这就是我们一般使用的情况
3)使用printf的程序进程结束了,比如我们有时候忘了加\n,但是printf之后程序就继续运行,很快就退出了,所以看起来也没什么问题
4)强制输出,使用函数:fflush(stdout);


我们一班的使用过程中,要么是最后会输出\n,要么是程序很快就退出了,所以不会发现什么异常。但是如果在一个循环里长时间的输出的话,就会发现不对了,就像上面的情况一样。


在这种情况下,我们有两种办法可以解决问题,第一种,把缓冲区设为0,setbuf(stdout,NULL)。当然这个是很不合理的,所以我是用了上面的4),代码如下:
while(size > 0){
        delay();
        size -= 128;
        if( (len - size) % 1024 == 0 ){
            printf(" len = %d, %d copied ", len, len - size);
            fflush(stdout); //强制输出
        }
    }



这样再运行起来就是预期的效果了:
clip0047.gif


OK,以上就是这两个问题的相关总结了。
相信对于大部分程序猿来说这些都是很基础的东西,不过估计还是会有一些像我这样的小白吧...一般可能不会注意到这些东西,但是真要用到的时候还是很关键的。


不知不觉已经跨年了...那就祝大家在2015年里万事顺心,工作顺利,新年快乐!

回复评论 (4)

想问一下动图是怎么做的
training
点赞  2015-1-1 11:42
最后那个printf这个问题我也碰到过,一直没找到解决得办法,谢谢分享
点赞  2015-1-1 11:51
引用: 白丁 发表于 2015-1-1 11:42
想问一下动图是怎么做的
屏幕录像+gif制作软件,具体名字忘了…回头告诉你吧
点赞  2015-1-1 12:08
引用: IC爬虫 发表于 2015-1-1 11:51
最后那个printf这个问题我也碰到过,一直没找到解决得办法,谢谢分享
能帮上忙就好^_^
点赞  2015-1-1 12:26
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复