标签 计算机基础 下的文章
 2022-12-04T17:28:24.298432    |      C/C++    |     AilsonJack    |     暂无评论    |     154 views
1、const char *ptrconst char *ptr 表示的是指向常量的指针,不能通过该指针去修改指针所指向地址中的内容,但是可以修改该指针的指向。 可以通过其他普通指针对同样地址中的数据进行修改,测试例子如下:编译测试程序:gcc test.c -o test.exegcc编译报错,提示对指向常量的指针的错误操作。屏蔽23行的代码 ptr[0] = 'A';,重新编译,运行结果如下:2、char const *ptrchar const *ptr 和 const char *ptr 等价。3、char * const ptrchar * const ptr 表示的是指针的指向不可以被修改,但是可以修改指针指向地址中的内容。测试例子如下:编译测试程序:gcc test.c -o test.exegcc编译报错,提示指针的指向不可以被修改。屏蔽27行的代码 ptr = name;,重新编译,运行结果如下:4、总结const char *ptr 表示的是指向常量的指针,不能通过该指针去修改指针所指向地址中的内容,但是可以修改该指针的指向。char const *ptr 和 const char *ptr 等价。char * const ptr 表示的是指针的指向不可以被修改,但是可以修改指针指向地址中的内容。const放在*号前表示修饰的是指针指向的对象,const放在*号后表示修饰的是指针本身。根据上面章节的内容,下面的写法是什么意思应该比较简单吧:const char * const ptr;表示的是ptr指针的指向不可以被修改,ptr指针所指向地址中的内容不可以被修改。
CC++计算机基础编程语言 阅读全文»
 2022-02-27T18:32:06.395938    |      C/C++    |     AilsonJack    |     暂无评论    |     150 views
不同体系结构的CPU对于数据在内存中存放的排列顺序规定不同。数据在内存中的存储是以字节(byte)为单位的,因此对于半字(Half-Word)和字(Word)在内存中就有两种存储顺序,分别称为:大端模式(Big Endian)和小端模式(Little Endian)。大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中;小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。大端模式的次序就像是我们平时书写的次序,先写大数,后写小数。大端存储次序还广泛运用在TCP/IP协议上,因此又称为网络字节次序。
计算机基础CC++CPU 阅读全文»
 2021-10-29T19:04:46.092336    |      C/C++    |     AilsonJack    |     暂无评论    |     142 views
在程序的开发和调试过程中,可以使用一个宏定义来代替printf输出调试信息,等程序开发完成之后,如果不需要调试信息,直接将宏定义为空就行,这样便于随时打开和关闭调试信息。这样的调试程序的宏一般是可变参数宏,因为打印函数printf是可变参数的,因此定义的宏也要求支持可变参数。可变参数宏可以接受可变数目的参数,就像可变参数的函数一样。可变参数宏也使用三个点(...)来表示宏的可变参数性。__VA_ARGS__ 宏是用来表示可变参数宏的可变参数的内容。简单的说就是将可变参数宏中的 ... 的内容原样传递给右边 __VA_ARGS__ 所在的位置。示例代码如下:通过屏蔽或打开程序中的 __DEBUG__ 宏,可以让调试信息输出或者不输出。
CC++编程语言计算机基础 阅读全文»
 2021-09-26T21:00:10.059909    |      C/C++    |     AilsonJack    |     暂无评论    |     259 views
在编写C程序时,如果想要打印某个字符串,而字符串的内容比较多,这就涉及到对这个长字符串进行书写换行,这里的换行并不会对最终的显示结果进行换行,只是为了阅读代码能够更加的清晰,不至于字符串的内容过长影响代码的阅读体验。1.长字符串示例上述代码需要打印的字符串内容比较长,在代码阅读软件中,可能需要拖动水平方向上的滚动条,才能看清楚字符串的完整内容,这极大的影响了代码的阅读效率。下图是上述代码的运行结果:2.书写长字符串的换行方法方法一:利用双引号对长字符串进行换行在对长字符串进行书写换行时,可以使用双引号将长字符串拆分成多个子字符串,编译器在编译处理时会自动的拼接这些子字符串,不会影响最终想要的显示效果,示例代码如下:上述示例的运行结果如下:运行结果和长字符串未拆分时的效果一样。方法二:利用反斜杠对长字符串进行换行可以使用反斜杠对长字符串进行拆分,反斜杠后的换行符会被C忽略,所以可以拆分字符串,但是下一行的空格会被计算在内,这反斜杠拆分字符串的一个问题吧,示例代码如下:上述代码的运行结果如下图所示:运行结果和最终想要的结果还是有差异的,插入了一些不需要的空格,因为反斜杠拆分的字符串会把下一行的空格也计算在内。3.总结对长字符串的书写换行,建议使用双引号进行拆分,这是最完美的,显示效果和最终想要的效果是一致的。
CC++编程语言计算机基础 阅读全文»
 2021-03-14T11:58:46.768758    |      C/C++    |     AilsonJack    |     暂无评论    |     421 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    |     暂无评论    |     357 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    |     暂无评论    |     172 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++编程语言计算机基础 阅读全文»
 2020-12-06T22:02:22.853039    |      C/C++    |     AilsonJack    |     暂无评论    |     866 views
通常情况下,像'>','<'和'=='这类的比较运算符的优先级要低于计算运算符。所以下面的两段代码是一样的:代码1:代码2:但是,如果有&和|参与的运算就要注意了,下面的代码3写法未必是你想的那样,其实际运算顺序是代码4那样的。代码3:代码4:是不是有点奇怪呀,这是因为&和|这两个运算符的优先级低于'>','<'和'=='这类的比较运算符的优先级。注:实际编程过程中,如果实在不清楚运算符的具体优先级关系,那么最好的保障就是在合适的地方添加上括号。比如代码3,我们应该加上括号,也就是代码5那样,才是我们想要的运算顺序:代码5:
CC++编程语言计算机基础 阅读全文»
 2018-12-25T21:14:13.563000    |      嵌入式学习    |     AilsonJack    |     暂无评论    |     686 views
在计算机内部数值是以补码的方式进行存储的,采用补码进行数据存储当然有其优点,下面会一一介绍相关内容,让各位彻底弄懂原码、反码、补码的概念以及为什么采用补码作为数据存储的方式。注意:下面列举的例子均是指8位的字节数据。对于16位,32位的数据同样适用,只是为了书写方便,8位是最好的选择啦!1、无符号数与有符号数在介绍原码、反码和补码的概念之前,先来了解下无符号数和有符号数。在计算机系统中,我们可以将数分为两类:无符号数和有符号数。无符号数的意思是我们不关心这些数的符号,因此也就无所谓正负,反正它们就是数而已,就像小学生一样,眼中只有自然数。在8位的字节运算中,无符号数的范围是0000_0000B~1111_1111B,即十进制的0~255。有符号数是分正负的,而且规定,数的正负要通过它的最高位来辨别。如果最高位是0,它就是正数;如果是1,就是负数。这样一来,在8位的字节运算环境中,正数的范围是0000_0000B~0111_1111B,即十进制的0~127;负数的范围是1000_0000B~1111_1111B,即10进制的-128~-1。也就是说8位的数据,有符号数的范围是-128~127(对这里的负数有疑问的可以往下面看)。2、原码、反码与补码原码:最高位表示符号位,其余位代表数值大小。如果最高位是0,它就是正数;如果最高位是1,就是负数。例如:十进制数+2,其二进制形式为:0000_0010B;如果是-2,就是1000_0010B。因为最高位是符号位,所以原码二进制的形式值不等于真正的数值。例如上面的有符号数1000_0010B,其最高位1代表负数,表示的数值为-2,而不是形式值130(1000_0010B转换成无符号十进制等于130)。反码:正数的反码是其本身;负数的反码是在原码的基础上,符号位不变,其余各数值位取反。补码:正数的补码是其本身;负数的补码是在其原码的基础上,符号位不变,其余各数值位取反,最后+1(即负数的补码是其反码+1)。下面列举几个8位二进制数的原码、反码和补码的编码形式:十进制数原码反码补码420010 10100010 10100010 1010-421010 10101101 01011101 01101270111 11110111 11110111 1111-1271111 11111000 00001000 00013、补码存储数据的优点在讲述原码、反码和补码时,有一个特殊的数值没有涉及到,那就是0,在我们平时使用中,0是没有正负之分的,但是不可否认+0 = -0,那么下面列举下+0,-0的原码、反码与补码的形式:十进制数原码反码补码+00000 00000000 00000000 0000-01000 00001111 11110000 0000为什么-0的反码1111_1111B进行加1操作求取到的-0补码是0000_0000B。因为这里假定数据宽度是8位的,1111_1111B加1之后,会逐个向上进位,我们假定有第9位,且值为0,那么0_1111_1111B加1之后,就变成了1_0000_0000B,但是实际上我们要的数据是8位的宽度,这里舍弃第9位,最后结果就变成了0000_0000B。好了,在讲述补码的优点之前,先看看计算机为什么不使用原码作为数的存储格式而采用补码作为数的存储格式。下面说说原码的优缺点吧。原码的优点当然是简单直观,例如+2的原码为:0000_0010B,-2的原码为:1000_0010B。原码的缺点也显而易见:(1)、+0与-0在原码中竟然有两个二进制形式,与我们熟知的+0=-0貌似有出入。(2)、原码不能直接参加运算,可能会出错。例如:1+(-1)=0,而用原码的二进制形式进行运算为:0000_0001B + 1000_0001B = 1000_0010B,换算成十进制为-2,显然计算结果错误。那么计算机为什么要使用补码呢?首先,根据运算法则减去一个正数等于加上一个负数,即: 1-1 = 1+(-1),所以计算机被设计成只有加法而没有减法,而让计算机辨别”符号位”会让计算机的基础电路设计变得十分复杂,于是就让符号位也参与运算,从而产生了反码。用反码计算,出现了”0”这个特殊的数值,0带符号是没有任何意义的。而且会有0000_0000B和1000_0000B两个原码编码表示0。于是设计了补码,负数的补码就是反码+1,正数的补码就是正数本身,从而解决了0的符号以及两个编码的问题:用0000_0000B表示0,用1000_0000B表示-128。注意:-128并没有原码和反码。使用补码,不仅仅修复了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数值。如果还不清楚-128怎么来的,那么我就来按我的想法来推出来吧,-127的原码为:1111_1111B,则其反码为:1000_0000B,补码为:1000_0001B,那么自然我们可以想想补码为1000_0000B代表的就是-128=-127-1=1000_0001B - 1= 1000_0000B。这也就是8位数据最小的负整数了。这就是为什么8位二进制,使用补码表示的范围为[-128,127]。补码存储数据的优点:(1)、避免了0的编码有两个;(2)、符号位和有效值位可以一起处理,减法通过加法就可以实现,即简化了计算机的结构设计也提高了运算速度。补码存储数据的缺点:(1)、不够简单直观。当然这里的不够简单直观是对我们来说的。计算机倒没什么。下面给大家推荐一个计算补码的小工具,那就是我们经常使用的Windows计算器,只要选择好相应的数据宽度,输入你的数值,就可以观察到对应的二进制形式了:好了,讲了这么多,相信大家对原码、反码和补码都有一定的认识了,如果还有什么疑问,欢迎留言交流。
嵌入式计算机基础 阅读全文»
  • 1

  本站信息

目前本站共被浏览 155116 次
目前本站已经运行 3316 天
目前本站共有 165 篇文章
目前本站共有 6 条评论信息
目前本站共有 104 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2023年11月26日
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号