一、结构
1.1 setup()
1.2 loop()
二、结构控制
2.1 if
2.2 if…else
2.3 for
2.4 switch case
2.5 while
2.6 do… while
2.7 break
2.8 continue
2.9 return
2.10 goto
三、扩展语法
3.1 ;(分号)
3.2 {}(花括号)
3.3 //(单行注释)
3.4 /* */(多行注释)
3.5 #define
3.6 #include
四、算数运算符
4.1 =(赋值运算符)
4.2 +(加)
4.3 -(减)
4.4 *(乘)
4.5 /(除)
4.6 %(模)
五、比较运算符
5.1 ==(等于)
5.2 !=(不等于)
5.3 <(小于)
5.4 >(大于)
5.5 <=(小于等于)
5.6 >=(大于等于)
六、布尔运算符
6.1 &&(与)
6.2 ||(或)
6.3 !(非)
七、指针运算符
7.1 * 取消引用运算符
7.2 & 引用运算符
八、位运算符
8.1 & (bitwise and)
8.2 | (bitwise or)
8.3 ^ (bitwise xor)
8.4 ~ (bitwise not)
8.5 << (bitshift left)
8.6 >> (bitshift right)
九、复合运算符
9.1 ++ (increment)
9.2 – (decrement)
9.3 += (compound addition)
9.4 -= (compound subtraction)
9.5 *= (compound multiplication)
9.6 /= (compound division)
9.6 &= (compound bitwise and)
9.8 |= (compound bitwise or)
————————————————————————————————————————————
1.1 setup()
在Arduino中程序运行时将首先调用 setup() 函数。用于初始化变量、设置针脚的输出\输入类型、配置串口、引入类库文件等等。每次 Arduino 上电或重启后,setup 函数只运行一次。
示例
int buttonPin = 3;//定义一个变量buttonPin为针脚3
void setup()
{
Serial.begin(9600);//定义初始串口波特率为9600
pinMode(buttonPin, INPUT);//定义buttonPin也就是前面定义的针脚3为input输入针脚
}
void loop()
{
// …
}
1.2 loop()
在 setup() 函数中初始化和定义了变量,然后执行 loop() 函数。顾名思义,该函数在程序运行过程中不断的循环,根据一些反馈,相应改变执行情况。通过该函数动态控制 Arduino 主控板。
示例
int buttonPin = 3; // setup 中初始化串口和按键针脚.
void setup()
{
beginSerial(9600);
pinMode(buttonPin, INPUT);
}
// loop 中每次都检查按钮,如果按钮被按下,就发送信息到串口
void loop()
{
if (digitalRead(buttonPin) == HIGH)//通过eigitalRead读取到针脚3的电平值是否为高
serialWrite(‘H’);//是高就通过串口写出H
else
serialWrite(‘L’);//如果不是就通过串口写出L
delay(1000);//延时1000毫秒,也就是1秒
}
————————————————————————————————————————————
2.1 if
if(条件判断语句)和 ==、!=、<、>(比较运算符)
if 语句与比较运算符一起用于检测某个条件是否达成,如某输入值是否在特定值之上等。if 语句的语法是:
if (someVariable > 50)
{
// 执行某些语句
}
本程序测试 someVariable 变量的值是否大于 50。当大于 50 时,执行一些语句。换句话说,只要 if 后面括号里的结果(称之为测试表达式)为真,则执行大括号中的语句(称之为执行语句块);若为假,则跳过大括号中的语句。 if 语句后的大括号可以省略。若省略大括号,则只有一条语句(以分号结尾)成为执行语句。
下面几种写法都是正确的:
第一种:
if (x > 120) digitalWrite(LEDpin, HIGH);//判断x的值是不是大于120,是的话就让LEDpin这个针脚的电平成为高电平
第二种:
if (x > 120)
digitalWrite(LEDpin, HIGH);
第三种:
if (x > 120){ digitalWrite(LEDpin, HIGH); }
第四种:
if (x > 120){
digitalWrite(LEDpin1, HIGH);
digitalWrite(LEDpin2, HIGH);
}
在小括号里求值的表达式,需要以下操作符:
比较运算操作符:
x == y(x 等于 y)注意这是等于,并不是赋值,赋值是=
x != y(x 不等于 y)
x < y(x 小于 y)
x > y(x 大于 y)
x <= y(x 小于等于 y)
x >= y(x 大于等于 y)
警告:
注意使用赋值运算符的情况(如 if (x = 10))。一个“=”表示的是赋值运算符,作用是将 x 的值设为 10(将值 10 放入 x 变量的内存中)。两个“=”表示的是比较运算符(如 if (x == 10)),用于测试 x 和 10 是否相等。后面这个语句只有 x 是 10 时才为真,而前面赋值的那个语句则永远为真。
这是因为 C 语言按以下规则进行运算 if (x=10):10 赋值给 x(只要非 0 的数赋值的语句,其赋值表达式的值永远为真),因此 x 现在值为 10。此时 if 的测试表达式值为 10,该值永远为真,因为非 0 值永远为真。所以,if (x = 10) 将永远为真,这就不是我们运行 if 所期待的结果。另外,x 被赋值为 10,这也不是我们所期待的结果。
if 的另外一种分支条件控制结构是 if…else 形式。
2.2 if…else
if/else是比if更为高级的流程控制语句,它可以进行多次条件测试。比如,检测模拟输入的值,当它小于500时该执行哪些操作,大于或等于500时执行另外的操作。代码如下:
if (pinFiveInput < 500)
{
// 执行A操作
}
else
{
// 执行B操作
}
else可以进行额外的if检测,所以多个互斥的条件可以同时进行检测。
测试将一个一个进行下去,直到某个测试结果为真,此时该测试相关的执行语句块将被运行,然后程序就跳过剩下的检测,直接执行到if/else的下一条语句。当所有检测都为假时,若存在else语句块,将执行默认的else语句块。
注意else if语句块可以没有else语句块。else if分支语句的数量无限制。
if (pinFiveInput < 500)
{
// 执行A操作
}
else if (pinFiveInput >= 1000)
{
// 执行B操作
}
else
{
// 执行C操作
}
另外一种进行多种条件分支判断的语句是switch case语句。
2.3 for
for语句
描述
for语句用于重复执行一段在花括号之内的代码。通常使用一个增量计数器计数并终止循环。for语句用于重复性的操作非常有效,通常与数组结合起来使用来操作数据、引脚。
for循环开头有3个部分:
(初始化;条件;增量计数){
//语句
}
“初始化”只在循环开始执行一次。每次循环,都会检测一次条件;如果条件为真,则执行语句和“增量计数”,之后再检测条件。当条件为假时,循环终止。
例子
//用PWM引脚将LED变暗
int PWMpin = 10; //将一个LED与47Ω电阻串联接在10号针脚
void setup()
{
//无需设置
}
void loop()
{
for (int i=0; i <= 255; i++)//定义一个变量i,并赋值为0,当i小于等于255的时候i就加1,也可写成i+=5,这样每循环一次i就加5
{
analogWrite(PWMpin, i);//让10号针脚的电平改变为i
delay(10);//延时10毫秒
}
}
编程提示
C语言的for循环语句比BASIC和其他电脑编程语言的for语句更灵活。除了分号以外,其他3个元素都能省略。同时,初始化,条件,增量计算可以是任何包括无关变量的有效C语句,任何C数据类型包括float。这些不寻常的for语句可能会解决一些困难的编程问题。
例如,在增量计数中使用乘法可以得到一个等比数列:
for(int x = 2; x < 100; x = x * 1.5){//定义X为2,当X小于100的时候X重新赋值为它自己的1.5倍
println(x);//打印输出x的值
}
生成:2,3,4,6,9,13,19,28,42,63,94
另一个例子,使用for循环使LED产生渐亮渐灭的效果:
int PWMpin = 10; //将一个LED与47Ω电阻串联接在10号针脚
void setup()
{
//无需设置
}
void loop()
{
int x = 1; //定义一个整数变量x赋值为1
for (int i = 0; i > -1; i = i + x) //定义i为0当i小于负一的时候,i的值为它自己加上X,也就是加上1,灯就依次变亮了
{
analogWrite(PWMpin, i); //让PWMpin针脚的电平变为i
if (i == 255) x = -1; // 当i等于最大值255的时候,把x改变为负一,这样再循环上去的时候i的值就会依次减一,就由亮变暗了
delay(10); //延时10毫秒,如果还想让灯由暗变亮的话就再写个判断
if(i==0) x=1; //当i减小到0的时候又把x变成1,这样i就又依次加1,灯由暗变亮了
delay(10);
}
}
2.4 switch case
switch / case语句
和if语句相同,switch…case通过程序员设定的在不同条件下执行的代码控制程序的流程。特别地,switch语句将变量值和case语句中设定的值进行比较。当一个case语句中的设定值与变量值相同时,这条case语句将被执行。
关键字break可用于退出switch语句,通常每条case语句都以break结尾。如果没有break语句,switch语句将会一直执行接下来的语句(一直向下)直到遇见一个break,或者直到switch语句结尾。
语法也是先switch然后跟括号()括号内写上变量值,后面跟大括号,大括号里写上case分支
例子
switch (var) {
case 1: //case 1后面是冒号
//当var等于1时,执行一些语句
break;
case 2
//当var等于2时,执行一些语句
break;
default:
//如果没有任何匹配,执行default
//default可有可不有
}
语法
switch (var) { //定义检查var的值
case label1: //如果var的值是label1的就就执行下面的语句
// 程序语句
break;
case label2: //如果var的值是label2的就就执行下面的语句
//程序语句
break;
default: //如果var的值都不在上面的里面的话就执行下面的语句
//程序语句
}
参数
var: 用于与下面的case中的标签进行比较的变量值
label: 与变量进行比较的值
2.5 while
while循环
描述
while循环会无限的循环,直到括号内的判断语句变为假。必须要有能改变判断语句的东西,要不然while循环将永远不会结束。这在您的代码表现为一个递增的变量,或一个外部条件,如传感器的返回值。
语法
while(表达){
//语句
}
参数
表达:为真或为假的一个计算结果
例子
var = 0; //定义一个变量var赋值为0
while(var < 200){ //当var的值小于200的时候执行下面的语句
var++ //var依次加1,加200次,直到var的值不小于200为止
}
2.6 do…while
do…while循环与while循环运行的方式是相近的,不过它的条件判断是在每个循环的最后,所以这个语句至少会被运行一次,然后才被结束。
do
{
//语句
}while(测试条件);
例子
do
{
delay(50); //延时50秒
X = readSensors(); //给X赋值
}while(X <100); //当x小于100时,继续运行,当x不小于100的时候就不运行了
2.7 break
break用于退出do,for,while循环,能绕过一般的判断条件。它也能够用于退出switch语句。
例子
for (x = 0; x < 255; x ++)
{
digitalWrite(PWMpin, x);
sens = analogRead(sensorPin);
if (sens > threshold){
x = 0;
break; //这里用break就打断循环了,相当于在此结束了,程序就不再循环了
}
delay(50);
}
2.8 continue
continue语句跳过当前循环中剩余的迭代部分( do,for 或 while )。它通过检查循环条件表达式,并继续进行任何后续迭代。
例子
for (x = 0; x < 255; x ++)
{
if (x > 40 && x < 120){
continue; // 当x在40与120之间时,跳过后面两句,即迭代。
}
digitalWrite(PWMpin, x);
delay(50);
}
2.9 return
终止一个函数,如有返回值,将从此函数返回给调用函数。
语法
return;
return value; // 两种形式均可
参数
value:任何变量或常量的类型
例子
一个比较传感器输入阈值的函数
int checkSensor(){ //这儿定义了一个整数形函数checkSensor
if (analogRead(0) > 400) { //如果读取0针脚的数据大于400的话
return 1;} //返回1,相当于调用这个函数后得到的值是1
else{
return 0; //返回0,相当于调用这个函数后得到的值是0
}
}
return关键字可以很方便的测试一段代码,而无需“comment out(注释掉)” 大段的可能存在bug的代码。
void loop(){
//写入漂亮的代码来测试这里。
return;
//剩下的功能异常的程序
//return后的代码永远不会被执行
}
2.10 goto
程序将会从程序中已有的标记点开始运行,这个东西,少用
语法
label:
goto label; //从label处开始运行
提示
不要在C语言中使用goto编程,某些C编程作者认为goto语句永远是不必要的,但用得好,它可以简化某些特定的程序。许多程序员不同意使用goto的原因是, 通过毫无节制地使用goto语句,很容易创建一个程序,这种程序拥有不确定的运行流程,因而无法进行调试。感觉就像你明明在1上一下就跳到了8上,并 不是从上而下的过程。
的确在有的实例中goto语句可以派上用场,并简化代码。例如在一定的条件用if语句来跳出高度嵌入的for循环。
例子
for(byte r = 0; r < 255; r++){
for(byte g = 255; g > -1; g–){
for(byte b = 0; b < 255; b++){
if (analogRead(0) > 250){
goto bailout; //这儿有一个goto语句所以程序会跳转到下一个bailout
}
//更多的语句…
}
}
}
bailout: //goto语句跳转到这儿继续执行
————————————————————————————————————————————
3.1 ;(分号)
用于表示一句代码的结束。
例子
int a = 13;
提示
在每一行忘记使用分号作为结尾,将导致一个编译错误。错误提示可能会清晰的指向缺少分号的那行,也可能不会。如果弹出一个令人费解或看似不合逻辑的编译器错误,第一件事就是在错误附近检查是否缺少分号。
3.2 {}(花括号也称大括号)
大括号(也称为“括号”或“大括号”)是C编程语言中的一个重要组成部分。它们被用来区分几个不同的结构,下面列出的,有时可能使初学者混乱。
左大括号“{”必须与一个右大括号“}”形成闭合。这是一个常常被称为括号平衡的条件。在Arduino IDE(集成开发环境)中有一个方便的功能来检查大括号是否平衡。只需选择一个括号,甚至单击紧接括号的插入点,就能知道这个括号的“伴侣括号”。
目前此功能稍微有些错误,因为IDE会经常会认为在注释中的括号是不正确的。
对于初学者,以及由BASIC语言转向学习C语言的程序员,经常不清楚如何使用括号。毕竟,大括号还会在”return函数”、“endif条件句”以及“loop函数”中被使用到。
由于大括号被用在不同的地方,这有一种很好的编程习惯以避免错误:输入一个大括号后,同时也输入另一个大括号以达到平衡。然后在你的括号之间输入回车,然后再插入语句。这样一来,你的括号就不会变得不平衡了。
不平衡的括号常可导致许多错误,比如令人费解的编译器错误,有时很难在一个程序找到这个错误。由于其不同的用法,括号也是一个程序中非常重要的语法,如果括号发生错误,往往会极大地影响了程序的意义。
大括号中的主要用途
功能 函数
void myfunction(datatype argument){
statements(s)
}
循环
while (boolean expression)
{
statement(s)
}
do
{
statement(s)
}
while (boolean expression);
for (initialisation; termination condition; incrementing expr)
{
statement(s)
}
条件语句
if (boolean expression)
{
statement(s)
}
else if (boolean expression)
{
statement(s)
}
else
{
statement(s)
}
3.3 //(单行注释)
Comments(注释)
注释用于提醒自己或他人程序是如何工作的。它们会被编译器忽略掉,也不会传送给处理器,不会执行,所以它们在Atmega芯片上不占用体积。 注释的唯一作用就是使你自己理解或帮你回忆你的程序是怎么工作的或提醒他人你的程序是如何工作的。编写注释有两种写法:
例子
x = 5; // 这是一条注释斜杠后面本行内的所有东西是注释
/* 这是多行注释-用于注释一段代码
if (gwb == 0){ // 在多行注释内可使用单行注释
x = 3; /* 但不允许使用新的多行注释-这是无效的
}
// 别忘了注释的结尾符号-它们是成对出现的!
*/
小提示
当测试代码的时候,注释掉一段可能有问题的代码是非常有效的方法。这能使这段代码成为注释而保留在程序中,而编译器能忽略它们。这个方法用于寻找问题代码或当编译器提示出错或错误很隐蔽时很有效。
3.4 /* /(多行注释)
Comments(注释)
上面已经讲过了跟单行同类型
例子
x = 5; // 这是一条注释斜杠后面本行内的所有东西是注释
/ 这是多行注释-用于注释一段代码
if (gwb == 0){ // 在多行注释内可使用单行注释
x = 3; /* 但不允许使用新的多行注释-这是无效的
}
// 别忘了注释的结尾符号-它们是成对出现的!
*/
小提示
当测试代码的时候,注释掉一段可能有问题的代码是非常有效的方法。这能使这段代码成为注释而保留在程序中,而编译器能忽略它们。这个方法用于寻找问题代码或当编译器提示出错或错误很隐蔽时很有效。
3.5 #define
#define 是一个很有用的C语法,它允许程序员在程序编译之前给常量命名。在Arduino中,定义的常量不会占用芯片上的任何程序内存空间。在编译时编译器会用事先定义的值来取代这些常量。
然而这样做会产生一些副作用,例如,一个已被定义的常量名已经包含在了其他常量名或者变量名中。在这种情况下,文本将被#defined 定义的数字或文本所取代。
通常情况下,优先考虑使用 const 关键字替代 #define 来定义常量。
Arduino 拥有和 C 相同的语法规范。
语法
#define 常量名 常量值 注意,#是必须的。
例子
#define ledPin 3
//在编译时,编译器将使用数值 3 取代任何用到 ledPin 的地方。
提示
在#define 声明后不能有分号。如果存在分号,编译器会抛出语义不明的错误,甚至关闭页面。
#define ledPin 3; //这是一种错误写法
类似的,在#define声明中包含等号也会产生语义不明的编译错误从而导致关闭页面。
#define ledPin = 3 //这是一种错误写法
不能包含等号只能用空格
3.6 #include
#include用于调用程序以外的库。这使得程序能够访问大量标准C库,也能访问用于arduino的库。 AVR C库(Arduino基于AVR标准语法)语法手册请点击这里。 注意#include和#define一样,不能在结尾加分号,如果你加了分号编译器将会报错。
例子
此例包含了一个库,用于将数据存放在flash空间内而不是ram内。这为动态内存节约了空间,大型表格查表更容易实现。
#include
prog_uint16_t myConstants[] PROGMEM = {0, 21140, 702 , 9128, 0, 25764, 8456,
0,0,0,0,0,0,0,0,29810,8968,29762,29762,4500};
————————————————————————————————————————————
4.1 =(赋值运算符)
= 赋值运算符(单等号) 注意:这个是赋值的=号并不是相比较的号
赋值运算符(单等号)
将等号右边的数值赋值给等号左边的变量
在C语言中,单等号被称为赋值运算符,它与数学上的等号含义不同,赋值运算符告诉单片机,将等号的右边的数值或计算表达式的结果,存储在等号左边的变量中。
例子
int sensVal; //声明一个名为sensVal的整型变量
senVal = analogRead(0); //将模拟引脚0的输入电压存储在SensVal变量中
编程技巧
要确保赋值运算符(=符号)左侧的变量能够储存右边的数值。如果没有大到足以容纳右边的值,存储在变量中的值将会发生错误。
比如你要把一个浮点型小数赋值给一个整数就不对
不要混淆赋值运算符[=](单等号)与比较运算符[](双等号),认为这两个表达式是相等的。
4.2 +(加)
加,减,乘,除
描述
这些运算符返回两个操作数的和,差,乘积,商。这些运算是根据操作数的数据类型来计算的,比如 9和4都是int类型,所以9 / 4 结果是 2.这也就代表如果运算结果比数据类型所能容纳的范围要大的话,就会出现溢出(例如. 1加上一个整数 int类型 32,767 结果变成-32,768)。如果操作数是不同类型的,结果是”更大”的那种数据类型。如果操作数中的其中一个是 float类型或者double类型, 就变成了浮点数运算。
例子
y = y + 3; //这里的Y得是整数型 int 类型才行,如果y是2输出结果为5
y=y+“你好啊”; //这里的y得是字符串类型 str 才行,如果y是“逗B”,输出结果就是“逗B你好啊”
//加可以用来做字符串相加,减的话必须是主内容包含被减掉的内容才可以。
x = x - 7;
i = j * 6;
r = r / 5;
result = value1 + value2;
result = value1 - value2;
result = value1 * value2;
result = value1 / value2;
value1: 任何常量或者变量,value2: 任何常量或者变量
但是要注意,互相做运算的变量或是常得是同一类型,不是的话先转换成同一类型
编程小提示
整型常量的默认值是int类型,所以一些整型常量(定义中)的计算会导致溢出.(比如: 60 * 1000 会得到一个负数结果.那么if(60*1000 > 0) ,if得到的是一个false值。
在选择变量的数据类型时,一定要保证变量类型的范围要足够大,以至于能容纳下你的运算结果。
要知道你的变量在哪个点会”翻身”,两个方向上都得注意.如: (0 - 1) 或 (0 - - 32768)
一些数学上的分数处理,要用浮点数,但其缺点是:占用字节长度大,运算速度慢。
使用类型转换符,例如 (int)myFloat 将一个变量强制转换为int类型。
4.3 -(减)
详见4.2 +(加)
4.4 *(乘)
详见4.2 +(加)
4.5 /(除)
详见4.2 +(加)
4.6 %(取模)
描述
一个整数除以另一个数,其余数称为模。它有助于保持一个变量在一个特定的范围(例如数组的大小)。
语法
结果=被除数%除数
参数
被除数:一个被除的数字
除数:一个数字用于除以其他数
返回
余数(模)
举例
X = 7%5; // X为2,商为1余2
X = 9% 5;// X为4商为1余4
X = 5% 5;// X为0商为1余0
X = 4%5; // X为4除不动就为它本身4了
示例代码
/通过循环计算1到10的模/
int values[10];
int i = 0;
void setup () {
}
void loop()
{
values [i] = analogRead(0); //所以读取的值就是10以内的除以10之后的余数
i =(i + 1)%10; //取模运算
}
提示
模运算符对浮点数不起作用。
————————————————————————————————————————————
5.1 ==(等于)比较是否等于
if(条件判断语句)和 ==、!=、<、>(比较运算符)
复习一下if语句
if 语句与比较运算符一起用于检测某个条件是否达成,如某输入值是否在特定值之上等。if 语句的语法是:
if (someVariable > 50)
{
// 执行某些语句
}
本程序测试 someVariable 变量的值是否大于 50。当大于 50 时,执行一些语句。换句话说,只要 if 后面括号里的结果(称之为测试表达式)为真,则执行大括号中的语句(称之为执行语句块);若为假,则跳过大括号中的语句。 if 语句后的大括号可以省略。若省略大括号,则只有一条语句(以分号结尾)成为执行语句。
if (x > 120) digitalWrite(LEDpin, HIGH);
if (x > 120)
digitalWrite(LEDpin, HIGH);
if (x > 120){ digitalWrite(LEDpin, HIGH); }
if (x > 120){
digitalWrite(LEDpin1, HIGH);
digitalWrite(LEDpin2, HIGH);
} // 以上所有书写方式都正确
在小括号里求值的表达式,需要以下操作符:
比较运算操作符:
x == y(x 等于 y)
x != y(x 不等于 y)
x < y(x 小于 y)
x > y(x 大于 y)
x <= y(x 小于等于 y)
x >= y(x 大于等于 y)
警告
注意使用赋值运算符的情况(如 if (x = 10))。一个“=”表示的是赋值运算符,作用是将 x 的值设为 10(将值 10 放入 x 变量的内存中)。两个“=”表示的是比较运算符(如 if (x == 10)),用于测试 x 和 10 是否相等。后面这个语句只有 x 是 10 时才为真,而前面赋值的那个语句则永远为真。
这是因为 C 语言按以下规则进行运算 if (x=10):10 赋值给 x(只要非 0 的数赋值的语句,其赋值表达式的值永远为真),因此 x 现在值为 10。此时 if 的测试表达式值为 10,该值永远为真,因为非 0 值永远为真。所以,if (x = 10) 将永远为真,这就不是我们运行 if 所期待的结果。另外,x 被赋值为 10,这也不是我们所期待的结果。
if 的另外一种分支条件控制结构是 if…else 形式。
5.2 !=(不等于)
详见5.1 ==(等于)
5.3 <(小于)
详见5.1 ==(等于)
5.4 >(大于)
详见5.1 ==(等于)
5.5 <=(小于等于)
详见5.1 ==(等于)
5.6 >=(大于等于)
详见5.1 ==(等于)
————————————————————————————————————————————
6.1 &&(与)
布尔运算符
这些运算符可以用于if条件句中。不会打&没关系英文输入状态按SHIFT+7
&&(逻辑与)就是同时的意思,小明买了一支笔&&买了一本书
只有两个运算对象为“真”,才为“真”,如:
if (digitalRead(2) == HIGH && digitalRead(3) == HIGH) { // 读取2号针脚和3号针脚的电平
}
如果当两个输入都为高电平,则为“真”。
||(逻辑或)
只要一个运算对象为“真”,就为“真”,如:
if (x > 0 || y > 0) {
//其中x大于0或是y大于0都可执行程序
}
如果x或y是大于0,则为“真”。
!(逻辑非)
如果运算对象为“假”,则为“真”,例如
if (!x) {
// …
}
如果x为“假”,则为真(即如果x等于0)。
警告
千万不要误以为,符号为&(单符号)的位运算符”与”就是布尔运算符的“与”符号为&&(双符号)。他们是完全不同的符号。
同样,不要混淆布尔运算符||(双竖)与位运算符“或”符号为| (单竖)。
位运算符〜(波浪号)看起来与布尔运算符not有很大的差别!(正如程序员说:“惊叹号”或“bang”),但你还是要确定哪一个运算符是你想要的。
举例
if (a >= 10 && a <= 20){} // 如果a的值在10至20之间,则为“真”
6.2 ||(或)
详见6.1 &&(与)
6.3 !(非)
详见6.1 &&(与)
————————————————————————————————————————————
7.1 * 取消引用运算符
指针运算符
& (取地址) 和 * (取地址所指的值)
指针对C语言初学者来说是一个比较复杂的内容,但是编写大部分arduino代码时可以不用涉及到指针。然而,操作某些数据结构时,使用指针能够简化代码,但是指针的操作知识很难在工具书中找到,可以参考C语言相关工具书。
————————————————————————————————————————————
8.1 & (按位与)
按位与(&)
按位操作符对变量进行位级别的计算。它们能解决很多常见的编程问题。下面的材料大多来自这个非常棒的按位运算指导。
说明和语法
下面是所有的运算符的说明和语法。进一步的详细资料,可参考教程。
按位与(&)
位操作符与在C + +中是一个&符,用在两个整型变量之间。按位与运算符对两侧的变量的每一位都进行运算,规则是:如果两个运算元都是1,则结果为1,否则输出0.另一种表达方式:
0 0 1 1 运算元1
0 1 0 1 运算元2
————————
0 0 0 1(运算元1&运算元2)-返回结果
在Arduino中,int类型为16位,所以在两个int表达式之间使用&会进行16个并行按位与计算。代码片段就像这样:
int a = 92; //二进制: 0000000001011100
int b = 101; // 二进制: 0000000001100101
int c = a & b; // 结果: 0000000001000100, 或10进制的68
a和b的16位每位都进行按位与计算,计算结果存在c中,二进制结果是01000100,十进制结果是68.
按位与最常见的作用是从整型变量中选取特定的位,也就是屏蔽。见下方的例子。
按位或(|)
按位或操作符在C++中是|。和&操作符类似,|操作符对两个变量的为一位都进行运算,只是运算规则不同。按位或规则:只要两个位有一个为1则结果为1,否则为0。换句话说:
0 0 1 1 运算元1
0 1 0 1 运算元2
————————
0 1 1 1(运算元1 | 运算元2) - 返回的结果
这里是一个按位或运算在C + +代码片段:
int a = 92; // 二进制: 0000000001011100
int b = 101; //二进制: 0000000001100101
int c = a | b; // 结果: 0000000001111101, 或十进制的125
示例程序
按位与和按位或运算常用于端口的读取-修改-写入。在微控制器中,一个端口是一个8位数字,它用于表示引脚状态。对端口进行写入能同时操作所有引脚。
PORTD是一个内置的常数,是指0,1,2,3,4,5,6,7数字引脚的输出状态。如果某一位为1,着对应管脚为HIGH。(此引脚需要先用pinMode()命令设置为输出)因此如果我们这样写,PORTD=B00110001;则引脚2、3、7状态为HIGH。这里有个小陷阱,我们可能同时更改了引脚0、1的状态,引脚0、1是Arduino串行通信端口,因此我们可能会干扰通信。
我们的算法的程序是:
读取PORT并用按位与清除我们想要控制的引脚
用按位或对PORTD和新的值进行运算
int i; // 计数器
int j;
void setup()
DDRD = DDRD | B11111100; //设置引脚2~7的方向,0、1脚不变(xx|00==xx)
//效果和pinMode(pin,OUTPUT)设置2~7脚为输出一样
serial.begin(9600);
}
void loop () {
for (i=0; i<64; i++){
PORTD = PORTD & B00000011; // 清除2~7位,0、1保持不变(xx & 11 == xx)
j = (i << 2); //将变量左移为·2~7脚,避免0、1脚
PORTD = PORTD | j; //将新状态和原端口状态结合以控制LED脚
Serial.println(PORTD, BIN); // 输出掩盖以便调试
delay(100);
}
}
按位异或(^)
C++中有一个不常见的操作符叫按位异或,也叫做XOR(通常读作”eks-or“)。按位异或操作符用‘^'表示。此操作符和按位或(|)很相似,区别是如果两个位都为1则结果为0:
0 0 1 1 运算元1
0 1 0 1 运算元2
————————
0 1 1 0(运算元1 ^运算元2) - 返回的结果
按位异或的另一种解释是如果两个位值相同则结果为0,否则为1。
下面是一个简单的代码示例:
int x = 12; // 二进制: 1100
int y = 10; // 二进制: 1010
int z = x ^ y; // 二进制: 0110, 或十进制 6
// Blink_Pin_5
//演示“异或”
void setup(){
DDRD = DDRD | B00100000; / /设置数字脚5设置为输出
serial.begin(9600);
}
void loop () {
PORTD = PORTD ^ B00100000; // 反转第5位(数字脚5),其他保持不变
delay(100);
}
8.2 | (按位或)
详见8.1 &(按位与)
8.3 ^ (按位异或)
详见8.1 &(按位与)
8.4 ~ (按位非)
按位取反 (~)
按位取反在C+ +语言中是波浪号~。与&(按位与)和|(按位或)不同,按位取反使用在一个操作数的右侧。按位取反将操作数改变为它的“反面”:0变为1,1变成0。例如:
0 1 operand1
———————
1 0 ~ operand1
int a = 103; // 二进制: 0000000001100111
int b = ~a; // 二进制: 1111111110011000 = -104
你可能会惊讶地看到结果为像-104这样的数字。这是因为整数型变量的最高位,即所谓的符号位。如果最高位是1,这个数字将变为负数。这个正数和负数的编码被称为补。想了解更多信息,请参考Wikipedia文章two’s complement.
顺便说一句,有趣的是,要注意对于任何整数型操作数X,〜X和-X-1是相同的。
有时,对带有符号的整数型操作数进行位操作可以造成一些不必要的意外。
8.5 <<(左移位运算符)
bitshift left (<<), bitshift right (>>)
描述
出自Playground的 The Bitmath Tutorial 在C++语言中有两个移位运算符:左移位运算符(«)和右移运算符(»)。这些操作符可使左运算元中的某些位移动右运算元中指定的位数。
语法
variable « number_of_bits variable » number_of_bits
参数
variable - (byte, int, long) number_of_bits integer ⇐ 32
例子
int a = 5; // 二进制数: 0000000000000101
int b = a << 3; // 二进制数: 0000000000101000, 或十进制数:40
int c = b >> 3; // 二进制数: 0000000000000101, 或者说回到开始时的5
//当你将x左移y位时(x«y),x中最左边的y位会逐个逐个的丢失:
int a = 5; // 二进制: 0000000000000101
int b = a << 14; // 二进制: 0100000000000000 - 101中最左边的1被丢弃
如果你确定位移不会引起数据溢出,你可以简单的把左移运算当做对左运算元进行2的右运算元次方的操作。例如,要产生2的次方,可使用下面的方式:
1 << 0 == 1
1 << 1 == 2
1 << 2 == 4
1 << 3 == 8
…
1 << 8 == 256
1 << 9 == 512
10 << 1 == 1024
…
当你将x右移y位(x»y),如果x最高位是1,位移结果将取决于x的数据类型。如果x是int类型,最高位为符号位,确定是否x是负数或不是,正如我们上面的讨论。如果x类型为int,则最高位是符号位,正如我们以前讨论过,符号位表示x是正还是负。在这种情况下,由于深奥的历史原因,符号位被复制到较低位:
X = -16; //二进制:1111111111110000
Y = X >> 3 //二进制:1111111111111110
这种结果,被称为符号扩展,往往不是你想要的行为。你可能希望左边被移入的数是0。右移操作对无符号整型来说会有不同结果,你可以通过数据强制转换改变从左边移入的数据:
X = -16; //二进制:1111111111110000
int y = (unsigned int)x >> 3; // 二进制: 0001111111111110
如果你能小心的避免符号扩展问题,你可以将右移操作当做对数据除2运算。例如:
INT = 1000;
Y = X >> 3; 8 1000 //1000整除8,使y=125
8.6 >> (右移位运算符)
详见 8.5 <<(左移位运算符)
————————————————————————————————————————————
9.1 ++ (递增)
++ (递增) / – (递减)
描述
递增或递减一个变量
语法
x++; //x自增1返回x的旧值
++x; // x自增1返回x的新值
x–; // x自减1返回x的旧值
–x; //x自减1返回x的新值
参数
x: int或long(可能是unsigned)
返回
变量进行自增/自减操作后的原值或新值。
例子
x = 2;
y = ++x; // 现在x=3,y=3
y = x–; // 现在x=2,y还是3
9.2 – (递减)
详见 9.1 ++ (递增)
9.3 += (复合加)
+= , -= , *= , /=
描述
执行常量或变量与另一个变量的数学运算。+= 等运算符是以下扩展语法的速记。
语法
X += Y; //相当于表达式X = X + Y;
X -= Y; //相当于表达式X = X - Y;
X *= Y; //相当于表达式X = X * Y;
X /= Y; //相当于表达式X = X / Y;
参数
X:任何变量类型
Y:任何变量类型或常数
例子
x = 2;
x += 4; // x 现在等于6
x -= 3; // x 现在等于3
x *= 10; // x 现在等于30
x /= 2; // x 现在等于15
9.4 -= (复合减)
详见 9.3 += (复合加)
9.5 *= (复合乘)
详见 9.3 += (复合加)
9.6 /= (复合除)
详见 9.3 += (复合加)
9.7 &= (复合运算按位与)
描述
复合运算按位与运算符(&=)经常被用来将一个变量和常量进行运算使变量某些位变为0。这通常被称为“清算”或“复位”位编程指南。
语法
x &= y; // 等价于 x = x & y;
参数
x:char,int或long类型变量
Y:char,int或long类型常量
例如
首先,回顾一下按位与(&)运算符
0 0 1 1 运算元1
0 1 0 1 运算元2
————————
0 0 0 1(运算元1&运算元2) - 返回的结果
任何位与0进行按位与操作后被清零,如果myBite是变量
myByte&B00000000 = 0;
因此,任何位与1进行“按位与运算”后保持不变
myByte B11111111 = myByte;
注意:因为我们用位操作符来操作位,所以使用二进制的变量会很方便。如果这些数值是其他值将会得到同样结果,只是不容易理解。同样,B00000000是为了标示清楚,0在任何进制中都是0(恩。。有些哲学的味道) 因此 - 清除(置零)变量的任意位0和1,而保持其余的位不变,可与常量B11111100进行复合运算按位与(&=)
1 0 1 0 1 0 1 0变量
1 1 1 1 1 1 0 0 mask
——————————
1 0 1 0 1 0 0 0
变量不变 位清零
将变量替换为x可得到同样结果
X X X X X X X X变量
1 1 1 1 1 1 0 0 mask
——————————
X X X X X X 0 0
变量不变 位清零
同理
myByte = 10101010;
myByte&= B1111100 == B10101000;
9.8 |= (复合运算按位或)
描述
复合按位或操作符(| =)经常用于变量和常量“设置”(设置为1),尤其是变量中的某一位。
语法
x |= y; //等价于 x = x | y;
参数
x: char,int或long类型
y:整数,int或long类型
例如
首先,回顾一下OR(|)运算符
0 0 1 1 运算元1
0 1 0 1 运算元2
————————
0 1 1 1(运算元1 | 运算元2) - 返回的结果
如果变量myByte中某一位与0经过按位或运算后不变。
myByte | B00000000 = myByte;
与1经过或运算的位将变为1.
myByte | B11111111 B11111111;
因此 - 设置变量的某些位为0和1,而变量的其他位不变,可与常量B00000011进行按位与运算(| =)
1 0 1 0 1 0 1 0变量
0 0 0 0 0 0 1 1
————————
1 0 1 0 1 0 1 1
变量保持不变 位设置
接下来的操作相同,只是将变量用x代替
X X X X X X X X变量
0 0 0 0 0 0 1 1 mask
——————————
X X X X X X 1 1
变量保持不变 位设置
同上:
myByte = B10101010;
myByte | = B00000011 == B10101011;