从零开始学习嵌入式P3----C语言表达式、字符的输入输出

从零开始学习嵌入式P3----C语言表达式、字符的输入输出 从零开始学习嵌入式P3----C语言表达式、字符的输入输出书接上文今天从变量的定义和初始化开始学习表达式、类型转换、常用运算符及运算符优先级最后介绍单个字符的输入输出函数getchar和putchar。默认在 Linux GCC 环境下运行。本篇目标学完这一篇我们应该能够按规则定义、初始化和修改变量理解表达式的值与类型区分隐式类型转换和显式类型转换正确使用算术、赋值、逗号及sizeof运算符看懂运算符优先级并主动使用括号消除歧义使用getchar和putchar完成单个字符的输入与输出。一、变量的定义与使用变量是在程序运行期间值可以改变的数据对象。变量在内存中拥有对应的存储空间我们通过变量名访问这块空间并按照变量声明的类型解释其中的数据。1. 先定义再使用变量定义的基本格式如下数据类型 变量名;例如shortnum;unsignedshortnum;intcount;unsignedintid;longtotal;unsignedlongaddress;floattemperature;doublevoltage;charcommand;unsignedcharregister_value;应为布尔不是C语言自带的关键字使用bool时需要使用stdbool.h库调用#includestdbool.hbool really;2. 变量名的规则C 语言标识符只能由字母、数字和下划线组成并且不能以数字开头。inta1_;// 合法int_a1;// 合法但不建议int1a;// 错误不能以数字开头intreturn;// 错误不能与 C 语言关键字重名虽然某些库函数名在特定作用域中可能可以被重新声明或遮蔽但这样做会让代码难以理解也可能引发编译问题因此不要把变量命名为printf、getchar等库函数名。变量名最好做到见名知意。常见命名风格包括intMaxScore;// 驼峰命名intint8_t;// 下划线命名在跨平台开发中很常见不论选什么保持一致和能看懂最重要。3. 初始化和赋值定义变量时为它设置初始值称为初始化intcount0;floattemperature25.0f;charcommandA;而变量定义完成后再使用赋值运算符修改它的值则称为赋值intcountfloattemperaturecharcommand//定义变量count0;temperature25.0f;commandA;这两种写法看起来相似但发生的时机不同inta10;// 定义并初始化a20;// 给已经存在的变量赋值二、什么是表达式表达式是由运算符连接常量、变量或其他表达式后形成的式子。例如1020a*b countcount1一个表达式通常同时具有两个重要属性类型决定结果按哪种数据类型解释值执行表达式后得到的结果。由于C语言中具有多种数据存储类型表达式中容易出现多种类型的数据。这会改变式子的输出结果。例如10/4// 输出类型是 int值是 210.0/4// 输出类型是 double值是 2.5同样是除法因为参与运算的数据类型不同结果也不同。这也是 C 语言初学阶段非常容易出错的地方。三、混合类型运算与类型转换当运算符两侧的数据类型不一致时编译器通常需要先把它们转换为适合共同运算的类型。1. 常见的隐式转换当表达式中出现像上文中的不同数据类型经行计算的情况2.5/2;C语言就会默认采用隐形转换机制经行转换。隐式类型转换由编译器自动完成常见情况包括当char和short参与整数运算时通常先将他们转换为int当运算两侧出现浮点类型时整数会转换为对应的浮点类型当float与double混合运算时float会转换为double同等级的有符号和无符号整数混合运算时转换为无符号类型实际的整数转换还会受到类型等级和目标平台表示范围的影响尤其是有符号数和无符号数混合时要格外谨慎数据格式。2. 显式类型转换有时也会人为经行数据类型的转换。程序员主动写出的类型转换也称强制类型转换基本格式为(目标类型)表达式例如intcount10;doubleresult(double)count/4;这里先把count转换成double再参与除法所以结果为2.5。类型转换只影响表达式求值时采用的类型不会自动改变原变量的声明类型doublevalue5.27;intint_part(int)value;// integer_part 为 5但value 仍然是 5.27四、不同数据类型的赋值看图片中的核心代码#includestdio.hintmain(void){doubled0;d10/4;printf(d %f\n,d);return0;}很多人会认为d的输出结果是double类型值为2.5。实际输出却是d 2.000000原因在于赋值之前会先计算等号右侧的表达式。10和4都是int类型因此执行的是整数除法小数部分直接被舍弃先得到整数2之后这个2才被转换为double并赋给d。10 / 4 - 整数除法得到 2 - 转换为 double - 2.000000如果想输出double结果只要让除法中的任意一个操作数在运算前变成浮点类型即可d10.0/4;d10/4.0;d(double)10/4;这三种写法的结果都是2.5。五、算术运算符C 语言常见的算术运算符如下运算符含义示例加法a b-减法a - b*乘法a * b/除法a / b%取余a % b自增a、a--自减a--、--a1. 除法与取余两个整数相除时仍然得到整数会直接省略小数点后面的部分inta10/4;// 2intb-10/4;// -2%用来计算整数除法的余数不能对浮点数使用inta10%4;// 22. 前置与后置自增和--可以粗略理解为a;//a a 1a--;//a a - 1和--只能作用于可修改的变量不能作用于常量。且a 与 a与不同的含义。inti5;intai;// 先使用 i 的旧值完成当前表达式再令 i 加 1a 5i 6intj5;intbj;// 先令 j 加 1再使用新值完成表达式j 6b 6如果只是单独写一条自增语句i和i都会让i增加 1i;j;六、赋值运算符常用赋值运算符包括 - //a - 1意为aa-1 * //a * 1意为aa*1 / //a / 1意为aa/1 % //a % 1意为aa%1复合赋值可以让代码更紧凑。1. 左值与右值简单理解左值表示一个可以被定位的对象可修改变量能够放在赋值号左边右值用于提供参与计算或赋值的值变量、常量和表达式都可以出现在右侧。inta0;intb10;a100;// 正确ab;// 正确ab100;// 正确// 100 a; // 错误常量不能作为赋值目标// a b 0; // 错误表达式不能作为赋值目标2. 混合类型赋值不同类型之间赋值时可能发生转换整数赋给浮点变量整数转换为浮点类型浮点数赋给整数变量小数部分被舍弃小范围整数赋给大范围整数通常进行符号扩展或补 0大范围整数赋给小范围整数可能丢失高位数据有符号与无符号整数互相赋值按目标类型规则转换结果可能与直觉不同。例如inta3.99;// a 得到 3doubleb10;// b 得到 10.0unsignedcharc355;// 可能得到 99在常见 8 位unsigned char平台中355 超出 0-255 的范围转换结果相当于对 256 取模因此得到 99。对于不能由目标类型表示的有符号整数转换具体结果可能由编译器实现决定。七、符号位代码#includestdio.hintmain(void){unsignedinta5;if(a-100){printf(Yes\n);}else{printf(No\n);}return0;}从数学上看5 - 10等于-5条件应该为假。但这段程序通常会输出Yes原因是a为unsigned int。表达式a - 10求值时整数常量10会转换为unsigned int整个减法执行无符号运算。无符号整数不能表示负数。当结果越过 0 时会按照模 2ⁿ 的规则回绕。假设unsigned int为 32 位5U - 10U 4294967291U这个值当然大于 0所以程序进入if分支。如果本来就需要表示负数则应从一开始选择有符号类型inta5;if(a-100){printf(Yes\n);}else{printf(No\n);}这个例子说明类型不仅决定变量能保存什么也会直接改变表达式的计算方式。八、逗号运算符与sizeof1. 逗号运算符逗号表达式从左到右依次计算各个子表达式并把最后一个子表达式的值作为整体结果inta1;intb2;intresult(a10,b20);//以最后一个式子为整个表达式的结果// result 的值为 22逗号运算符的优先级很低所以通常需要加括号。日常代码不宜用逗号表达式会降低可读性。2.sizeof运算符sizeof用来获取类型或对象占用的存储空间结果以字节为单位#includestdio.hintmain(void){inta0;printf(sizeof(int) %zu\n,sizeof(int));printf(sizeof(a) %zu\n,sizeof(a));return0;}对类型使用sizeof时必须加括号对变量或普通表达式使用时括号可以省略但为了统一和易读通常保留。sizeof(int);// 正确sizeofa;// 正确sizeof(a);// 正确九、运算符优先级当一个表达式中出现多个运算符时C 语言会按照优先级和结合性决定求值结构。常见运算符从高到低的大致顺序优先级类别常见运算符高括号和后缀()、后置、后置--单目前置、前置--、!、~、sizeof、强制类型转换乘除取余*、/、%加减、-移位、关系与相等、、、、、!按位运算、^、逻辑运算、条件运算?:赋值、、-、*、/、%低逗号,不建议仅靠背诵优先级来编写复杂表达式。存在歧义时主动使用括号既能确保计算顺序也能让读者快速看懂意图intresult(ab)*c;十、常用输入输出函数概览C 语言的标准输入输出函数声明在stdio.h中#includestdio.h按处理对象可以粗略分为类别常见函数用途单个字符输入输出getchar、putchar一次读取或输出一个字符格式化输入输出scanf、printf按指定格式处理数据字符串输入输出fgets、puts读取或输出字符串十一、putchar输出字符putchar的作用是在标准输出中写出一个字符基本用法为putchar(字符或字符编码表达式);例如#includestdio.hintmain(void){putchar(A);putchar(\n);return0;}putchar每次只输出一个字符。调用成功时它返回所输出字符对应的无符号字符值并以int类型表示失败时返回EOF。虽然putchar一次只能输出一个字符但可以连续调用来输出一段文字#includestdio.hintmain(void){putchar(h);putchar(e);putchar(l);putchar(l);putchar(o);putchar(\n);return0;}也可以利用字符数组和循环实现不过数组与循环将在后续内容中进一步学习。十二、getchar输入字符getchar从标准输入读取一个字符intchgetchar();要注意它的返回类型是int而不是char。在普通终端中标准输入通常按行缓冲。用户输入字符后往往还需要按下回车程序才能读取到这一行数据。小写字母转换为大写字母利用 ASCII 中大小写字母编码之间的固定差值可以把输入的小写字母转为大写#includestdio.hintmain(void){intchgetchar();putchar(ch);putchar(\n);return0;}小结这一篇学习了变量的定义、初始化与赋值理解了表达式具有类型和值并重点分析了整数除法、显式类型转换以及有符号与无符号混合运算。随后认识了算术、赋值、逗号和sizeof运算符最后使用getchar和putchar完成了单个字符的输入与输出。嵌入式程序经常直接处理寄存器、计数值和原始字节类型选择或转换顺序稍有不同表达式的结果就可能完全改变。看到一条表达式时不仅要计算它的数学结果还要先确认每个操作数是什么类型。因为篇幅原因本篇内容只讲到字符的输入输出其他输入输出只能留到下一篇讲。