C语言12个浅显易懂的知识点
1.关于+=以及-=
这是两个运算符,但你否有过这种经历:
[cpp]view plaincopy
inttemp;
chari
for(i=0;i
{
...
temp=+2;//这里本意是每次循环,temp都自增2,但是却将'+='写成了'=+',按照这种写法,每次循环都为temp赋值正数2,与本意相差甚远
}
2. 关于意想不到的死循环
[cpp]view plaincopy
unsignedchari;
for(i=0;i=0;i--)
{
//something
}
这也是一个死循环,你看出什么原因了吗?无论i如何减,i都是大于等于0的。
这就告诉我们对于每个变量类型的取值范围要由清醒的认识。值得注意的是相同的变量类型对于不同的cpu构架和不同的编译器会有不同的结果。比如int类型在大多数16位cpu构架中占用两个字节,但在32位cpu中却往往占用4个字节;char类型在绝大多数编译器中都是有符号数,但在keilmdk中却是无符号数,若是要在keilmdk下定义有符号char类型变量,必须用signed显式声明。我曾读过一本书,其中有一句话:“signed关键字也是很宽宏大量,你也可以完全当它不存在,在缺省状态下,编译器默认数据位signed类型”,这句话便是有异议的,我们应该对自己所用的cpu构架以及编译器熟练掌握。
3. 关于'='和'=='
[cpp]view plaincopy
if(value=0x01)
{
//something
}
当我们判断一个变量是否等于0x01时,你是否也写过类似上面的代码?c语言的创造者认为赋值运算符=出现的概率要远远大于等于运算符==,因此,我们正常逻辑中的等于符号(=)在c语言中成了赋值运算符,而c语言的等于运算符却被两个等于号(==)所代替。我之所以对这个事件耿耿于怀是因为我在大二的时候参加的c++二级上机考试,当我感觉很轻松的做完最后一道题后,却发现运算的结果却与逻辑相悖,经过调试发现,有一个条件一直为真,我检查了很多遍才发现出问题的逻辑将等于运算符写成了赋值运算符。在if语句中给变量赋一个非零值,也难怪这个逻辑总是为真。
编译器同样不对这个问题做出指导性建议,值得一提的是,如果你在keil的if语句中使用了赋值运算符,编译器会给出警告。
避免这个问题的一个很好的办法是使用良好编程习惯,比如上面的代码可写为:
[cpp]view plaincopy
if(0x01==value)
{
//something
}
将常量值放到变量的前面,即使将等于运算符写成赋值运算符,编译器也能产生一个语法错误,因为将一个变量赋值给一个常量是非法的。
4.error:#7:unrecognizedtoken
我在刚使用c语言以及keil编译器时,对于这个编译器错误,有很深的印象。出现这个编译错误的典型代表是在敲代码的时候输入了中文标点!!
真是让人感慨万分的错误!我们这些与硬件打交道的程序员,为模数电生,为pcb死,为debug奋斗一辈子,吃需求的亏,上大小写的当,最后死在标点上!!
5.关于字母'o'和数字'0',以及字母'l'和数字'1',在嵌入式编程中很容易和寄存器打交道,一个cpu如果有两个相同模块时,这些模块寄存器,往往使用数字0和数字1来区分模块0和模块1,比如,nxp的arm7串口模块的两个接收缓冲寄存器分别为:u0rbr和u1rbr,要命的是在键盘上字母o和数字0相距的还那么近,你是否也有将上述寄存器写成uorbr和ulrbr的经历,我是曾经在这方面纠结过一次,好在编译器能指出这个未定义的字符串。
6.sizeof()
不知道有多少人和我曾经一样,将这个关键字认为是一个库函数。
[cpp]view plaincopy
inti,j;
j=sizeof(i);//对于这一句,当初压根没把它往关键字上想,这家伙伪装的实在够好。
既然提到它,不如多说一下,sizeof在计算变量所占空间大小时,括号可以省略,而计算类型大小时,不能省略。什么意思呢?还是上面的变量声明,可以写成j=sizeof(i)也可以写成j=sizeofi,因为这是计算变量所占空间大小;可以写成j=sizeof(int),但不可以写成j=sizeofint,因为这是计算数据类型大小。
总体来说,关键字sizeof的具有一定的变态基础的,在我还是小白的时候,曾经为下面的一道题伤过脑袋:
下面代码里,假设在32位系统下,个sizeof计算的结果分别是多少?
int*p=null;
sizeof(p)的值是:
sizeof(*p)的值是:
inta[100]
sizeof(a)的值是:
sizeof(a[100])的值是:
sizeof(&a)的值是:
sizeof(&a[0])的值是:
intb[100];
voidfun(intb[100])
{
sizeof(b);
}
sizeof(b)的值为:
7 关于数组越界
[cpp]view plaincopy
inta[30];
for(i=30;i>0;i--)
{
a[i]=something;
}
这是个典型的数组越界例子,最近我同事的一个程序中便出现了。不知道有多少同学遇到或将要遇到数组越界问题,即便你定义了30个数组a[30],你也不可以为a[30]赋值,因为下标为30的元素已经越界了。所以说数组下标定义的很奇特,它是从0开始的。但当我们还是新手的时候,最容易忽视这一点。幸好现在的有些编译器会对这个越界产生警告信息。
8. 关于宏
[cpp]view plaincopy
#definemax_tast4;
这个错误编译器会指出的,即便这样,相信很多同学在最初的时候也不会在第一时间发现这句代码的最后多了一个分号。这个分号会导致一些编译器报错,因为宏定义的结尾并不需要分号。
同样与define有关的是这样一句:#defineconfig.h,我便吃过类似暗亏,在编译器的提示之下,看了几遍才发现头文件包含应该是#includeconfig.h。
既然提到#define,还是说说它需要注意的几个点,也是经常在资料上被提及的。
a.使用#define时,括号一定要足够。比如定义一个宏函数,求x的平方:
[cpp]view plaincopy
#definesqr(x)x*x..............1
或者这样写:
[cpp]view plaincopy
#definesqr(x)(x)*(x)...............2
上面两种都是有风险的,对于第一种定义,sqr(10+1)就会得到和我们的设想不一致的结果;第二种sqr(5*3)*sqr(5*3)也会得到和我们设想不一致的结果,因此更安全的定义方法是:
[cpp]view plaincopy
#definesqr(x)((x)*(x))
b.使用#define的时候,,意空格的使用。比如下面的例子:
[cpp]view plaincopy
#definesqr(x)((x)*(x))
这已经不是sqr(x)函数了,编译器会把认为定义了一个宏sqr,代表(x)((x)*(x)),因为sqr与(x)之间有空格。这点需要注意。
c.使用'#'在字符串中包含宏参数。比如下面的例子:
[cpp]view plaincopy
#definesqr(x)printf(thesquareofxis%d.\n,((x)*(x)))
[cpp]view plaincopy
如果这样使用宏:
[cpp]view plaincopy
sqr(8)
则输出为:
thesquareofxis64.
这个时候引号中的x被当做字符串来处理了,而不是一个可以被宏参数替换的符号.如果你想在字符中的x也被宏参数替换,可以这么来定义宏:
[cpp]view plaincopy
#definesqr(x)printf(thesquareof#xis%d.\n,((x)*(x)))
这样得到的结果为:
thesquareof8is64.
上面的这些例子,恐怕是网上随处可见的,但真的会这么用却有待考证。下面给出一个我自己遇到的不加括号产生错误的例子。在嵌入式编程中,遇到读取io端口某一位的电平状态的场合是在平常不过的了,比如在nxp的arm7中,读取端口p0.11的电平状态并判断是否为高电平,代码如下:
[cpp]view plaincopy
#definereadsdaio0pin&(1<<11)//定义宏,读io口p0.11的端口状态,但并未使用足够多的括号
//判断p0.11端口是否为高电平,使用下述语句就是错误的:
if(readsda==(1<<11))
{
//是高电平,处理高电平的问题
}
编译器在编译后将宏带入,原if语句变为:
[cpp]view plaincopy
if(io0pin&(1<<11)==(1<<11))
{
//是高电平,处理高电平的问题
}
这样的话,运算符'=='的优先级是大于'&'的,从而io0pin&(1<<11)==(1<>4)*10+uctimevalue&0x0f;//十进制转化为16进制,但忽略了运算符'+'的优先级是大于运算符'&'的
像这类代码编译肯定可以通过,但运行的结果却出乎我的意料,而且由于我先入为主的错误思想,要在一大段代码中发现这个错误着实要花费一番功夫。
再例如,如果我想判断一个寄存器的某一位是否为零,假如是判断寄存器io0set的bit17是否为零,但代码却写成了这样:
[cpp]view plaincopy
if(io0set&(1<<17)==0)
这样写其实是得不到正确的结果的,因为我忽略了==的优先级是大于&的.按照上面的代码分析:因为==的优先级大于&,所以程序先判断(1<<17)是否等于0?发现这是不相等的,所以(1<<17)==0表达式的值为假,即为0,0与(&)上任何一个数都是0,所以io0set&(1<<17))==0整个表达式的值永远为0,这与原意相差甚远。
按照原意,应该这样写:
[cpp]view plaincopy
if((io0set&(1<
全体单目第二; 所有的单目运算符比如++--+(正)-(负)指针运算*&
乘除余三,加减四; 这个余是指取余运算即%
移位五,关系六; 移位运算符:,关系:>=<=等
等于(与)不等排第七; 即==!=
位与异或和位或; 这几个都是位运算:位与(&)异或(^)位或(|)
三分天下八九十;
逻辑或跟与; 逻辑运算符:||和&&
十二和十一; 注意顺序:优先级(||)底于优先级(&&)
条件高于赋值, 三目运算符优先级排到13位只比赋值运算符和,高
逗号运算级最低! 逗号运算符优先级最低
大众VIZZION概念车来袭_梦想一定要有
什么是时钟电路?什么是脉冲?时钟电路是如何生成脉冲的?
人工智能冲刺是一项短期的概念验证性人工智能项目
5G技术在无人驾驶汽车中的应用
菌落计数器的使用方法及注意事项
C语言12个浅显易懂的知识点
MF11补偿型热敏电阻
Jaunt公司将重组,计划缩减VR业务以加快速度转型AR
时控开关是什么,时控开关怎么设置时间
TCA6507PWR型号LED驱动器的产品介绍
linux内核中的debugfs该怎样去使用呢?
汽车行业如何做需求管理
微软推出可体验VR触觉的腕上小工具
便携式与固定式气体检测仪的区别
中国已在称霸第四次工业革命的路上
关于捷豹路虎新款揽胜插电混动P400e性能分析
数据采集开关系统的特点说明
实现高级自动驾驶的 5 大挑战
高保真Cruden驾驶模拟器利用基于动作的系统 研究自动驾驶系统
自动驾驶要有适应国情的自主研发能力