广告位B招租:1
 2022-02-27T18:32:06.395938    |      C/C++    |     AilsonJack    |     暂无评论    |     22 views
不同体系结构的CPU对于数据在内存中存放的排列顺序规定不同。数据在内存中的存储是以字节(byte)为单位的,因此对于半字(Half-Word)和字(Word)在内存中就有两种存储顺序,分别称为:大端模式(Big Endian)和小端模式(Little Endian)。大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中;小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。大端模式的次序就像是我们平时书写的次序,先写大数,后写小数。大端存储次序还广泛运用在TCP/IP协议上,因此又称为网络字节次序。
计算机基础CC++CPU 阅读全文»
 2021-10-29T19:04:46.092336    |      C/C++    |     AilsonJack    |     暂无评论    |     50 views
在程序的开发和调试过程中,可以使用一个宏定义来代替printf输出调试信息,等程序开发完成之后,如果不需要调试信息,直接将宏定义为空就行,这样便于随时打开和关闭调试信息。这样的调试程序的宏一般是可变参数宏,因为打印函数printf是可变参数的,因此定义的宏也要求支持可变参数。可变参数宏可以接受可变数目的参数,就像可变参数的函数一样。可变参数宏也使用三个点(...)来表示宏的可变参数性。__VA_ARGS__ 宏是用来表示可变参数宏的可变参数的内容。简单的说就是将可变参数宏中的 ... 的内容原样传递给右边 __VA_ARGS__ 所在的位置。示例代码如下:通过屏蔽或打开程序中的 __DEBUG__ 宏,可以让调试信息输出或者不输出。
CC++编程语言计算机基础 阅读全文»
 2021-09-26T21:00:10.059909    |      C/C++    |     AilsonJack    |     暂无评论    |     101 views
在编写C程序时,如果想要打印某个字符串,而字符串的内容比较多,这就涉及到对这个长字符串进行书写换行,这里的换行并不会对最终的显示结果进行换行,只是为了阅读代码能够更加的清晰,不至于字符串的内容过长影响代码的阅读体验。1.长字符串示例上述代码需要打印的字符串内容比较长,在代码阅读软件中,可能需要拖动水平方向上的滚动条,才能看清楚字符串的完整内容,这极大的影响了代码的阅读效率。下图是上述代码的运行结果:2.书写长字符串的换行方法方法一:利用双引号对长字符串进行换行在对长字符串进行书写换行时,可以使用双引号将长字符串拆分成多个子字符串,编译器在编译处理时会自动的拼接这些子字符串,不会影响最终想要的显示效果,示例代码如下:上述示例的运行结果如下:运行结果和长字符串未拆分时的效果一样。方法二:利用反斜杠对长字符串进行换行可以使用反斜杠对长字符串进行拆分,反斜杠后的换行符会被C忽略,所以可以拆分字符串,但是下一行的空格会被计算在内,这反斜杠拆分字符串的一个问题吧,示例代码如下:上述代码的运行结果如下图所示:运行结果和最终想要的结果还是有差异的,插入了一些不需要的空格,因为反斜杠拆分的字符串会把下一行的空格也计算在内。3.总结对长字符串的书写换行,建议使用双引号进行拆分,这是最完美的,显示效果和最终想要的效果是一致的。
CC++编程语言计算机基础 阅读全文»
广告位B招租:2
 2021-03-14T11:58:46.768758    |      C/C++    |     AilsonJack    |     暂无评论    |     262 views
1.基本概念C语言中的inline关键字是C99标准的关键字,它的作用是将函数展开,把函数的代码复制到每一个调用该函数的地方。这样调用该函数的地方就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。可以节省时间,也会提高程序的执行速度。使用inline关键字修饰的函数就是内联函数。在C语言中,如果一些函数被频繁的调用,尤其是这些函数在递归函数中被调用,那么如果递归过程很长,就有可能出现递归还没完成,但是栈空间被用完了。此时如果将这些函数定义为内联函数,那么在递归函数中,内联函数是不会发生函数跳转和压栈操作的,因此也就不存在栈溢出的情况了。2.内联函数的定义方法将一个函数定义为内联函数是比较简单的,直接在定义函数的时候,在函数的前面添加inline关键字即可。内联函数(inline函数)一般和static一起使用,如果内联函数定义在.c文件中定义并且和static关键字一起使用,那么这个内联函数的作用域就是当前的.c文件,其他.c文件不能使用该内联函数;如果内联函数在.h文件中定义并且和static关键字一起使用,那么这个内联函数能够被包含该.h文件的所有源文件或者头文件使用。内联函数的定义方法如下:static inline add(int a, int b){    return (a + b);}3.内联函数使用场景如果函数本身比较简洁,并且函数使用的频率比较高,那么就可以将函数定义为内联函数。当然了,如果函数本身比较简洁,但是使用的频率不高,也可以将函数定义为内联函数。在嵌入式开发中,内联函数还是比较重要的,将一些功能简洁的函数定义为内联函数,可以减少代码的跳转和栈空间的使用,RAM在嵌入式系统中还是比较宝贵的。使用内联函数可以使程序的执行效率更高,同时是代码更紧凑。当然了使用内联函数的缺点也比较明显,就是程序的大小会变大(程序占用flash的空间变大,占用的RAM不变),因为每一个调用内联函数的地方,都会有一个内联函数的拷贝。4.内联函数和宏定义的一些对比内联函数在某种程度上和宏定义很想,比如上述的内联函数可以使用宏定义来描述:#define add(a, b)   ((a) + (b))虽然内联函数和宏很像,但是在使用方法上和宏还是有很大区别的,下面就从几个方面来对内联函数和宏进行对比:(1).展开的时机:内联函数在编译的时候展开,因此inline关键字是一个编译关键字。宏是在预处理时展开,因此#define关键字是一个预处理关键字。(2).参数类型检查:内联函数会进行严格的参数类型检查。宏不会检查参数类型,只是做简单的字符串替换,因此在使用带参数的宏时会有一些副作用,编写程序时要人为预防。(3).是否允许有复杂语句内联函数不允许出现复杂语句,如果出现复杂语句,该函数将不会展开,例如递归,大型循环等。宏定义对此不做要求。宏只是做字符串替换操作,而不了解语句的含义。(4).是否一定被展开内联函数不一定会被展开,是否展开是由编译器决定的。宏一定会被展开,只要使用了宏就可以保证被展开。
CC++编程语言计算机基础 阅读全文»
 2021-03-07T12:00:41.834717    |      C/C++    |     AilsonJack    |     暂无评论    |     222 views
这篇文章摘抄自: <<Linux设备驱动开发详解(第二版)>>,在这里记录下来方便自己和其他小伙伴查阅。在 Linux 内核中,经常会看到do{} while(0)这样的语句,许多人开始都会疑惑,认为do{} while(0)毫无意义,因为它只会执行一次,加不加do{}  while(0)效果是完全一样的,其实 do {}while(0)的用法主要用于宏定义中。这里用一个简单点的宏来演示:#define SAFE_FREE(p) do{ free(p); p = NULL;} while(0)假设这里去掉 do...while(0),即定义 SAFE_DELETE 为:#define SAFE_FREE(p) free(p); p = NULL;那么以下代码if(NULL != p)    SAFE_DELETE(p)else    .../* do something */会被展开为:if(NULL != p)    free(p); p = NULL;else    .../* do something */展开的代码中存在两个问题。(1)因为 if 分支后有两个语句,导致 else 分支没有对应的 if,编译失败。(2)假设没有 else 分支,则 SAFE_FREE 中的第二个语句无论 if 测试是否通过,都会执行。的确,将 SAFE_FREE 的定义加上{}就可以解决上述问题了,即:#define SAFE_FREE(p) { free(p); p = NULL;}这样,代码:if(NULL != p)    SAFE_DELETE(p)else    ... /* do something */会被展开为:if(NULL != p)    { free(p); p = NULL; }else    ... /* do something */但是,在 C 程序中,每个语句后面加分号是一种约定俗成的习惯,那么,如下代码:if(NULL != p)    SAFE_DELETE(p);else    ... /* do something */将被扩展为:if(NULL != p)    { free(p); p = NULL; };else    ... /* do something */这样,else 分支就又没有对应的 if 了,编译将无法通过。假设用了 do {} while(0),情况就不一样了,同样的代码会被展开为:if(NULL != p)    do{ free(p); p = NULL;} while(0);else    ... /* do something */不会再出现编译问题。do while(0)的使用完全是为了保证宏定义的使用者能在不出现编译错误的情况下使用宏,它不对其使用者做任何假设。
CC++编程语言计算机基础 阅读全文»
 2021-02-26T20:14:58.003964    |      C/C++    |     AilsonJack    |     暂无评论    |     101 views
宏定义尤其是带参数的宏定义,特别容易出现一些隐藏问题,因为宏定义在预处理阶段是按照定义原封不动的进行展开,此时如果展开之后涉及到运算符优先级的问题,那么隐藏bug就此出现。这里我先列举一个简单的例子,然后归纳下带参数宏定义对于括号使用的一些说明。1.构造带有隐藏bug的宏定义下面定义两个带参数宏,MUL_TWO是将两个数进行相乘,MUL_THREE是将三个数进行相乘。#define MUL_TWO(val1, val2)     (val1 * val2)#define MUL_THREE(x, y, z)      (MUL_TWO(x, y) * z)比如我这里计算2 * 3 * 4的运算结果,那么只需调用宏MUL_THREE(2, 3, 4)就可得到计算结果为:24,计算结果是正确的。但是如果将MUL_THREE(2, 3, 4)修改为MUL_THREE(1+1, 1+2, 1+3),此时的运算结果又是多少呢,很简单,我们将这个宏进行展开,展开的过程如下所示:MUL_THREE(1+1, 1+2, 1+3)    => (MUL_TWO(1+1, 1+2) * 1+3)(MUL_TWO(1+1, 1+2) * 1+3)   => ((1+1 * 1+2) * 1+3)然后我们计算下,得出结果是7,是不是计算错误了。2.改造上述宏定义这里的宏定义还是比较简单的,并且大多数的小伙伴应该都知道在定义带参数的宏时,参数需要使用括号括起来,那么我们改造下上述的宏,改造结果如下所示:#define MUL_TWO(val1, val2)     ((val1) * (val2))#define MUL_THREE(x, y, z)      (MUL_TWO(x, y) * z)此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:MUL_THREE(1+1, 1+2, 1+3)    => (MUL_TWO(1+1, 1+2) * 1+3)(MUL_TWO(1+1, 1+2) * 1+3)   => (((1+1) * (1+2)) * 1+3)然后我们计算下,得出结果是9,计算结果还是有问题。仔细检查下宏定义,原来是对MUL_THREE宏的z没有用括号括起来,这个问题也是比较容易犯的,修改好之后的宏如下所示:#define MUL_TWO(val1, val2)     ((val1) * (val2))#define MUL_THREE(x, y, z)      (MUL_TWO(x, y) * (z))此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:MUL_THREE(1+1, 1+2, 1+3)    => (MUL_TWO(1+1, 1+2) * (1+3))(MUL_TWO(1+1, 1+2) * (1+3)) => (((1+1) * (1+2)) * (1+3))此时的计算结果就是没问题的了。这里我再提个问题,为什么你在MUL_THREE宏中,只使用括号括起了z,为啥x和y你不同等对待,确实哈,如果对于不是很熟悉的小伙伴,可能看到我说的情况,会毫不犹豫的也对x和y进行同样的保护;也有的小伙伴看到我说的这个情况可能脑子里面就晕了。3.带参数宏定义对于括号使用的一些说明其实不对x和y做保护是有一个前提的,那就是你所定义的每一个宏定义都要确保对在当前宏中使用到的参数用括号进行保护。不知道各位明白我的意思不,不明白的话,看看我下面的总结吧。带参数宏定义,对于括号何时使用的总结:(1).带参数宏定义,如果参数在当前的宏中有进行运算,那么必须对该参数使用括号括起来(类似例子中MUL_THREE里面的z,MUL_TWO里面的val1和val2);(2).带参数宏定义,如果参数没有在当前的宏中有进行运算,而是直接当成参数传递给其他的宏,那么该参数是不用使用括号进行保护的(类似例子中MUL_THREE里面的x和y)。对于上面的总结第(2)点,能够对传递给其他宏的参数不进行括号保护是因为总结的第(1)点已经对宏做了一个规定,只要所有的宏定义都按照第(1)点进行书写,那么第(2)点自然也就不会出什么问题。各位小伙伴在定义带参数的宏时,按照我上面说的2点进行书写,什么隐藏bug的也就不会存在了。如果文中有什么问题欢迎指正,毕竟博主的水平有限。
CC++编程语言计算机基础 阅读全文»
广告位B招租:3
 2020-12-06T22:02:22.853039    |      C/C++    |     AilsonJack    |     暂无评论    |     670 views
通常情况下,像'>','<'和'=='这类的比较运算符的优先级要低于计算运算符。所以下面的两段代码是一样的:代码1:代码2:但是,如果有&和|参与的运算就要注意了,下面的代码3写法未必是你想的那样,其实际运算顺序是代码4那样的。代码3:代码4:是不是有点奇怪呀,这是因为&和|这两个运算符的优先级低于'>','<'和'=='这类的比较运算符的优先级。注:实际编程过程中,如果实在不清楚运算符的具体优先级关系,那么最好的保障就是在合适的地方添加上括号。比如代码3,我们应该加上括号,也就是代码5那样,才是我们想要的运算顺序:代码5:
CC++编程语言计算机基础 阅读全文»
 2020-07-05T22:05:44.122000    |      C/C++    |     AilsonJack    |     暂无评论    |     279 views
1.问题描述最近进行ARM嵌入式系统开发过程中遇到一个问题,就是打印浮点数据不正确。这里的打印函数在其他文件定义的,在main.c中调用了打印函数,但是并没有include打印函数的头文件,编译能够正确的编译过去,但是打印浮点数据时浮点数据的内容始终不正确,比如kprintf("float_num:%f\r\n", 12.06);实际显示的内容可能是:float_num:0.0000。最开始以为浮点的堆栈处理问题,后来检查浮点的入栈和出栈并没有什么问题,后来调试发现kprintf("float_num:%f\r\n", 12.06);这句代码的汇编格式使用d0在保存浮点数据,正常来说ARM传递参数使用的是r0,r1,r2,r3寄存器或者堆栈,这明显就不对,采用的貌似是编译器的通用参数处理方式。当然了导致这个问题的原因就是kprintf这个函数并未声明,因为kprintf函数未声明,编译器在编译当前文件时,并不知道kprintf函数的参数及顺序,因此采用的貌似是编译器的通用参数处理方式。kprintf函数未声明时,kprintf("float_num:%f\r\n", 12.06);对应的汇编代码为:vldr    d0, [pc, #188]ldr     r0, [pc, #200]bl      kprintfkprintf函数在main.c文件中声明了时,kprintf("float_num:%f\r\n", 12.06);对应的汇编代码为:add    r3, pc, #252ldrd   r2, r3, [r3]ldr    r0, [pc, #188]bl     kprintf函数未声明除了造成上述问题之外(参数传入的不正确导致结果出错),也可能导致结果正确,但是返回的结果不正确(比如一个函数返回double型的结果,如果函数未声明就使用,可能会返回4字节的结果,导致结果返回错误)。函数未声明时,kprintf("int_num:%d\r\n", 15);能够正确的显示,因为此时15这个值能够通过普通寄存器(r0/r1/r2/r3)传递,因此不会出现打印浮点数的问题。如果传递的参数或者返回的值,不能通过普通寄存器(r0/r1/r2/r3)传递时,就可能出现奇怪的问题了。2.问题解决方法解决这个问题的方法自然是,在使用到kprintf的文件中include打印函数kprintf的头文件。3.小结对于开发过程中,如果编译时提示"warning: implicit declaration of function 'xxx'"这类的信息,一定还是加上这些函数的声明。如果不添加函数声明,编译虽然能够通过,但是遇到我上面提及的怪异问题,调试可能都不知如何下手,谨记吧。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
ARMCC++嵌入式 阅读全文»
 2019-01-24T19:38:18.749000    |      C/C++    |     AilsonJack    |     暂无评论    |     1210 views
最近在写一个操作文件的函数,使用fseek()函数定位到文件某个位置,然后用fwrite()重新更新该位置的内容,发现该位置处的内容并没有更新,反而更新的内容竟然出现在了文件末尾,真的是不知道什么情况。下面先简单复现我所遇到的问题吧,示例代码如下:将上述代码编译,然后在编译输出文件所在的文件夹中创建一个test.txt文件,内容为:123456789abcdef:接着运行程序,打开test.txt,可以看到内容(字符W)被写到文件的末尾了:这是什么情况,明明使用fseek定位到文件的开头了,但是实际却写入到了文件末尾,好吧,当时我也是挺困惑的,难道是写fseek()接口函数的哥们还遗留有什么bug。后来上网查证,发现这是我们打开文件的所使用的模式(”a+”)在作怪。下面看看对该模式的描述,直接在终端输入:man fopen其中对fopen()函数涉及的a与a+模式的描述如下:a:Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.a+:Open for reading and appending (writing at end of file). The file is created if it does not exist.  The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.上述对a与a+模式的描述大致内容是:打开一个文件,如果该文件不存在将创建文件,初始化的文件读指针位于文件的开头;对于文件的写操作,则始终将写入内容追加到文件的末尾,与文件指针没有关系。想必看到这里,大家也明白了。如果想使用fseek()函数定位写操作指针,那么就修改fopen()涉及的模式,这里修改为”r+”,就能实现将内容写到文件的开始了,对于自己的程序大家还是根据实际情况修改为相应的模式。如果有什么疑问,欢迎留言交流^_^。
编程语言CC++ 阅读全文»
广告位B招租:4
广告位B招租:2
广告位B招租:3
广告位B招租:4
  • 1

  本站信息

目前本站共被浏览 127491 次
目前本站已经运行 2679 天
目前本站共有 147 篇文章
目前本站共有 2 条评论信息
目前本站共有 98 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2022年06月17日
广告位E招租
Copyright © 2015~2021  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号