常量是在程序执行过程中,其值不发生改变的量,分为直接常量和符号常量两种。
直接常量又分为整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制(VS2019支持二进制常量表示:0b或0B表示二进制)。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
80 //十进制
0120 //八进制
0X50 //十六进制
0B1010000 //二进制
浮点常量由整数部分、小数点、小数部分和指数部分组成。可以使用小数形式或者指数形式来表示浮点常量。
当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。
0.25; //合法
.25; //合法 相当于0.25
5.; //合法 相当于5.0
314159E-5; //合法 3.14159;
314159E; //非法 应输入指数值而非“;”
.E-5; //非法 缺少整数或者分数
字符常量是括在单引号之间的,例如,‘x’ 。
字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。
ASCII 码字符表
转义字符:一种特殊的字符常量
\
开头,后跟一个或几个字符。\v
垂直制表和 \f
换页符对屏幕没有任何影响,但会影响打印机执行响应操作。下表列出了一些这样的转义序列码:
转义序列 | 含义 | ASCII码值(十进制) |
---|---|---|
\\ | \ 字符 | 092 |
\’ | ’ 字符 | 039 |
\" | " 字符 | 034 |
\a | 警报铃声 | 007 |
\b | 退格键 | 008 |
\f | 换页符 | 012 |
\n | 换行符 | 010 |
\r | 回车 | 013 |
\t | 水平制表符 | 009 |
\v | 垂直制表符 | 011 |
\0 | 空字符(NULL) | 000 |
\ddd | 一到3位的八进制数 | - |
\xhh | 一到2位的十六进制数 | - |
注意数据溢出,如 \520
代表 80 可是我们算的是336,由于字符数据最多256,这里发送数据溢出,所以是 80
字符串字面值或常量是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。
可以使用空格做分隔符,把一个很长的字符串常量进行分行。
下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。
"hello,world";
"hello,\
world";
"hello,""w""orld";
在C语言中,可以用一个标识符来表示一个常量,称之为符号常量。符号常量在使用前必须先定义,一般形式为:#define 标识符 常量值
其中#define是一条编译预处理命令,称为宏定义命令,其功能是把该标识符定义为其后的常量值,该标识符称为符号常量。一个#define命令只能定义一个符号常量,若要定义多个符号常量,需要使用多个#define命令。
示例:
#include<stdio.h>
#define PI 3.1415
int main()
{
printf("圆的面积为:%lf",PI*3*3);
return 0;
}
//output:圆的面积为:28.273500
请注意,把常量定义为大写字母形式,是一个很好的编程习惯。
标识符是编程时使用的名字,用于给变量、常量、函数、语句块等命名,以建立起名称与使用之间的关系。
C语言中把标识符分为三类:关键字,预定义标识符,用户自定义标识符 ,对于自定义标识符有如下规则和规范
命名基本规则
常用命名规范
提示:大家写项目的时候,保持一种标准,建议使用小驼峰(工作了看企业需求)
变量来源于数学,是计算机语言中能储存计算结果或能表示值的抽象概念。
每种变量都有对应的数据类型,有不同大小的存储空间*(为了适应不同的数据类型,也为了节省内存)*,在C语言中主要有如下基础数据类型。
在C语言中本来是不存在bool类型的,但在C99标准中加入了bool类型。加上stdbool头文件即可使用!
定义变量就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。
不过,我们必须给变量取一个合适的名字,就好像每个人都有自己的名字一样,否则就难以区分了。
type variable_list
在这里,type 必须是一个有效的 C 数据类型,可以是 char、int、float、double 或任何用户自定义的对象,variable_list 可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。下面列出几个有效的声明:
int age,money;
char sex;
float PI;
变量可以在定义的时候被赋值,这种在定义是指定一个初始值的方式叫做变量的初始化。
int age = 18; //初始化
age = 19; //赋值
不带初始化的定义:变量的初始值是未定义的。
C 中有两种类型的表达式:
变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:
int g = 20; // g是左值
但是下面这个就不是一个有效的语句,会生成编译时错误:
10 = 20; // 10是右值
数据类型转换就是将数据(变量、数值、表达式的结果等)从一种类型转换为另一种类型。
类型转换一般是为了解决如下的一些问题:
C语言中类型转换主要分为两种:
自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。
float f = 100;
100 是 int 类型的数据,需要先转换为 float 类型才能赋值给变量 f。再如:
int n = f;
f 是 float 类型的数据,需要先转换为 int 类型才能赋值给变量 n。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真(数据输入和输出不一致),或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。
自动类型转换示例:
#include<stdio.h>
int main()
{
float PI = 3.14159;
int s1, r = 5;
double s2;
s1 = r * r * PI;
s2 = r * r * PI;
printf("s1=%d, s2=%f\n", s1, s2);
return 0;
}
运行结果:
s1=78, s2=78.539749
在计算表达式r*r*PI
时,r 和 PI 都被转换成 double 类型,表达式的结果也是 double 类型。但由于 s1 为整型,所以赋值运算的结果仍为整型,舍去了小数部分,导致数据精度丢失。
自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。
强制类型转换的格式为:
(type) expression
type
为目标类型名称,expression
为表达式。例如:
(float) a; // 将变量 a 转换为 float 类型
(int)(x+y); // 把表达式 x+y 的结果转换为 int 整型
(float) 100; // 将数值 100(默认为int类型)转换为 float 类型
下面是一个需要强制类型转换的经典例子:
#include <stdio.h>
int main()
{
int sum = 315; // 总数
int count = 7; // 数目
double average; // 平均数
average = (double) sum / count;
printf("Average is %lf!\n", average);
return 0;
}
output:Average is 44.714286!
sum 和 count 都是 int 类型,如果不进行干预,那么sum / count
的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。
既然 average 是 double 类型,为何不充分利用,尽量提高运算结果的精度呢?为了达到这个目标,我们只要将 sum 或者 count 其中之一转换为 double 类型即可。上面的代码中,我们将 sum 强制转换为 double 类型,这样sum / count
的结果也将变成 double 类型,就可以保留小数部分了,average 接收到的值也会更加精确。
注意不要写作
(double) (sum / count)
,这样写运算结果将是 44.000000,仍然不能保留小数部分。