【跟着陈七一起学C语言】今天总结:C语言最基础入门
友情链接:专栏地址
知识总结顺序参考C Primer Plus(第六版)和谭浩强老师的C程序设计(第五版)等,内容以书中为标准,同时参考其它各类书籍以及优质文章,以至减少知识点上的错误,同时方便本人的基础复习,也希望能帮助到大家
最好的好人,都是犯过错误的过来人;一个人往往因为有一点小小的缺点,将来会变得更好。如有错漏之处,敬请指正,有更好的方法,也希望不吝提出。最好的生活方式就是和努力的大家,一起奔跑在路上
文章目录
- 【思维导图】
- 🚀第一个简单程序示例相关知识点总结
- 🚀第二个简单程序示例相关知识点总结
-
- ⛳六、字符串和格式化输入输出
-
- 🎈(一)C语言字符串
- 🎈(二)printf()和scanf()
-
- 1.用printf()函数输出数据
-
- (1)一般格式
- (2)printf()的转换说明修饰符
- (3)转换说明的意义
-
- ①转换不匹配
- ②printf()的返回值
- ③打印较长的字符串
- 2.用scanf()函数输入数据
-
- (1)scanf()输入的原理剖析
- (2)地址列表
- (3)格式字符串
- (4)scanf()的返回值
- 🎈(三)字符输入输出函数
-
- 1.putchar()
- 2.getchar
- 3.ctype.h系列的字符函数
- 🚀第三个简单程序示例相关知识点总结
-
- ⛳七、运算符、表达式和语句
-
- 🎈(一)运算符
-
- 1.赋值运算符"="
- 2.算术运算符
- 3.自增自减运算符
- 4.关系运算符
- 5.逻辑运算符
-
- (1)逻辑与 &&
- (2)逻辑或
- (3)逻辑非 !
- 6.位运算符
-
- (1)位与
- (2)位或
- (3)位非
- (4)左移
- (5)右移
- 7.逗号运算符
- 8.三目运算符
- 9.运算符的优先级
- 10.sizeof运算符和size_t类型
- 11.iso646.h头文件
- 🎈(二)表达式
- 🎈(三)语句
- 🎈(四)类型转换
-
- 1.隐式类型转换
- 2.强制类型转换
【思维导图】
我的总结方式一般是先搞一段程序过来,然后再逐行的学习相关的知识点
🚀第一个简单程序示例相关知识点总结
#include <stdio.h>int main(void) /* 一个简单的C程序 */
{int num; /* 定义一个名为num的变量 */num = 1; /* 为num赋一个值 */printf("I am a simple "); /* 使用printf()函数 */printf("computer.\\n");printf("My favorite number is %d because it is first.\\n",num);return 0;
}
输出结果:
I am a simple computer.
My favorite number is 1 because it is first.
某些窗口环境会在单独的窗口运行 程序,然后在程序运行结束后自动关闭窗口。如果遇到这种情况,可以在程 序中添加额外的代码,让窗口等待用户按下一个键后才关闭。一种方法是, 在程序的return语句前添加一行代码: getchar();
⛳一、头文件
程序的第一行:
#include <stdio.h>
#include指令和头文件
程序如戏,程序中有很多元素(printf等), 都是一个个演员,但是他们之间都互不认识, 但是却要一起合作, 强行编译,就会导致错误!得预先介绍他们,知道他们各自的名号和用法
#include <stdio.h>
此行作用相当于把stdio.h文件中的所有内容都输入该行所在的位置。实际上,这是一种“拷贝-粘贴”的操作。#include
这行代码是一条C预处理器指令(preprocessor directive)。通常,C编译器在编译前会对源代码做一些准备工作,即预处理 (preprocessing)。所有的预处理器命令都是以#开头,#include
就是包含一个源代码文件- 用< >括起来的是一个文件,文件中含有这些基本的相关元素介绍说明,所有的C编译器软件包都提供stdio.h文件。该文件中包含了供编译器使用的输入和输出函数(如, printf())信息。该文件名的含义是标准输入/输出头文件。通常,在C程序顶部的信息集合被称为头文件(header)
- 既然是头文件,则要求放在文件的最前面,如果放到后面,当 #include 之前出现了相关的函数,就会有问题
拓展:
#include <stdio.h>
< >表示,从编译器默认的路径中去找文件 stdio.h 这个默认路径,取决于编译器。不同平台下不同编译器的路径都不相同。 这个默认路径下,已经包含了 c 标准库所需要的所有头文件,
使用C标准库的头文件使用该方式
#include “mytest.h”
" "表示从当前项目目录下寻找文件mytest.h,如果在当前目录下找不到,再从编译器默认的路径中查找。
使用用户自定义的头文件使用该方式
⛳二、main()函数
int main(void)
{
return 0;
}
int main(void)
人生,就是一个程序。在不同的阶段,做不同的事。 每件事,就是一个函数。 每个人的人生都不相同, 以不同的顺序,做不同的事(相当于调用不同的“函数”), 但是都是从“出生”开始,每个软件也各不相同,包含不同的函数,但是都是从 main 函数开始
C程序包含一个或多个函数,它们是C程序的基本模块。其中有一个名为main()的函数。
- 圆括号表明main()是一个函数名。
- int表明main()函数返回一个整数。通过函数内部的return返回,一般程序成功结束,main函数返回0程序有异常,则会返回一个大于 0 的整数。
- void表明main()不带任何参数。通常,函数名后面的圆括号中包含一些传入函数的信息。该例中没有传递任何信息。因此,圆括号内是单词void,这些内容我们以后总结函数的时候再总结。现在,只需记住int和void是标准ANSI C定义main()的一部分
- main 函数是程序的唯一入口。 也就是说,程序运行时,首先从 main 函数开始执行。
- 一个程序,必须要有一个 main 函数,而且也只能有一个 main 函数。
- 有返回值的C函数要有return语句。该语句以return关键字开始,后面是待返回的值,并以分号结尾。如果遗漏 main()函数中的 return 语句,程序在运行至最外面的右花括号 (})时会返回0。因此,可以省略main()函数末尾的return语句。但是,不要在其他有返回值的函数中漏掉它。因此,强烈建议读者养成在 main()函数中 保留 return 语句的好习惯。
拓展:
1.有时仍然能看见一些旧式的C代码
main()
C90标准勉强接受这种形式,但是C99和C11标准不允许这样写。因此, 即使使用的编译器允许,也不要这样写。
void main()
一些编译器允许这样写,但是所有的标准都未认可这种写法。因此,编译器不必接受这种形式,而且许多编译器都不能这样写。需要强调的是,只要坚持使用标准形式,把程序从一个编译器移至另一个编译器时就不会出什么问题。
2.之后会总结把信息从main()函数传回操作系统的另一种方式int main(int argc , char* argv) { }
⛳三、注释
/* 一个简单的C程序 */
注释
顾名思义就是对自己写的代码简单的解释一下,写注释能让他人(包括自己)更容易明白你所写的程序
- 被/* */两个符号括起来的部分是程序的注释,可将注释放在任意的地方,甚至是与要解释的内容在同一行,较长的注释可单独 放一行或多行。
- 在/* 和 /之间的内容都会被编译器忽略,缺少任意一半都是无效注释,而且一个/ * 只会和之后出现的第一个/配对,中间内容都是无效的,即使还有一个/ *
- C99新增了另一种风格的注释,普遍用于C++和Java。这种新风格使用// 符号创建注释,仅限于单行。
个人习惯:
- 一般注释:重要变量名的作用(用来表示什么),比较复杂的算法思想,程序中的关键步骤
- 在行尾注释 使用//注释
- 在代码的上一行使用//注释
- 多方内容的注释,使用 /* */
⛳四、花括号、函数体和块
{
…
}
花括号把main()函数括起来
- 花括号括起来的区域就叫做块,函数也要使用花括号扩起来,这个区域叫函数体
- 一般而言,所有的C函数都使用花括号标记函数体的开始和结束。这是规定,不能省略
⛳五、常量和变量
🎈(一)常量
num = 1;
printf("I am a simple ");
在程序运行中,其值不能被改变的量称为常量,如示例中的1和"I am a simple "都是常量
常用的常量有以下几类:
1.整型常量
如1000,12345,0,-345等都是整型常量
2.实型常量
(1)十进制小数形式
由数字和小数点组成,如123.456,0.345,-56.79,0.0,12.0等
(2)指数形式
如12.34e3(代表12.34∗10312.34*10^312.34∗103),-346.87e(代表−346.87∗10−25-346.87*10^{-25}−346.87∗10−25),0.145E-25(代表0.145∗10−250.145*10^{-25}0.145∗10−25)
由于在计算机输入或输出时无法表示上角或下角,故规定以字母e或E代表以10为底的指数。但应注意:e或E之前必须要有数字,且e或E后面必须为整数,如不能写成e4,12e2.5。
3.字符常量
(1)普通字符
用单撇号括起来的一个字符,如:‘a’ ,‘Z’,‘3’,‘?’,‘#’。不能写成’ab’或’12’。请注意:单撇号只是界限符,字符常量只能是一个字符,不包括单撇号。'a’和’A’是不同的字符常量。字符常量存储在计算机存储单元中时,并不是存储字符(如a,z,#等)本身,而是以其代码(一般采用ASCII代码)存储的,例如字符’a’的ASCII代码是97,因此,在存储单元中存放的是97(以二进制形式存放)。ASCII字符与代码对照表见附录A0。
(2)转义字符
除了以上形式的字符常量外,C语言还允许用一种特殊形式的字符常量,就是以字符" \\ "开头的字符序列。意思是将“\\”后面的字符转换成另外的意义。如“\\n”中的“n”不代表字母n而作为“换行”符。
再例如,'\\t’代表将输出的位置跳到下一个Tab位置(制表位置),一个Tab位置为8列。这是一种在屏幕上无法显示的“控制字符”,在程序中也无法用一个一般形式的字符来表示,只能采用这样的特殊形式来表示。
4.字符串常量
如"I am a simple ","computer.\\n"等,用双撇号把若干个字符括起来,字符串常量是双撇号中的全部字符(但不包括双撇号本身)。注意不能错写成’CHINA’ ,’ boy’ ,‘123’。单撇号内只能包含一个字符,双撇号内可以包含一个字符串
5.符号常量
用#define
指令,指定一个符号名称代表一个常量,如:
#define PI 3.1416
经过以上的指定后,本文件中从此行开始所有的PI都代表3.1416。在对程序进行编译前,预处理器先对PI进行处理,把所有PI全部置换为3.1416。这种用一个符号名代表一个常量的,称为符号常量。在预编译后,符号常量已全部变成字面常量(3.1416)。使用符号常量有以下好处。
- 含义清楚,看程序时从PI就可大致知道它代表圆周率
- 在需要改变程序中多处用到同一个常量时,能做到“一改全改”
🎈(二)变量
int num;
num = 1;
程序在运行时,需要保存很多内容常常变化的数据。 比如,射击类游戏中不断变化的“分数”。一个程序运行时,大量数据存储在“变量”中。 数据在大量变量之间“计算”、“交换”。 变量是处理数据的基本实体
变量,是内存中的一块存储空间,即一小块内存,代表一个有名字的、具有特定属性的一个存储单元。在程序整个运行期间,值随时可以发生变化,如示例中的num
-
int num;
这行代码表示变量必须先定义,后使用,在定义时指定该变量的名字和类型,在该例中,定义完成了两件事。其一,在函数中有一个名为 num的变量(variable)。其二,int表明num是一个整数(即,没有小数点或 小数部分的数)。int是一种数据类型。
-
num=1;
这行代码表示给变量赋值,1即是变量num的值 -
int是C语言的一个关键字(keyword),表示一种基本的C语言数据类型。
-
num是一个标识符(identifier),也就一个变量、函数或其他 实体的名称
下面就以这个顺序来详细总结
拓展:
以前的C语言,还要求把变量声明在块的顶部,其他语句不能在任何声明的前面。(C Primer Plus(第六版)书中将int num;称为变量的声明)
int main() //旧规则 {int doors;int dogs;doors = 5;dogs = 3;// 其他语句 }
C99和C11遵循C++的惯例,可以把声明放在块中的任何位置。尽管如此,首次使用变量之前一定要先声明它。因此,如果编译器支持这一新特性,可以这样编写上面的代码:
int main() // 目前的C规则 {// 一些语句int doors;doors = 5; // 第1次使用doors// 其他语句int dogs;dogs = 3; // 第1次使用dogs// 其他语句 }
1.变量的定义
int num;
定义形式 1:
int x;
int y;
定义形式 2:(不推荐)
int x, y;
定义形式 3:(定义的时候,设置一个初始值)
int x = 100;
代码详解:
2.变量的赋值
num = 1;
赋值是C语言的基本操作 之一。该行代码的意思是“把值1赋给变量num”。
- 在执行int num;声明时,编译器在计算机内存中为变量num预留了空间,然后在执行这行赋值表达式语句时,把值储存在之前预留的位置。
- 可以给num赋不同的值,这就是num之所以被称为变量(variable)的原因。
- 注意,该赋值表达式语句从右侧把值 赋到左侧。另外,该语句以分号结尾,
3.数据类型
通过上面的总结,你可能会想变量和数据类型有什么关系呢?
变量,是一个盒子,盒子里保存了“数据” ,数据又分成很多“类型”(数据类型)
- => 变量的类型,就是变量中数据的类型。
- => 变量在定义(创建)时,必须先指定它的类型。 【相当于:制作一个盒子时,必须先确认这个盒子是用来装什么的】
- => 1 个变量只有 1 个类型,而且不能改成其他类
(1)整数类型
int 类型
和数学的概念一样,在C语言中,整数是没有小数部分的数。例如, 2、−23和2456都是整数。而3.14、0.22和2.000都不是整数。
- 在内存中占 4 个字节
- 表示范围:- (2 的 31 次方) ~ 2 的 31 次方-1
长整形 long
long 也就是 long int 可用来存储更大的整数
- 在 32 位系统上,占 4 个字节,和 int 相同
- 在 64 位系统上,占 8 个字节【正负9百亿亿左右】
长长整形 long long
用来存储整数。 在内存中占 8 字节。 很少使用,仅用于特殊的计算。
短整形 short
用来存储整数。 在内存中占 2 字节。 用于存储小范围的整数 表示范围:- (2 的 15 次方) ~ 2 的 15 次方-1 【正负 3 万多】
(2)字符类型
char
单个字符:
‘0’ ‘1’ ‘2’ ‘3’ … ‘9’
‘a’ ‘b’ ‘c’ ‘d’ … ‘z’
‘A’ ‘B’ ‘C’ ‘D’ … ‘Z’
‘,’ ‘-’ ‘!’ ‘#’ …
单个字符常量, 要求用‘’括起
占一个字节。 char name = ‘a’
字符的实际表示: 所有的字符,使用对应的 ASCII 值来存储。 因为计算机中只能存储 0 和 1 的组合, ASCII 码,使用 1 个字节(包含 8 个二进制位,即 8 个 0 和 1 的组合) 比如:’A’ , 用 0100 0001 来表示, 就是 65 ‘B’, 用 0100 0010 来表示, 就是 66
char类型的另一种用法
用来表示小范围的整数(-128 ~ 127) 不过现在开发时,小范围的整数,也常常直接用 int 类型
//实例:
int x = 0;
x = 'a' + 1; // 97 + 1
注意:1 和 ‘1’的区别,
int x = 1;
char y = '1'; //49
(3)浮点型
需要精确计算的数学、工程应用,用整数类型不合适。
①float单精度浮点型
用来存储带小数部分的数据。 在内存中占用 4 个字节
表示范围: −3.4∗1038~+3.4∗1038-3.4*10^{38}~+3.4*10^{38}−3.4∗1038~+3.4∗1038 (不需记忆) 精度:最长 7 位有效数字(是指 7 位 10 进制位)
float y = 1.123456789; //精度只能取值到 1.123457, 在第7位是四舍五入后的值
②double双精度浮点型
用来存储带小数部分的数据。 在内存中占用8 个字节
表示范围:−1.7∗103081.7∗10308-1.7*10^{308}~1.7*10^{308}−1.7∗10308 1.7∗10308(不需记忆) 精度:最长 16 位有效数字(是指 16 位 10 进制位)
double y = 1.12345678901;
注意:
- 带小数的常量默认都是double类型 ,3.14是double类型 ,例如3.14f 强制指定是 float
- 可以用”科学计数法”表示浮点类型的常量 1.75E5 或 1.75e5,就是 1.75乘以 10 的 5 次方
- 1 是 int 类型的常量 1.0 是 double类型的常量
- C语言的第3种浮点类型是long double,以满足比double类型更高的精度 要求。不过,C只保证long double类型至少与double类型的精度相同。
(4)_Bool型
C99标准添加了_ Bool类型,用于表示布尔值,即逻辑值true和false。因为C语言用值1表示true,值0表示false,所以_Bool类型实际上也是一种整数 类型。但原则上它仅占用1位存储空间,因为对0和1而言,1位的存储空间足 够了。
(5)可移植类型:stdint.h和inttypes.h
C 语言提供了许多有用的整数类型。但是,某些类型名在不同系统中的 功能不一样。C99 新增了两个头文件stdint.h和inttypes.h,以确保C语言的类 型在各系统中的功能相同。
①C语言为现有类型创建了更多类型名。这些新的类型名定义在stdint.h头文件中。例如:
- int32_t表示32位的有符号整数类型。在使用32位int的系统中,头文件会把int32_t作为int的别名。
- 不同的系统也可以定义相同的类型 。例如,int为16位、long为32位的系统会把int32_t作为long的别名。然后,使用int32_t类型编写程序,并包含stdint.h头文件时,编译器会把int或long替换成与当前系统匹配的类型。
上面讨论的类型别名是精确宽度整数类型(exact-width integer type)的示例。int32_t表示整数类型的宽度正好是32位。但是,计算机的底层系统可能不支持。因此,精确宽度整数类型是可选项。如果系统不支持精确宽度整数类型怎么办?
②C99和C11提供了第2类别名集合。一些类型名保证所表示的类型一定是至少有指定宽度的最小整数类型。这组类型集合被称为最小宽度类型(minimum width type)。例如:
- int_least8_t是可容纳8位有符号整数值的类型中宽度最小的类型的一个别名。
- 如果某系统的最小整数类型是16位,可能不会定义int8_t类型。尽管如此,该系统仍可使用int_least8_t类型,但可能把该类型实现为16位的整数类型。
拓展:
当然,一些程序员更关心速度而非空间。为此,C99和C11定义了一组可使计算达到最快的类型集合。这组类型集合被称为最快最小宽度类型 (fastst minimum width type)。例如,int_fast8_t被定义为系统中对8位有符号值而言运算最快的整数类型的别名。
另外,有些程序员需要系统的最大整数类型。为此,C99定义了最大的有符号整数类型intmax_t,可储存任何有效的有符号整数值。类似地, unitmax_t表示最大的无符号整数类型。这些类型有可能比long long和unsigned long类型更大,
C99 和 C11 不仅提供可移植的类型名,还提供相应的输入和输出。例如,printf()打印特定类型时要求与相应的转换说明匹配。如果要打印int32_t 类型的值,有些定义使用%d,而有些定义使用%ld,怎么办?C 标准针对这一情况,提供了一些字符串宏(来显示可移植类型,例如: inttypes.h头文件中定义了PRId32字符串宏,代表打印32位有符号值的合 适转换说明(如d或l)
(6)signed和unsigned修饰符
在实际应用中,有的数据的范围常常只有正值(如学号、年龄、库存量、存款额等)。为了充分利用变量的值的范围,可以将变量定义为“无符号”类型。可以在类型符号前面加上修饰符unsigned,表示指定该变量是“无符号整数”类型。如果加上修饰符signed,则是“有符号类型”。
整数类型和字符类型都可以用signed和unsigned修饰符,例如(方括号中的内容是可选的):
[signed] int //有符号基本整型
unsigned int //无符号基本整型
[signed] short [int] //有符号短整型
unsigned short[int] //无符号短整型
[signed] long [int] //有符号长整型
unsigned long [int] //无符号长整型
[signed] long long [int] //有符号双长整型 (C99增加)
unsigned long long [int] //无符号双长整型 (C99增加)[signed] char //有符号字符型
[unsigned] char //无符号字符型
4.关键字和保留标识符
关键字是C语言的词汇。它们对C而言比较特殊,不能用它们作为标识符(如,变量名)。许多关键字用于指定不同的类型,如 int。还有一些关 键字(如,if)用于控制程序中语句的执行顺序
粗体表示的是C90标准新增的关键字,斜体表示的C99标准新增的 关键字,粗斜体表示的是C11标准新增的关键字。
如果使用关键字不当(如,用关键字作为变量名),编译器会将其视为语法错误。还有一些保留标识符(reserved identifier),C语言已经指定了它们的用途或保留它们的使用权,如果你使用这些标识符来表示其他意思会导致一些问题。因此,尽管它们也是有效的名称,不会引起语法错误,也不能随便使用。保留标识符包括那些以下划线字符开头的标识符和标准库函数名,如printf()。
5.变量名的命名规范
(1)只能包含 3 种字符(数字、大/小写字母,下划线)
(2)不能以数字开头(即,只能以字母或下划线开头) 如:int 2name; //非法
(3)不能和“关键字”同名(c 语言内部已经使用的“名称”),比如类型名 int
操作系统和C库经常使用以一个或两个下划线字符开始的标识符(如, _kcab),因此最好避免在自己的程序中使用这种名称。
(4)C语言的名称区分大小写,即把一个字母的大写和小写视为两个不同的 字符。因此,stars和Stars、STARS都不同。
(5)变量名的最大长度,C 语言没有规定。 最大长度限制,取决于编译器,一般都在 32 以上。
(6)变量名,最好“顾名思义”,不用使用汉语拼英的简写!比如:用 name 表示姓名,用 power 表示功率。
(7)命名风格: C/C++语言编程规范
①下划线风格student_age; (一般用于常量)
②小驼峰风格 int studentAge; (一般用于变量名、参数)
③大驼峰风格(帕斯卡命名法) class StudentAge; (一般用于“类名”和函数名)
④全部大写#define MAX_AGE 30 (一般用于宏)
⑤请忘记“匈牙利命名法”(属性+类型+对象描述),这种方法已经很少使用
拓展:
C99和C11允许使用更长的标识符名,但是编译器只识别前63个字符。对于外部标识符,只允许使用31个字符。
〔以前C90只允许 6个字符,这是一个很大的进步。旧式编译器通常最多只允许使用8个字符。〕实际上,你可以使用更长的字符,但是编译器会忽略超出的字符。
也就是说,如果有两个标识符名都有63个字符,只有一个字符不同,那么编译器会识别这是两个不同的名称。如果两个标识符都是64个字符,只有最后一个字符不同,那么编译器可能将其视为同一个名称,也可能不会。标准并未定义在这种情况下会发生什么。
🎈(三)常变量
常变量与常量的异同是:常变量具有变量的基本属性:有类型,占存储单元,只是不允许改变其值。可以说,常变量是有名字的不变量,而常量是没有名字的不变量。有名字就便于在程序中被引用。
C 99允许使用常变量,方法是在定义变量时,前面加一个关键字const,如:
const int a = 3;
拓展:
请思考:常变量与符号常量有什么不同?如:
//定义符号常量 define Pi 3.1415926//定义常变量 const float pi=3.1415926;
符号常量Pi和常变量pi都代表3.1415926,在程序中都能使用。但二者性质不同:
- 定义符号常量用#define指令,它是预编译指令,它只是用符号常量代表一个字符串,在预编译时仅进行字符替换,在预编译后,符号常量就不存在了(全置换成3.1415926了),对符号常量的名字是不分配存储单元的。
- 而常变量要占用存储单元,有变量值,只是该值不改变而已。从使用的角度看,常变量具有符号常量的优点,而且使用更方便。有了常变量以后,可以不必多用符号常量。
🚀第二个简单程序示例相关知识点总结
// talkback.c -- 演示与用户交互
#include <stdio.h>
#include <string.h> // 提供strlen()函数的原型
#define DENSITY 62.4 // 人体密度(单位:磅/立方英尺)int main() {float weight, volume;int size, letters;char name[40]; // name是一个可容纳40个字符的数组printf("Hi! What's your first name?\\n");scanf("%s", name);printf("%s, what's your weight in pounds?\\n", name);scanf("%f", &weight);size = sizeof name;letters = strlen(name);volume = weight / DENSITY;printf("Well, %s, your volume is %2.2f cubic feet.\\n",name, volume);printf("Also, your first name has %d letters,\\n",letters);printf("and we have %d bytes to store it.\\n", size);return 0;
}
⛳六、字符串和格式化输入输出
🎈(一)C语言字符串
C语言没有专门用于储存字符串的变量类型,字符串都被储存在char类型的数组中。数组由连续的存储单元组成,字符串中的字符被储存在相邻的 存储单元中,每个单元储存一个字符
- 数组看作是一行连续的多个存储单元。用更正式的说法是,数组是同类型数据元素的有序序列。
- 在 c 语言中,为了便于存储字符串,要求在最后一个字符的后面存储一个 0(一个字节)。这个 0,称为“字符串结束符”,常用 ‘\\0’ 表示
- “” 的字符串长度是 0(空串)
- “ ” 的字符串长度是 1(含有一个空格)
数组:
char name[40];
name后面的方括号表明这是一个数组,方括号中的40表明该数组中的元素数量。char表明每个元素的类型。
🎈(二)printf()和scanf()
C语言本身不提供输入输出语句,输入和输出操作是由C标准函数库中的函数来实现的。在C标准函数库中提供了一些输人输出函数,例如 printf函数和 scanf 函数。读者在使用它们时,千万不要误认为它们是C语言提供的“输入输出语句”。printf 和 scanf不是C语言的关键字,而只是库函数的名字。实际上可以不用printf和 scanf这两个名字,而另外编写一个输入函数和一个输出函数,用来实现输人输出的功能,采用其他名字作为函数名。
- 在C程序中用来实现输出和输入的主要是printf函数和 scanf 函数。这两个函数是格式输入输出函数。用这两个函数时,程序设计人员必须指定输人输出数据的格式,即根据数据的不同类型指定不同的格式。
- 必须要包含stdio.h头文件,在stdio.h头文件中存放了调用标准输入输出函数时所需要的信息,包括与标准Ⅰ/O库有关的变量定义和宏定义以及对函数的声明。
1.用printf()函数输出数据
(1)一般格式
printf( 格式字符串, 待打印项1, 待打印项2,...);//例如:
printf("%s, what's your weight in pounds?\\n", name);
①格式字符串使用双撇号括起来的一个字符串,称为格式控制字符串,简称格式字符串。它包括两个信息:
-
格式声明(转换说明):格式声明由“%”和格式字符组成,如%d、%f等。它的作用是将输出的数据转换为指定的格式后输出。格式声明总是由“%”字符开始的。
-
普通字符。普通字符即需要在输出时原样输出的字符。
②待打印项1、待打印项2等都是要打印的项。我们叫做输出列表,它们可以是变量、常量,甚至是在打印之前先要计算的表达式。
③请求printf()函数打印数据的指令要与待打印数据的类型相匹配。例如, 打印整数时使用%d,打印字符时使用%c。这些符号被称为转换说明 (conversion specification),它们指定了如何把数据转换成可显示的形式。
④注意事项:
- 格式字符串中的转换说明一定要与后面的每个项相匹配,并且是有顺序的
- 由于printf()函数使用%符号来标识转换说明,因此打印%符号就成了个 问题。如果单独使用一个%符号,编译器会认为漏掉了一个转换字符。解决方法很简单,使用两个%符号就行了
(2)printf()的转换说明修饰符
在%和转换字符之间插入修饰符可修饰基本的转换说明。
(3)转换说明的意义
转换说明把以二进制格式储存在计算机中的值转换成一系列字符(字符串)以便于显示。例如,数字76在计算机内部的存储格式是二进制数01001100。%d转换说明将其转换成字符7和 6,并显示为76;%x转换说明把相同的值(01001100)转换成十六进制记数法4c;%c转换说明把01001100转换成字符L。
转换(conversion)可能会误导读者认为原始值被转替换成转换后的值。实际上,转换说明是翻译说明,%d的意思是“把给定的值翻译成十进制整数文本并打印出来”。
①转换不匹配
前面强调过,转换说明应该与待打印值的类型相匹配。通常都有多种选择。例如,如果要打印一个int类型的值,可以使用%d、%x或%o。这些转换说明都可用于打印int类型的值,其区别在于它们分别表示一个值的形式不 同。类似地,打印double类型的值时,可使用%f、%e或%g
②printf()的返回值
大部分C函数都有一个返回值,这是函数计算并返回给主调程序(calling program)的值。例如,C库包含一个sqrt()函数,接受一个数作为参数,并返回该数的平方根。可以把返回值赋给变量,也可以用于计算,还可以作为参数传递。总之,可以把返回值像其他值一样使用。 printf()函数也有一个返回值,它返回打印字符的个数。如果有输出错误, printf()则返回一个负值(printf()的旧版本会返回不同的值)。
printf()的返回值是其打印输出功能的附带用途,通常很少用到,但在检查输出错误时可能会用到(如,在写入文件时很常用)。如果一张已满的 CD或DVD拒绝写入时,程序应该采取相应的行动,例如终端蜂鸣30秒。不过,要实现这种情况必须先了解if语句。
#include <stdio.h>
int main(void)
{int bph2o = 212;int rv;rv = printf("%d F is water's boiling point.\\n", bph2o);printf("The printf() function printed %d characters.\\n",rv);return 0;
}/*首先,程序用rv = printf(...);的形式把printf()的返回值赋给rv。因此,该
语句执行了两项任务:打印信息和给变量赋值。其次,注意计算针对所有字
符数,包括空格和不可见的换行符(\\n)。
*/
③打印较长的字符串
有时,printf()语句太长,在屏幕上不方便阅读。空白(空格、制表 符、换行符)仅用于分隔不同的部分,C 编译器会忽略它们。因此,一条语句可以写成多行,只需在不同部分之间输入空白即可。
//该语句在逗号和rv之间断行。为了让读者知道该行未完,示例缩进了rv。C编译器会忽略多余的空白。
printf("The printf() function printed %d characters.\\n",
rv);
但是,不能在双引号括起来的字符串中间断行。从我这个颜色上就能反映出,肯定是不行的,C编译器会报错字符串常量中有非法字符。在字符串中,可以使用\\n 来表示换行字符,但是不能通过按下Enter(或Return)键产生实际的换行 符。
printf("The printf() function printed %d
characters.\\n", rv);
给C语言断行的方法有三种:
#include <stdio.h>int main(void)
{
printf("Here's one way to print a ");
printf("long string.\\n");
printf("Here's another way to print a \\
long string.\\n");
printf("Here's the newest way to print a "
"long string.\\n"); /* ANSI C */
return 0;
}/*该程序的输出如下:
Here's one way to print a long string.
Here's another way to print a long string.
Here's the newest way to print a long string.
*/
- ①使用多个printf()语句。因为第1个字符串没有以\\n字符结束,所以第2个字符串紧跟第1个字符串末尾输出。
- ②用反斜杠(\\)和Enter(或Return)键组合来断行。这使得光标移至下一行,而且字符串中不会包含换行符。其效果是在下一行继续输出。 但是,下一行代码必须和程序清单中的代码一样从最左边开始。如果缩进该行,比如缩进5个空格,那么这5个空格就会成为字符串的一部分。
- ③ANSI C引入的字符串连接。在两个用双引号括起来的字符串之间用空白隔开,C编译器会把多个字符串看作是一个字符串。
2.用scanf()函数输入数据
C库包含了多个输入函数,scanf()是最通用的一个,因为它可以读取不同格式的数据。当 然,从键盘输入的都是文本,因为键盘只能生成文本字符:字母、数字和标 点符号。如果要输入整数 2014,就要键入字符 2、0、1、4。如果要将其储存为数值而不是字符串,程序就必须把字符依次转换成数值,这就是scanf() 要做的。
scanf()把输入的字符串转换成整数、浮点数、字符或字符串,而 printf()正好与它相反,把整数、浮点数、字符和字符串转换成显示在屏幕上的文本。
一般格式:
scanf(格式字符串,地址列表)//例如:
#include <stdio.h>int main(void)
{int age; // 变量float assets; // 变量char pet[30]; // 字符数组,用于储存字符串printf("Enter your age, assets, and favorite pet.\\n");scanf("%d %f", &age, &assets); // 这里要使用&scanf("%s", pet); // 字符数组不使用&printf("%d $%.2f %s\\n", age, assets, pet);return 0;
}
scanf()和 printf()类似,也使用格式字符串和参数列表。scanf()中的格式字符串表明字符输入流的目标数据类型。两个函数主要的区别在参数列表中。
实际上,在C语言中scanf()并不是最常用的输入函数。这里重点介绍它是因为它能读取不同类型的数据。C 语言还有其他的输入函数,如 getchar() 和 fgets()。
(1)scanf()输入的原理剖析
假设scanf()根据一 个%d转换说明读取一个整数
- ①scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始读取。
- ②因为要读取整数,所以 scanf()希望发现一个数字字符或者一个符号(+或-)。如果找到一个数字或符号,它便保存该字符,并读取下一个字符。如果下一个字符是数字,它便保存该数字并读取下一个字符。scanf()不断地读取和保存字符,直至遇到非数字字符。
- ③如果遇到一个非数字字符,它便认为读到了整数的末尾。然后, scanf()把非数字字符放回输入,这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符
- ④最后,scanf()计算已读取数字(可能还有符号)相应的数值,并将计算后的值放入指定的变量中。
注意:当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\\0’,让数组中的内容成为一个C字符串。
(2)地址列表
printf()函数使用变量、常量和表达式,而scanf()函数使用指向变量的指针,用scanf()读取基本变量类型的值,在变量名前加上一个&,如果用scanf()把字符串读入字符数组中,不要使用&。
(3)格式字符串
①scanf()函数中,格式字符串可以使用空白(换行符、制表符和空格)把转换说明分成多个字段。 在依次把转换说明和地址列表字段匹配时会跳过空白,即除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。因此,scanf("%d%d", &n, &m)与scanf("%d %d", &n, &m)
的行为相同
②在用“%c”格式声明输入字符时,空格字符和“转义字符”中的字符都作为有效字符输入,例如:
scanf("%c%c%c",&c1,&c2,&c3);//在执行此函数时应该连续输入3个字符,中间不要有空格。如:
abc(回车)//若在两个字符间插人空格。如:
a b c (回车)
//系统会把第1个字符'a'送给cl;第2个字符是空格字符’',送给c2;第3个字符'b'送给c3
③scanf()函数允许把普通字符放在格式字符串中,但如果在格式控制字符串中除了格式转换声明和空白以外还有其他字符,则在输入数据时在对应的位置上应输人与这些字符相同的字符。例如:
scanf("a=%f,b=%f,c=%f",&.a,&.b,&.c);//在输入数据时,应在对应的位置上输入同样的字符。即输入
a=1,b=3,c=2
//如果输人
1 3 2
④在输入数值数据时,如输入空格、回车、Tab键或遇非法字符(不属于数值的字符),认为该数据结束。例如:
scanf("%d%c%f",&a,&b,&c);//输入
1234a123o.26
第1个数据对应%d格式,在输入1234之后遇字符’ a’ ,因此系统认为数值1234后已没有数字了,第1个数据应到此结束,就把1234送给变量a。把其后的字符’a’送给字符变量b,由于%c只要求输入一个字符,系统判定该字符已输入结束,因此输入字符a之后不需要加空格。字符’a’后面的数值应送给变量c。如果由于疏忽把1230.26错打成123o.26,由于123后面出现字母o,就认为该数值数据到此结束,将123送给变量c,后面几个字符没有被读入。
- scanf()函数所用的转换说明与printf()函数几乎相同。主要的区别是,对于float类型和double类型,printf()都使用%f、%e、%E、%g和%G转换说明。而scanf()只把它们用于float类型,对于double类型时要使用l修饰符。
(4)scanf()的返回值
scanf()函数返回成功读取的项数。如果没有读取任何项,且需要读取一 个数字而用户却输入一个非数值字符串,scanf()便返回0。当scanf()检测 到“文件结尾”时,会返回EOF(EOF是stdio.h中定义的特殊值,通常用 #define指令把EOF定义为-1)
🎈(三)字符输入输出函数
1.putchar()
想从计算机向显示器输出一个字符,可以调用系统函数库中的putchar函数(字符输出函数)。
putchar函数的一般形式为:putchar(c)
putchar是 put character(给字符)的缩写,很容易记忆。C语言的函数名大多是可以见名知义的,不必死记。putchar©的作用是输出字符变量c的值,显然输出的是一个字符。
说明:putchar(c)中的c可以是字符常量、整型常量、字符变量或整型变量(其值在字符的ASCII代码范围内)。
//可以用putchar函数输出转义字符,例如:
putchar('\\101'); //(输出字符A)
putchar('\\''); //(括号中的\\'是转义字符代表单撇号,输出单撇号字符)
putchar('lo15'); //(八进制数15等于十进制数13,从附录A查出13是“回车”的ASCII代码,因此输出回车,不换行,使输出的当前位置移到本行开头)
2.getchar
为了向计算机输人一个字符,可以调用系统函数库中的getchar函数(字符输入函数)。
getchar 函数的一般形式为:getchar()
getchar是get character(取得字符)的缩写,getchar函数没有参数,它的作用是从计算机终端(一般是键盘)输入一个字符,即计算机获得一个字符。getchar 函数的值就是从输入设备得到的字符。getchar 函数只能接收一个字符。如果想输入多个字符就要用多个getchar 函数。
3.ctype.h系列的字符函数
C 有一系列专门处理字符的函数,ctype.h头文件包含了这些函数的原型,这些函数接受一个字符作为参数,如果该字符属于某特殊的类别,就返回一个非零值(真);否则,返回0(假)。例如:
如果isalpha()函数的参数是一个字母,则返回一个非零值。
isspace(),如果该函数的参数是空白字符,则返回真。
注意,字符映射函数不会修改原始的参数,这些函数只会返回已修改的值。也就是说,下面的语句不改变ch的值:
tolower(ch); // 不影响ch的值//这样做才会改变ch的值:
ch = tolower(ch); // 把ch转换成小写字母
🚀第三个简单程序示例相关知识点总结
/* shoes1.c -- 把鞋码转换成英寸 */
#include <stdio.h>
#define ADJUST 7.31 // 字符常量int main(void)
{const double SCALE = 0.333;// const变量double shoe, foot;shoe = 9.0;foot = SCALE * shoe + ADJUST;printf("Shoe size (men's) foot length\\n");printf("%10.1f %15.2f inches\\n", shoe, foot);return 0;
}
⛳七、运算符、表达式和语句
几乎每一个程序都需要进行运算,对数据进行加工处理,否则程序就没有意义了。要进行运算,就需规定可以使用的运算符。C语言的运算符范围很宽,把除了控制语句和输入输出以外几乎所有的基本操作都作为运算符处理,例如将赋值符“=”作为赋值运算符、方括号
作为下标运算符等。
名称 | 符号 |
---|---|
算术运算符 | (+, -, *, /, %, ++, --) |
关系运算符 | (> ,<,==,>=,<=,!=) |
逻辑运算符 | (!,&&, ||) |
位运算符 | (<<,>>,~,|,^,&) |
赋值运算符 | (=及其扩展赋值运算符) |
条件运算符 | (?: ) |
逗号运算符 | (,) |
指针运算符 | (*和&) |
求字节数运算符 | (sizeof) |
强制类型转换运算符 | ((类型) ) |
成员运算符 | (. ->) |
下标运算符 | ([]) |
其他 | (如函数调用运算符()) |
🎈(一)运算符
1.赋值运算符"="
在C语言中,=并不意味着“相等”,而是一个赋值运算符。下面的赋值表达式语句:
bmw = 2002;
把值2002赋给变量bmw。也就是说,=号左侧是一个变量名,右侧是赋 给该变量的值。符号=被称为赋值运算符。另外,上面的语句不读作“bmw等 于2002”,而读作“把值2002赋给变量bmw”。赋值行为从右往左进行。
几个术语:
数据对象:赋值表达式语句的目的是把值储存到内存位置上。用于储存值的数据存储区域统称为数据对象(data object)。C 标准只有在提到这个概念时才会用到对象这个术语,使用变量名是标识对象的一种方法,还可以指定数组的元素、结构的成员,或者使用指针表达式(指针中储存的是它所指向对象的地址)。
左值:左值(lvalue)是 C 语言的术语,用于标识特定数据对象的名称或表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。 对于早期的C语言,提到左值意味着:
- 它指定一个对象,所以引用内存中的地址;
- 它可用在赋值运算符的左侧,左值(lvalue)中的l源自left。
注意:在后来标准中新增了const限定符。用const创建的变量不可修改。 因此,const标识符满足上面的第1项,但是不满足第2项,为此,C标准新增了一个术语:可修改的左值(modifiable lvalue),用于标识可修改的对象。所以,赋值运算符的左侧应该是可修改的左值。
右值:指的是能赋值给可修改左值的量,且本身不是左值。如:
bmw = 2002;
bmw是可修改的左值,2002是右值,右值可以是常量、变量或其他可求值的表达式(如,函数调用)
int ex; int why; int zee; const int TWO = 2; why = 42; zee = why; ex = TWO * (why + zee);int jane, tarzan, cheeta; cheeta = tarzan = jane = 68;
ex、why和zee都是可修改的左值(或对象定位值),它们可用于赋值运算符的左侧和右侧。TWO是不可改变的左值,它只能用于赋值运算符的右侧
拓展:
TWO被初始化为2,这里的=运算符表示初始化而不是赋值,因此并未违反规则,在定义变量的同时,我们利用=运算符给予一个初值的操作我们叫做初始化
许多其他语言都会回避该程序中的三重赋值,但是C完全没问题。赋值的顺序是从右往左:首先把86赋给jane,然后再赋给tarzan,最后赋给 cheeta。
复合赋值运算:
x += 10; // x = x + 10 x -= 10; // x = x - 10
类似的还有: *= , /=, %=等等
2.算术运算符
最上面的+和-又叫符号运算符,下面的+和-才是加法和减法运算符,+和-运算符都被称为二元运算符(binary operator),即这些运算符需要两个运算对象才能完成操作。
- 由于键盘无×号,运算符×以*代替。
- 由于键盘无÷号,运算符÷以/代替。
- %运算符要求参加运算的运算对象(即操作数)为整数,结果也是整数。如8%3,结果为2。除%以外的运算符的操作数都可以是任何算术类型。
3.自增自减运算符
i++ => i = i+1
++i => i = i+1i-- => i = i-1
--i => i = i-1i = 100;
x = i++; // x = 100
i = 100;
x = ++i; // x= 101
4.关系运算符
char, int, short, float, double 等数据类型,都可以使用比较运算:
大于: >
大于或等于: >=
小于: <
小于或等于: <=
不等于: !=
等于: ==
注意:比较运算的结果为(逻辑值)
结果为“真”: true
结果为“假”: false
5.逻辑运算符
(1)逻辑与 &&
都为真,逻辑与才是真只要有一个是假, 逻辑与就是假,相当于“而且”
注意:条件 1 && 条件 2
当条件 1 为真时,才会去判断条件 2,当条件 1 为假时,就不再判断条件 2
拓展:
&&运算符可用于测试范围。例如,要测试score是否在90~100的范围
内,可以这样写:if (range >= 90 && range <= 100)printf("Good show!\\n");
千万不要模仿数学上的写法:
if (90 <= range <= 100) // 千万不要这样写!
由于<=运算符的求值顺序是从左往右,所以编译器把测试表达式解释为:
(90 <= range) <= 100
,子表达式90 <= range的值要么是1(为真),要么是0(为假)。
(2)逻辑或
只要有一个是真, 结果就是真,都为假时,结果才是假,相当于“或者”
注意:条件 1 || 条件 2
当条件 1 为真时,就不再判断条件 2,当条件 1 为假时,才判断条件 2
(3)逻辑非 !
相当于“不”,是一种单目运算符,即只有一个操作数
注意:逻辑非,只对一个条件进行运算! 是一种“单目运算符”
6.位运算符
什么是位? 内存的基本单元是字节, 一个字节有 8 位.
(1)位与
对应位, 都是 1, 结果才是1
(2)位或
对应位, 都是 0, 结果才是 0 只要有一位是 1, 结果就是1
(3)位非
取反
(4)左移
<<
右边的位用 0 来填充, 移动超出左边边界的位则直接抛弃。 向左移动 n 个位置,就等同于乘以 2 的 n 次方
只适用于: int/short/long/char 等定点数类型(float/double 浮点数类型不能使用)
(5)右移
>>
如果左操作数是无符号类型,则左边多出来的位用 0 来填充。 如果左操作数是有符号类型,则左边多出来的位用 符号位(最高位) 来填充。 向左移动 n 个位置,就等同于除以 2 的 n 次方 只适用于: int/short/long/char 等定点数类型(float/double 浮点数类型不能使用)
7.逗号运算符
#include <stdio.h>
int main(void) {int x;// 先计算 x = 3+5, 再计算 3*5x = 3+5, 3*5, 10/5;printf("x=%d\\n", x); //x=8//取最后一个表达式的值,作为整个“逗号表达式”的值x = (3+5, 3*5, 10/5);printf("x=%d\\n", x); //x=2return x;
}
8.三目运算符
条件 ? 表达式 1 :表达式 2
如果条件为真,就取表达式 1 作为整个表达式的值
如果条件为假,就取表达式 2 作为整个表达式的值
#include <stdio.h>int main(void) {int salary;printf("请输入您的月薪: ");scanf("%d", &salary);printf("%s晚上好",salary > 30000 ? "老鸟" : "菜鸟");return 0;
}
9.运算符的优先级
最高优先级:( )和[ ],最低优先级:逗号表达式 ,倒数第二低优先级:赋值和复合赋值(=, +=, -= …) :x = a+ b*c;
! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
10.sizeof运算符和size_t类型
sizeof运算符以字节为单位返回运算对象的大小(在C中,1字节定义为char类型占用的空间大小。过去,1字节通常是8位,但是一些字符集可能使用更大的字节)。运算对象可以是具体的数据对象(如,变量名)或类型。如果运算对象是类型(如, float),则必须用圆括号将其括起来
C 语言规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型,
// sizeof.c -- 使用sizeof运算符
// 使用C99新增的%zd转换说明 -- 如果编译器不支持%zd,请将其改成%u或%lu
#include <stdio.h>
int main(void)
{int n = 0;size_t intsize;intsize = sizeof (int);printf("n = %d, n has %zd bytes; all ints have %zdbytes.\\n",n, sizeof n, intsize);return 0;
}
11.iso646.h头文件
C 是在美国用标准美式键盘开发的语言。但是在世界各地,并非所有的 键盘都有和美式键盘一样的符号。因此,C99标准新增了可代替逻辑运算符的拼写,它们被定义在ios646.h头文件中。如果在程序中包含该头文件,便可用and代替&&、or代替||、not代替!。
🎈(二)表达式
表达式(expression)由运算符和运算对象组成(前面介绍过,运算对 象是运算符操作的对象)。最简单的表达式是一个单独的运算对象,以此为 基础可以建立复杂的表达式。下面是一些表达式:
4
-6
4+21
a*(b + c/d)/20
q = 5*2
x = ++q % 3
q > 3
运算对象可以是常量、变量或二者的组合。一些表达式由子表达式(subexpression)组成(子表达式即较小的表达式),例如,c/d是上 面例子中a*(b + c/d)/20的子表达式。
C 表达式的一个最重要的特性是,每个表达式都有一个值。要获得这个值,必须根据运算符优先级规定的顺序来执行操作。
🎈(三)语句
语句(statement)是C程序的基本构建块。一条语句相当于一条完整的计算机指令。在C中,大部分语句都以分号结尾。C把末尾加上一个分号的表达式都看作是一条语句(即,表达式语句)。因此,像下面这样写也没问题:
//但是,这些语句在程序中什么也不做,不算是真正有用的语句。
8;
3+4;//第一个只是一个表达式(它可能是一个较大表达式的一部分),而第二个则是一条语句:
legs = 4
legs = 4;
C语句分为:
- 控制语句:控制语句用于完成一定的控制功能
-
函数调用语句:由一个函数调用加一个分号构成
printf("This is a C statament");
-
表达式语句:由一个表达式加一个分号构成,最经典的是由赋值表达式构成一个赋值语句
a=3;
-
空语句
;
此语句只有一个分号,它什么也不做。那么它有什么用呢?可以用来作为流程的转向点(流程从程序其他地方转到此语句处),也可用来作为循环语句中的循环体(循环体是空语句,表示循环体什么也不做)。
-
复合语句:复合语句(compound statement)是用花括号括起来的一条或多条语句, 复合语句也称为块(block)。
🎈(四)类型转换
通常,在语句和表达式中应使用类型相同的变量和常量。但是,如果使用混合类型,C 不会像 Pascal那样停在那里死掉,而是采用一套规则进行自动类型转换。虽然这很便利,但是有一定的危险性,尤其是在无意间混合使用类型的情况下(许多UNIX系统都使用lint程序检查类型“冲突”。如果选择更高错误级别,许多非UNIX C编译器也可能报告类型问题)。最好先了解一些基本的类型转换规则
1.隐式类型转换
(1)当类型转换出现在表达式时,无论是unsigned还是signed的char和short 都会被自动转换成int
(2)涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
(3)类型的级别从高至低依次是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。例外的情况是,当 long 和 int 的大小相同时,unsigned int比long的级别高。之所以short和char类型没有列出,是因为它们已经被升级到int或unsigned int。
(5)当作为函数参数传递时,char和short被转换成int,float被转换成 double
(6)算术转换中,就按照以上规则转换;在赋值转换中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级或降级(demotion)。所谓降级,是指把一种类型转换成更低级别的类型;在输出转换中,按照转换说明的格式进行转换,但是int类型数据,按照%f格式输出时,将得到错误的输出,float(或double)类型数据,按照%d格式输出时,将得到错误的输出
2.强制类型转换
通常,应该避免自动类型转换,尤其是类型降级。但是如果能小心使用,类型转换也很方便。可以利用强制类型转换运算符将一个表达式转换成所需类型。即在某个量的前面放置用圆括号括起来 的类型名,该类型名即是希望转换成的目标类型,圆括号和它括起来的类型名构成了强制类型转换运算符(cast operator)
例如:考虑下面两行代码,其中mice是int类型的变量。第2行包含两次int强制类型转换。
mice = 1.6 + 1.7;
mice = (int)1.6 + (int)1.7;
- 第1 行使用自动类型转换。首先,1.6和1.7相加得3.3。然后,为了匹配 int 类型的变量,3.3被类型转换截断为整数3。
- 第2行,1.6和1.7在相加之前都 被转换成整数(1),所以把1+1的和赋给变量mice。
行文至此,落笔为终。文末搁笔,思绪驳杂。只道谢不道别。早晚复相逢,且祝诸君平安喜乐,万事顺意。