——数据类型运算符与表达式)
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了——数据类型运算符与表达式)相关的知识,希望对你有一定的参考价值。
3.1 C语言的数据类型
C语言中的数据类型分为如下几类:
- 基本数据类型
- 整型:如整数12、-15等。
- 字符型:如字符’A’、'a’等。
- 浮点型:又被成为实型,即小数,如12.34等。
- 单精度浮点型
- 双精度浮点型
- 枚举型
- 构造数据类型:构造数据类型由一个或多个数据类型用构造的方法定义出来的类型,它由多个基本数据类型或者构造数据类型的成员组成。
- 数组类型
- 结构体类型
- 共用体类型
- 指针类型:值用来表示某个变量在内存中的地址的类型。
- 空类型:有一些函数不需要向调用者返回函数值,可以这类函数定义为“空类型”,用
void
表示说明。
3.2 常量与变量
对于基本数据类型来说,按照其取值是否可改变分为常量和变量两种:
- 在程序执行过程中,其值不会发生改变的量称为常量。如123这个数值在程序执行中无论如何都还是123。
- 在程序执行过程中,其值可发生改变的量称为变量。如
a=123
在程序执行中可能会被重新赋值a=456
就发生了改变。
注意:
- 常量和变量可以与数据类型结合起来进行分类,如整型常量、整型变量、浮点常量、浮点变量等。
- 在程序中常量是可以不经说明直接引用的,如
123
这个数可以直接在程序中任意位置直接使用。 - 在程序中变量必须是先定义后使用,如定义变量
int a=123
后程序才能通过变量名a
来使用这个变量。
3.2.1 常量和符号常量
在程序执行过程中,其值不发生改变的量称为常量。在C语言中分类:
- 直接常量(字面常量):即字面含义的常量。
- 整型常量:12、0、-3。
- 浮点型常量:4.6、-1.23。
- 字符常量:‘a’、‘C’、‘1’。
- 符号常量:用标识符来代表一个常量,称为符号常量。
注意:
-
所谓的标识符就是用来标识变量名、函数名、数组名、类型名、文件名的字符序列,就是我们人为起的一个有特别含义的名字,如name通常表示名字、age通常表示年龄等。
-
符号常量必须先定义后使用,定义的格式是:
#define 标识符 常量
。如#define AGE 18
。 -
在C语言中
#define
是一条预处理命令,功能是把该标识符定义为其后的常量值。定义后,在程序的任意地方都可以使用标识符来引用这个常量值,而不是直接引用常量值,这样的好处是便于修改(如果直接修改常量值需要在程序中所有引用了该常量值的地方都更改;如果只是修改符号常量只需要更改一次就可以了。) -
约定成俗是将符号常量的标识符用大写字母表示如
AGE
,变量的标识符用小写字母如age
。
字面常量示例代码如下:
#include <stdio.h>
// 10、'a'、12.34、123、'1'、-55.55都是常量
// 而int num=10这种情况其实是将常量值10赋给了变量num
int main()
int num = 10;
char c = 'a';
float f = 12.34f;
printf("%d\\n", num);
printf("%c\\n", c);
printf("%f\\n", f);
printf("%d, %c, %f\\n", 123, '1', -55.55);
符号常量示例代码如下:
#include <stdio.h>
// 使用#define定义符号常量,然后在程序的任何地方都可以通过标识符来引用这个常量值
// 如果想要修改这个常量的值,只需要修改#define处的代码,而不需要修改整个程序
#define PRICE 10;
int main()
// 注意,符号常量的值在当前作用域是不能修改的,也不能再赋值
// PRICE = 100;
int num = 10;
int total = num * PRICE;
printf("%d", total);
3.3.3 变量
值可以改变的量称为变量。如int a=123;
在程序运行中可以重新赋值a=456
,那么其值发生了改变,这里a
就是变量。
变量定义必须在变量使用之前。如下:
// 变量定义必须在变量使用之前
printf("%d", num);
int num = 10;
注意变量名和变量值是两个不同的概念,如int a=123;
中a
是变量名,而123
是变量值。
3.3 整型数据
3.3.1 整型常量的表示方法
整型常量就是整常数,在C语言中,整常数有八进制、十六进制和十进制三种,如下表:
分类 | 前缀 | 数码范围 | 合法整常数 | 不合法整常数 | 注意事项 |
---|---|---|---|---|---|
十进制整常数 | 无前缀 | 0~9 | 234、-567、65535、1627 | 023、23D | 十进制数是不能有前导零的。 |
八进制整常数 | 0 | 0~7 | 015(十进制为13)、0101(十进制为65)、0177777(十进制为65535) | 256(没有前缀0)、03A2(包含了非八进制数码)、-0127(出现了负号) | 八进制整常数必须以0开头,即以0作为八进制数的前缀。八进制数通常是无符号数。 |
十六进制整常数 | 0X 或0x | 09、AF或a~f | 0X2A(十进制为42)、0XA0(十进制为160)、0xFFFF(十进制为65535) | 5A(无前缀0X)、0X3H(包含了非十六进制数码) | 十六进制整常数的前缀为0X 或0x 。 |
整常数除了可能有前缀之外,还可能有后缀。
- 后缀
L
或l
:表示长整数。- 因为在16位字长的机器上,基本整型的长度为16位,因此表示的数的范围是有限的。
- 十进制无符号整常数的范围是065535(即2^16个数);有符号数范围是-32768+32767(最高位为符号位);八进制无符号数的表示范围是0~0177777;十六进制无符号数的表示范围为0X0~0XFFFF或0x0~0xFFFF。
- 如果使用的数超过了以上范围则需要使用长整型来表示,即添加
L
或l
后缀。如十进制常数123L、八进制长整数077L(十进制为63);十六进制长整数0XA5L(十进制为165)。 - 长整数123L和基本整数123在数值上没有任何区别,但对于长整型123L系统将会给它分配4个字节的存储空间,而对于基本整型123只会分配2个字节的存储空间。
- 后缀
U
或u
:表示无符号数。- 无符号十进制整数
358u
、无符号十六进制整数0x38Au
等都是无符号数。 - 长整数后缀和无符号数后缀可以一起使用,如
235Lu
。
- 无符号十进制整数
#include <stdio.h>
int main()
// 十进制整数
int n1 = 123;
int n2 = -456;
int n3 = 65535;
printf("%d\\t%d\\t%d\\n", n1, n2, n3);// 123 -456 65535
// 八进制整数
int n4 = 015;
int n5 = 0101;
int n6 = 0177777;
printf("%d\\t%d\\t%d\\n", n4, n5, n6);// 13 65 65535
// 十六进制整数
int n7 = 0X2A;
int n8 = 0xA0;
int n9 = 0XFFFF;
printf("%d\\t%d\\t%d\\n", n7, n8, n9);// 42 160 65535
// 长整数
long l1 = 123456L;
long l2 = 0123L;
long l3 = 0x5AL;
printf("%ld\\t%ld\\t%ld\\n", l1, l2, l3);// 123456 83 90
// 无符号整数
unsigned int u1 = 123U;
unsigned int u2 = 012u;
unsigned int u3 = 0x3Au;
unsigned long u4 = 235Lu;
printf("%d\\t%d\\t%d\\t%ld\\n", u1, u2, u3, u4);// 123 10 58 235
3.3.2 整型变量
3.3.2.1 整型数据在内存中的存放形式
我们常用的数表示形式是十进制的,但计算机并不认识十进制,它只能认识二进制数0或1。所以我们需要将其他进制的数转换成二进制数去存储。
如定义一个整型变量int i = 10;
,数值是以补码表示的:
- 正数的补码和原码(原码就是其绝对值的二进制形式)相同。
- 负数的补码是将该数的绝对值的二进制形式按位去反再加1。
一个在线计算原码、反码、补码的网站:原码/反码/补码计算器
3.3.2.2 整型变量分类
整型变量分为如下几类:
- 基本型:类型说明符为
int
,在内存中占2个字节。 - 短整型:类型说明符为
short int
或short
,所占字节和取值范围均与基本型相同。 - 长整型:类型说明符为
long int
或long
,在内存中占4个字节。 - 无符号型:类型说明符为
unsigned
。
而无符号型又可以与基本型、短整型、长整型向搭配,如下:
- 无符号基本型:类型说明符为
unsigned int
或unsigned
。 - 无符号短整型:类型说明符为
unsigned short
。 - 无符号长整型:类型说明符为
unsigned long
。
各种无符号类型量所占的内存空间字节数与相应的有符号类型量相同。但由于省去了符号位,故不能表示负数。
下表列出了各类整型量所分配的内存字节数及数的表示范围:
类型说明符 | 数的范围 | 字节数 |
---|---|---|
int | -32768~32767 即-215~(215-1) | 2 |
unsigned int | 0~65535 即0~(2^16-1) | 2 |
short int | -32768~32767 即-215~(215-1) | 2 |
unsigned short int | 0~65535 即0~(2^16-1) | 2 |
long int | -21474836482147483647即-2^31(2^31-1) | 4 |
unsigned long | 0~4294967295 即0~(2^32-1) | 4 |
**注意:int
、long
等数据类型具体由几个字节表示则与编译器有关。**如下,可以通过size()
函数来查看数据类型的字节数:
#include <stdio.h>
int main()
int is = sizeof(int);
int uis = sizeof(unsigned int);
int sis = sizeof(short int);
int usis = sizeof(unsigned short int);
int lis = sizeof(long int);
int ulis = sizeof(unsigned long int);
// 如下是我所使用编译器打印的结果
printf("%d\\t%d\\t%d\\t%d\\t%d\\t%d", is, uis, sis, usis, lis, ulis);// 4 4 2 2 4 4
3.3.2.4 整型变量的定义
变量定义的一般形式为:类型说明符 变量名标识符, 变量名标识符,...
。例如:int a,b,c;
、long x,y,z;
、unsigned p,q;
。
变量定义的注意事项:
- 允许在一个类型说明符之后,定义多个相同类型的变量。如
int a,b,c;
中的a、b、c都是整型变量。 - 各变量名之间用逗号分隔,类型说明符与变量名之间至少一个空格间隔。如
int a;
。 - 最后一个变量名周必须以分号(
;
)结尾。 - 变量定义必须在变量使用之前,一般放在函数体的开头部分。
#include <stdio.h>
int main()
// 定义四个整型变量
int a, b, c, d;
// 定义一个无符号整型变量
unsigned u;
// 给变量赋值
a = 12;
b = -24;
u = 10;
c = a + u;
d = b + u;
// 打印结果
printf("a+u=%d, b+u=%d", c, d);
3.3.2.4 整型数据的溢出
看下面的代码,发现b的结果并不符合我们的预期结果,我们希望它是32768,但它变成了-32768。
#include <stdio.h>
int main()
short a = 32767;
short b = a + 1;
printf("a=%d, b=%d", a, b);// a=32767, b=-32768
为什么是如上结果的原因如下图所示:
3.4 实型数据
3.4.1 实型常量的表示方法
实型也被称为浮点型,所以实型常量也被称为实数或浮点数。在C语言中,实数采用十进制形式,它有两种表示方法:
组成 | 合法浮点数 | 不合法浮点数 | 说明 | |
---|---|---|---|---|
十进制数形式 | 由数码0~ 9和小数点组成。 | 0.0、25.0、5.789、0.13、-12.34、30. | 注意,必须有小数点。 | |
指数形式 | 由十进制数,加阶码标志“e”或“E”以及阶码(只能为整数,可以带符号)组成。一般形式是a E n (a为十进制数,n为十进制数),值为a*10^n 。 | 2.1E5 (等于2.1*10^5 )、3.7E-2 (等于3.7*10^-2 ) | 345 (无小数点)、E7 (阶码标志E之前无数字)、-5 (无阶码标志)、53.-E3 (负号位置不对)、2.7E (无阶码) |
注意:标准C允许浮点数使用后缀。后缀为“f”或“F”即表示该数为浮点数。如356f
和356.
是等价的。
#include <stdio.h>
int main()
printf("%f\\n ", 356.);
printf("%f\\n ", 356.1);
printf("%f\\n ", 356.1f);
3.4.2 实型变量
3.4.2.1 实型数据在内存中的存放形式
浮点数在内存中一般占4个字节(32位),按指数形式存储。
3.4.2.2 实型变量的分类
在C语言中浮点数变量分为:
实型变量 | 类型说明符 | 比特数(字节数) | 有效数字 | 数的范围 |
---|---|---|---|---|
单精度(float型) | float | 32(4) | 6~7 | 3.4E-38~3.4E+38 |
双精度(double型) | double | 64(8) | 15~16 | 1.7E-308~1.7E+308 |
长双精度(long double型) | long double | 128(16) | 18~19 | 10(-4931)~104932 |
实型变量定义的格式和整型变量一样:
#include <stdio.h>
int main()
// 单精度实型变量
float x, y;
// 双精度实型变量
double a, b;
// 长双精度实型变量
long double m, n;
// 打印float、double、long double在该编译器中占用字节数
printf("%d, %d, %d", sizeof(float), sizeof(double), sizeof(long double));
3.4.2.3 实型数据的舍入误差
计算机中存储单元是有限的,不可能无限存储小数,所以浮点数的有效数字是有限的。当超过这个范围之后就会产生误差。
#include <stdio.h>
int main()
// a是单精度浮点数,有效位数只有7位,整数占了五位,因此小数两位之后为无效数字
float a = 33333.33333;
// b是双精度浮点数,有效位数为16位,默认输出6位小数(不足六位以 0 补齐,超过六位按四舍五入截断)
double b = 33333.33333333333333;
printf("a=%f, b=%f", a, b);// a=33333.332031, b=33333.333333
3.4.3 实型常数的类型
C语言中的浮点数常数不区分单精度还是双精度,都统一按照双精度double来进行处理。
3.5 字符型数据
字符型数据包括字符常量和字符变量。
3.5.1 字符常量
所谓的字符常量就是用单引号(''
)括起来的一个字符。如'a'
、'1'
、'?'
等。字符常量有如下特点:
- 字符常量必须用单引号(
''
)括起来,不能使用双引号(""
)。如"a"
是不合法的字符常量。 - 字符常量只能有一个字符,不能是字符串。如
'acb'
就是不合法的字符常量。 - 字符常量中的字符可以是字符集中的任意字符,包括数字。但是字符常量不能参与运算,即
'5'+5
是错误的。
3.5.2 转义字符
转义字符是一种特殊的字符常量。以反斜线"/"
开头,后面跟着一个或几个字符,有特定的含义,如\\n
不表示字符n
而是“回车换行”。转义字符主要用于表示那些用一般字符不便于表示的控制代码。常见的转义字符如下:
转义字符 | 转义字符的意义 | ASCII代码 |
---|---|---|
\\n | 回车换行 | 10 |
\\t | 横向跳到下一制表位置 | 9 |
\\b | 退格 | 8 |
\\r | 回车 | 13 |
\\f | 走纸换页 | 12 |
\\\\ | 反斜线符"\\" | 92 |
\\' | 单引号符 | 39 |
\\” | 双引号符 | 34 |
\\a | 鸣铃 | 7 |
\\ddd | 1~3位八进制数所代表的字符,如\\101 表示字母"A"、\\102 表示字母"B"。 | |
\\xhh | 1~2位十六进制数所代表的字符,如\\X0A 表示换行。 |
#include <stdio.h>
int main()
printf("%d\\t%d\\n", 123, 456);
printf("%d\\r\\"%d\\"", 999, 666);
3.5.3 字符变量
字符变量用来存储字符常量,即单个字符。字符变量的定义格式如下:char 变量名标识符 = '字符常量';
。如char a='A',b;
。其格式和书写规则与整型变量相同。
#include <stdio.h>
int main()
char a = 'A';
char b = '0';
char c = '\\t';
printf("%c%c%c", a, c, b);// A 0
3.5.4 字符数据在内存中的存储形式及使用方法
每个字符变量被分配一个字节的内存空间,因此只能存放一个字符。而字符值如c
在内存中是以ASCII码的形式存放在变量的内存单元中的,而ASCII码值是十进制数字,可以转换成二进制存放在计算机中。如'x'
的十进制ASCII码是120,如果有char a='x';
,那么a变量在内存中存放120的二进制是0111 1000
。
因此C语言允许对整型变量赋予字符值(如int b = 'c';
),也允许对字符变量赋予整型值(如char a = 97;
)。同样在输出时,允许把字符变量当作整型输出(如printf("%d", 'a');// 97
),也允许把整型变量按字符量输出(如printf("%c", 97);// a
)。
#include <stdio.h>
int main()
// 对字符变量赋予整型值
char a = 97;
// 对整型变量赋予字符值
int b = 'c';
// 把字符变量当作整型输出
printf("%d", a);// 当格式符为"d"时,对应输出的变量值为整数
// 把整型变量按字符量输出
printf("%c", b);// 当格式符为"c"时,对应输出的变量值为字符
注意:整型变量为两个字节,字符变量为一个字节,当整型变量按照字符变量处理时,只有低八位参与处理。
还可以让字符变量与整数参与运算,如将小写字母转换成大写,因为它们的ASCII码值相差32,可以利用这一点来进行大小写字母的相互转换:
#include <stdio.h>
int main()
// 将小写字母转换成大写字母,减去32
char a = 'a';
a = a - 32;
printf("%c", a);// A
// 将大写字母转换成小写字母,加上32
char A = 'A';
A = A + 32;
printf("%c", A);// a
3.5.5 字符串常量
字符串常量就是用双引号括起来的字符序列,如"china"
、"hello world"
、"123456"
。字符串常量与字符常量得到区别如下:
- 字符常量由单引号括起来,如
'a'
;字符串常量由双引号括起来,如"hello"
。 - 字符常量只能是单个字符,如
'a'
是正确的,但'abc'
是错误的;字符串常量可以包含一个或多个字符,如"a"
是正确的,"abc"
也是正确的。 - 可以将一个字符常量赋予给一个字符变量(如
char c='a';
),但不能把一个字符串常量赋给一个字符变量(如char c="abc"
是错误的),但可以用一个字符数组来存放一个字符串常量。 - 字符常量占用一个字节的内存空间;字符串常量占用的内存字节数等于字符串中字节数加1,增加的一个字节用于存放
"\\0"
字符(ASCII码为0),这是字符串结束的标志。
3.6 变量赋初值
上面使用的通常是声明变量后再赋值,格式是:类型说明符 变量1;变量1=值1;
。还可以在声明变量的同时赋予初值,格式是:类型说明符 变量1=值1, 变量2=值2, ...
。如下:
#include <stdio.h>
int main()
// 声明变量后再赋值
int a;
a = 123;
// 声明变量的同时并赋值
int b = 123;
int c = 456, d = 789;
// 但注意不支持如下赋值方式
// int a = b = c = 111;
3.7 各类数值型数据之间的混合运算
变量的数据类型是可以相互转换的,转换的方法有两种:一种是自动转换,一种是强制转换。
3.7.1 自动类型转换
自动转换发生在不同数据类型的量混合运算时,由编译系统自动完成。自动转换的规则如下:
- 如果参与运算量的类型不同,则先转换成同一类型,然后再进行运算。
- 转换按数据长度增加的方向进行,以保证精度不降低。如
int
类型和long
类型运算则先转换成long
类型、如float
与double
数据运算则先转换成double
类型。 - 所有的浮点运算都是按双精度进行运算的,即使是只含有float单精度运算的表达式,也会先转换成double双精度类型,再运算。
- char类型和short类型参与运算,都会先转换成int类型。
- 在赋值运算中,等号两边的数据类型不同时,等号右边的类型将转换为右边的类型。如果等号右边的数据类型长度比左边长时,转换后将丢失一部分数据,即会降低精度,丢失的部分按四舍五入向向前舍入。
#include <stdio.h>
int main()
// 自动类型转换
float PI = 3.14159;
int s, r = 5;
// r是整型,PI是浮点数,r和PI都转换成double型计算,结果也为double型
// 但s是整型,因此会转换成整型,舍去了小数部分
s = r * r * PI;// 求圆的面积
printf("%d", s);// 78
3.7.2 强制类型转换
强制类型转换是手动进行的,需要通过类型转换运算来实现。
其格式是:(类型说明符)(表达式)
,即将表达式的运算结果转换成类型说明符所表示的类型。如(float)a
将a转换成单精度浮点型;(int)(x+y)
将x+y
的结果转换成整型。
#include <stdio.h>
int main()
float f = 12.34;
printf("(int)f=%d, f=%f\\n", (int) f, f);// (int)f=12, f=12.340000
注意:
- 类型说明符和表达式都必须加小括号(如
(int)(x+y)
),单个变量不需要加括号(如(int)x
)。 - 无论是强制转换还是自动转换,都是临时的,并没有对其本身的类型进行改变。如
(int)f
转换成int类型,但f本身的数据类型没有变。
3.8 算术运算符和算术表达式
3.8.1 C运算符简介
C语言的运算符分为如下几类:
- 算术运算符:用于各类数值运算。
- +:加法运算。
- -:减法运算。
- *:乘法运算。
- /:除法运算。
- %:求余运算。
- ++:自增运算。
- –:自减运算。
- 关系运算符:用于比较运算。
>
:大于<
:小于==
:等于>=
:大于等于<=
:小于等于!=
:不等于
- 逻辑运算符:用于逻辑运算。
&&
:与||
:或!
:非
- 位操作运算符:按二进制位进行运算。
&
:位与|
:位或~
:位非^
:位异或<<
:左移>>
:右移
- 赋值运算符:用于赋值运算。
=
:简单赋值+=
、-=
、*=
、/=
、%=
:复合算术赋值&=
、|=
、^=
、>>=
、<<=
:复合位运算赋值
- 条件运算符:用于条件求值。
?:
:三目运算符
- 逗号运算符:用于将若干表达式组合成一个表达式。
,
- 指针运算符
*
:取内容运算&
:取地址运算
- 求字节数运算符:用于计算数据类型所占字节数。
sizeof
- 特殊运算符
()
[]
:下标->
或.
:成员
3.8.2 算术运算符和算术表达式
3.8.2.1 基本的算术运算符
- 加法运算符“+”:加法运算符为双目运算符,即应有两个量参与加法运算。如a+b、4+8等。+表示一元运算符正号时是右结合,表示二元运算符加号时是左结合
- 减法运算符“-”:减法运算符为双目运算符。但“-”也可作负值运算符,此时为单目运算,如-x、-5等具有左结合性。
- 乘法运算符“*”:双目运算,具有左结合性。
- 除法运算符“/”:双目运算具有左结合性。参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是浮点数,则结果为双精度浮点数。
- 求余运算符(模运算符)“%”:双目运算,具有左结合性。要求参与运算的量均为整型。 求余运算的结果等于两数相除后的余数。
所谓的右结合性就是从右向左执行运算,如
a=b=c
,按a=(b=c)
运算。所谓的左结合性就是从左向右执行运算,如
a+b+c
,按(a+b)+c
运算。
示例如下:
#include <stdio.h>
int main()
int a = 5 + 5;
int b = 5 - 4;
int c = 5 * 5;
int d = 10 / 3;
float e = 10.0f / 3;
int f = 10 % 3;
// a=10, b=1, c=25, d=3, e=3.333333, f=1
printf("a=%d, b=%d, c=%d, d=%d, e=%f, f=%d", a, b, c, d, e, f);
3.8.2.2 算术表达式和运算符的优先级和结合性
算术表达式:用算术运算符和括号将操作数连接起来的,符合C语法规则的式子。如a+b
、(a*2)/2
、i++
等。
运算符的优先级:C语言中,运算符的运算优先级共分为15级。1级最高,15级最低。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。
运算符的结合性:C语言中运算符的结合性分为左结合性(从左向右)和右结合性(从右向左)。如x-y+z
的运算是(x-y)+z
从左向右就是左结合性;如赋值运算x=y=z
的运算是x=(y=z)
先执行y=z
运算再执行x=(y=z)
运算,这种从右向左的就是右结合性。
3.8.2.3 强制类型转换运算符
基本格式是(类型说明符)(表达式)
,即将表达式的运算结果强制转换成类型说明符所表示的类型。如(int)a
将a转换成整型;如(float)(x+y)
将x+y的结果转换成浮点型。
3.8.2.4 自增与自减运算符
自增运算符++
使变量的值自增1;自减运算符--
使变量的值自减1。分为如下几种形式:
++i
:i自增1后再参与其他运算。--i
:i自减1后再参与其他运算。i++
:i参与运算后,i的值再自增1。i--
:i参与运算后,i的值再自减1。
#include <stdio.h>
int main() <以上是关于——数据类型运算符与表达式)的主要内容,如果未能解决你的问题,请参考以下文章