如果有错误,欢迎指正,一起学习吧!
第二章数据与运算
2.1数据类型
定义:是计算机中能够表示的一类数据的集合。
它包含4个方面的信息:
- 这种数据在存储时用几个字节;
- 每个存储元中存储的信息代表的含义,即存储形式;
- 能够表示的数据范围;
- 这种数据能够进行什么样的处理(运算)。
均在VC环境下的表示范围
C语言的数据类型
1. 基本的:
类型名称 | 类型标识符 | 字节数 | 范围 | 存储形式 | 运算 |
---|---|---|---|---|---|
带符号字符型 | [signed] char | 1 | -128~127 | ASCII码 (在内存中以二进制表示) | 不好细分 |
无符号字符型 | unsighed char | 1 | 0~255 | ASCII码 (在内存中以二进制表示) | 不好细分 |
带符号短整型 | [signed] short [int] | 2 | -32768~32767 | 补码 | 算术,逻辑,比较,自增和自减 |
无符号短整型 | unsignedshort [int] | 4 | 0~65535 | 补码 | 算术,逻辑,比较,自增和自减 |
带符号整型 | [signed] int | 4 | -2147483648~2147483647 | 补码 | 算术,逻辑,比较,自增和自减 |
无符号整型 | unsigned[int] | 4 | 0~4294967295 | 补码 | 算术,逻辑,比较,自增和自减 |
带符号长整型 | [signed] long [int] | 4 | -2147483648~2147483647 | 补码 | 算术,逻辑,比较,自增和自减 |
无符号长整型 | unsigned long[int] | 4 | 0~4294967295 | 补码 | 算术,逻辑,比较,自增和自减 |
单精度实型 | float | 4 | 6~7位有效数字,约10-38~1038 | IEEE754 | 算术(不包括取模),逻辑,比较,自增和自减 |
双精度实型 | double | 8 | 14~15位有效数字,约10-308~10-308 | IEEE754 | 算术(不包括取模),逻辑,比较,自增和自减 |
这个表中"[ ]"表示写该类型时,这一部分可以省略。例如,signed int 可以简写成 int 。除非为了强调,类型名称中“带符号”通常省略,例如“带符号整型"通常简称“整型”,反之,类型名称中没有“带符号”、“无符号”字样都表示省略了“带符号”。
2. 枚举类型:
枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。可以说枚举型是预处理指令#define的替代。
定义一个枚举类型,需要使用 enum 关键字。
第一步: 声明枚举类型:
enum 枚举名 {枚举元素1,枚举元素2,……};
- 枚举名是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项;
- 枚举元素是一些命名的整型常量,元素之间用逗号,隔开;
- 枚举元素一般看成 整型 (字节数 , 范围 , 存储形式 , 运算跟整型类似)(在C++中自增,自减是不允许的) ;
- 枚举元素值的大小一般默认从0开始,可以自定义枚举元素的大小,后面的值若没有指定则其值为前一元素加 1;
eg:
enum season
{
spring, summer=3, autumn, winter
};
没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5
第二步: 定义枚举变量
有三种方式来定义枚举变量
- 先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;//变量day的类型为枚举型enum DAY
- 定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;//变量day的类型为枚举型enum DAY
- 省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;//变量day的类型为枚举型enum DAY
可以说枚举与#define 宏有些类似,但两者之间还是有些区别的:
- #define 宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
- 一般在编译器里,可以调试枚举常量,但是不能调试宏常量。
- 枚举可以一次定义大量相关的常量,而#define 宏一次只能定义一个。
3. void类型:
在C语言中,void类型具有特殊的用途,并不表示一个具体的数值类型。因此没有具体的字节数;没有值域;不对应于内存中的任何特定存储形式;不能对void类型执行算术或逻辑运算。
-
对函数返回的限定,这种情况我们比较常见。
-
对函数参数的限定,这种情况也是比较常见的。
一般我们常见的就是这两种情况:
当函数不需要返回值值时,必须使用void限定,这就是我们所说的第一种情况。例如:void func(int a,char *b)。
当函数不允许接受参数时,必须使用void限定,这就是我们所说的第二种情况。例如:int func(void)。
- void指针:void指针可以指向任意类型的数据,就是说可以用任意类型的指针对void指针对void指针赋值。
eg:
int *a;
void *p;
p=a;
如果要将void指针p赋给其他类型的指针,则需要强制类型转换,就本例而言:a=(int *)p。
4. 派生类型:
包括数组类型、指针类型和结构体类型。
数组类型和结构类型统称为聚合类型。函数的类型指的是函数返回值的类型。派生类型会在后边几个文章中进行讲解。
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。为了得到某个类型或某个变量在特定平台上的准确大小,可以使用 sizeof 运算符,表达式 sizeof(type) 得到对象或类型的存储字节大小。
2.2常量
常量的定义:
常量就是固定的值,在程序运行期间从开始到结束都不会改变的值,并且一旦定义好了以后,在程序中任何情况都是不能修改的。
常量的类型:
整型常量,实型常量,字符型常量,字符串常量,枚举常量。
(我只选取我觉得重要的部分进行讲解)
实型常量:
即实数,有日常写法和指数写法两种书写形式(即科学计数法)。其类型默认为 double型,若需要表示成 float ,可以使用后缀 F 或 f (eg:3.0f表示float类型的实数)。
日常写法:
即日常写十进制小数的写法。 eg: 3.2
指数写法:即科学计数法,通过例子来讲解:
eg:
1.763x1018在C语言中写成1.763e18,e不区分大小写,e前面可以是整数或者小数,正数、负数,但不能忽略;e后面可以是正数或者负数,但必须是整数。
字符型常量:
书写方式是将单个字符写在一对单引号之间,eg :‘a’,‘A’,‘!’ 等。编译程序在翻译源程序时,把字符型常量翻译成该字符对应的ASCII码。
但是一些特殊的字符不能直接像上面例子那样写出来,我们给这类字符取了一个名称叫转义字符。
常用的转义字符表:
转义符 | 含义 | ASCII |
---|---|---|
\n | 换行 | 10 |
\r | 回车 | 13 |
\b | 退格 | 8 |
\t | 水平制表符 | 9 |
\f | 换页 | 12 |
\\ | 反斜线\ | 92 |
\' | 单引号' | 39 |
\" | 双引号" | 34 |
\ooo | ooo是1~3位八进制数 | \ |
\xhh(x不区分大小写) | hh是1~2位十六进制数 | \ |
使用方法:
- 使用ASCII码:
char a=10;
printf("h%chhhhhhhhhh",a);
int a=10;
printf("hhh%chhhhhhhh",a);//强制转换
输出效果:
h
hhhhhhhhhh
hhh
hhhhhhhh
- 直接使用转义符
printf("hhhhhhhhhh%nhhhhh");
输出结果:
hhhhhhhhhh
hhhhh
- 使用进制表示:
//已知 \n 的ASCII码为10 用十六进制表示就是:\xa 用八进制表示: \12
printf("hhhhhhhhhh\xahhhhh"); //用十六进制表示
printf("hhhhhhhhhh\12hhhhh"); //用八进制表示
输出结果:
hhhhhhhhhh
hhhhh
hhhhhhhhhh
hhhhh
字符串常量:
用双引号 " " 括起来的部分。
字符串常量所占内存字节数比字符串字符个数多了1个字节,因为C语言的格式字符串在后面由编译系统自动加了一个结束标志位’\0’。但’\0’并不是字符串的一部分,它只作为字符串的结束标志。字符串常量通常要用字符数组来存放。
eg:
char s[]="abc\0de";
//有6个字符,分别是:1. a ;2. b ;3. c ;4. \0 ;5. d ;6. e
//所占内存字节数为 7 因为结尾 有一个 \0 可以用sizeof()来验证;
printf("%d",sizeof(s));
输出结果:
7
但是注意,字符串的实际字符个数到 \0 截至 (也是字符串的长度)
因此
char s[]="abc\0de"; //实际上包含三个有效字符:a、b 和 c
printf("%d",strlen(s));//使用 strlen 函数计算字符串长度时,只会计算到第一个 \0 字符为止
输出结果:
3
定义常量:
有4种方法:
- 使用 #define 预处理器指令:
预处理器指令 #define 可以用来定义宏常量,这种方法简单但有一些限制,比如不能定义类型,因此编译器不会检查类型错误。宏常量可以在任何地方被定义,但通常放在文件的开头并且会将名称大写以区分于普通变量
eg:
#define PI 3.14159
- 使用 const 关键字:
使用 const 关键字可以定义具有特定类型的常量,这使得编译器可以检查类型错误。
eg:
const double PI = 3.14159;
- 使用 #define 和 const 结合:
有时,为了保持代码的清晰度和一致性,可以在定义常量时同时使用 #define 和 const,通常用于大型项目中保持代码风格的一致性。
#define PI (3.14159)
const double pi = PI;
- 使用枚举类型:
枚举类型可以用来定义一组相关的常量。
enum color {
RED = 1,
GREEN,
BLUE
};
注意: 枚举常量的不可修改不是强制性的,可以按照上文进行人为修改
2.3变量
变量是程序运行过程中存储数据的存储单元。在程序中定义一个变量就是命令计算机在执行这个程序时安排一个存储单元。根据不同的标准可以把变量划分为不同的类型。
- 按照变量能够存储的数据类型可以把变量分为:字符型变量,整型变量,实型变量,指针型变量等;
- 按照用做变量的存储区域可以把变量分为:寄存器变量,自动变量,静态变量;
- 按照变量的有效范围可以把变量分为:全局变量,局部变量。
2.3.1变量的存储类别:
-
存储数据的区域:
内存,外存,CPU中的寄存器。寄存器容量最小,访问速度极快;内存容量比寄存器大,访问速度比寄存器慢;外存容量最大,但外存的数据必须调进内存后才能进行处理。程序中定义的变量总是用寄存器或者内存作为存储单元。
计算机系统的内存是由操作系统管理的,操作系统在把一个程序调进内存准备运行时,一般需要在内存中划出三块区域供程序使用:一块存放程序各语句编译后的机器代码,称为程序区;一块作为系统堆栈(“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。),称为动态存储区;还有一块是静态数据存储区。三块区域在整个内存中的具体位置由操作系统决定。 -
指定变量的存储类别:
定义变量的基本格式:类型 变量名
定义变量并初始化:类型 变量名 = 初始值
“类型”是指数据类型,确定变量中能够存放什么类型的数据。这种定义变量的方式没有指出是把变量安排到寄存器,动态存储区还是静态存储区。如果要明确指定变量的存储区域,则需要在定义变量时用register(寄存器型,安排在寄存器),auto(自动型,安排在栈中),或者static(静态型,安排在静态区)指定变量的存储类别,写成如下形式:
(1)系统默认存储类别是auto
(2)全局变量只能安排在静态区中,并且通常省略static不写;
(3)函数的形式参数只能安排在栈中,并且不允许写auto;
(4)定义局部变量没有写存储类别,则是省略了auto;
(5)只能把少量、多次反复使用的整数类型变量安排在寄存器中。
(6)静态变量若没有初始值,则默认为0,并且规定除了定义该变量的函数外,其他函数不能直接访问这个变量。
学习存储类别的目的是想知道变量最终被安排在哪里了,没有必要过多的考虑究竟把变量安排到哪一个存储区去。
2.3.2 全局变量和局部变量
- 变量的有效范围:定义变量可以在①函数内部;②函数外部;③复合语句中。在函数外部的变量不属于任何一个一个函数特有,而是归整个程序所有,称为全局变量。在函数内部定义的变量称为局部变量,局部包括形式参数和在函数体内定义的变量,以及在复合语句内部定义的变量。有效范围是指对于一个已经定义的变量,在程序的哪些部分可以访问这个变量。全局变量从定义处往下直至程序结束一直有效,其后各个函数均可访问;局部变量仅在定义它的那个函数内部有效,复合语句内部定义的变量只在该复合语句内有效。
- 变量的同名现象:有效范围不同的两个变量可以同名。全局变量可以和局部变量同名,不同函数中的局部变量可以同名,一个函数中如果有复合语句,复合语句中定义的变量可以与函数声明部分定义的变量同名。但是全局变量相互间不允许同名;在函数声明部分的变量相互间不允许同名;函数内部(复合语句之外)定义的变量也不能与函数的形参同名;同一复合语句中定义的变量也不能同名。
2.3.3 定义变量
上面我们知道了如何简单定义一个变量,但定义一个变量还需要掌握变量命名规则以及一些细节上值得注意的事。
- 变量命名规则:
为变量命名的字符序列称为标识符,标识符用于标识程序中的各种对象。C语言规定:标识符的第一个符号必须是字母或下划线(_),其余字符可以是字母、数字或下划线。C语言对大小写敏感。C语言的关键字不能用做标识符。可以采用小驼峰命名法和大驼峰命名法。 - 定义变量:
定义变量的基本格式:
类型 变量名;
通俗地说,这句话告诉计算机:“给我安排一个名为××的存储单元,我打算用它存放××类型的数据。”
在定义多个变量时,同类型的变量可以用下面的格式在一行中定义:
类型 变量名1,变量名2,变量名3······
2.3.4 变量的初始化
带有初值的变量定义格式为:类型 变量名 = 表达式;
如果定义变量时不指定初始值,计算机有两种处理方式:一种是在安排内存时保留内存单元中原有的数据(随机值,每次运行结果都会不一样);另一种是把变量占据的存储单元的各个二进制位用0填充。定义局部变量时用前一种方式,定义全局变量则采用后一种情况。如果若干变量的初始值相同,则必须分别指定:
int a=0,b=0,c=0;
2.3.5访问变量与赋值语句
访问变量也称为对变量的引用或者对变量的使用,通常是指对变量的两种操作:操作之一是对变量赋值,就是把一个数据存放对应的内存单元中,通常简述成“把数据放到变量中”;另一个操作是从变量对应的内存单元中取出数据,简称从变量中取值。
赋值语句的功能是命令计算机算出“表达式”的值,并把计算结果存放到指定的变量中。“=”是赋值号,具有方向性,用于把右边的数据送往左边的变量。
2.4 常用运算符与表达式
运算符是命令计算机执行某种计算的专用符号。按功能分,其种类有算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和其他运算符。按参与运算的操作数的数量不同分,有一元运算符和二元运算符和三元运算符。除了知道一个运算符需要几个操作数外,还需要知道运算符的优先级和结合性。
运算符的优先级和结合性。:
C语言允许很多运算符同时出现在一个表达式中,这时必须对各个运算符的处理次序作出明确规定——C语言对每个运算符设定了一个整数值,称为该运算符的优先级,代表优先级的整数值越小,相应运算符的优先级越高。不同运算符进行混合运算时,优先级高者先处理。但是圆括号()可以改变按运算符优先级确定的计算次序。
C语言在规定运算符优先级时出现了把两个或两个以上的运算符划分在同一级别的现象。当同一级别的运算符在表达式中连续出现时,有些运算符按从左向右的次序处理,这一类运算符称为是“左结合”的;也有一些运算符按从右向左的次序处理,这一类运算符称为是“右结合的”。
eg:
int x=10,y=2;
x / y * 5 % 7 //结果为4,x/y=5,5*5=25,25%7=4
1. 算术运算符:+,-,*,/,%(取模运算符)。“ / ”用于计算整数除以整数求商(没有小数部分)或者实数除以实数;“ % ”用于计算整数除以整数求余数,即取模运算符(只能用于整型数据,不应用于实型数据,并且结果余数的符号和被除数一致)。都是二元运算符,但+,-也可以作为一元运算符,改变数据(+没什么意义,但-表示取负数)。(左结合)
eg:
int a=-26,b=10;
a%b; //结果是-6,余数的符号和被除数一致
a%-b;//结果是-6,余数的符号和被除数一致
a%(-b);//结果是-6,余数的符号和被除数一致
注意:字符型数据可以作为数值计算的操作数。如果一个数值计算的表达式中出现字符型数据,意味着用该字符的ASCII码值(是一个整数)进行相应的计算。当字符型数据与整数或者一起进行运算时,系统会自动把字符型数据转换成占据存储字节较多的整数或者实数。
2. 关系运算符:==(判断是否相等),!=(判断是否不等),> ,<,>=(判断是否大于或等于),<=(判断是否小于或等于)。(左结合)
由于C语言中没有布尔值,关系运算符返回的结果为整型0和非0数字。C语言遵循非0即真的原则:0表示假,非0表示真。均为二元运算符。
3. 逻辑运算符:&&(逻辑与运算符。如果两个操作数都非零,则条件为真),||(逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真),!(逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假)。逻辑运算符遵守非0即真的原则。逻辑运算符 && 和 || 具有短路行为。这意味着在 a && b 中,如果 a 为假,则不会评估 b;在 a || b 中,如果 a 为真,则不会评估 b。除了“ !”为一元运算符以外,其他均为二元运算符。(左结合)
4. 位运算符:位运算符作用于二进制位,并逐位执行操作。& 按位与 ,| 按位或,^ 按位异或
,~取反,>>右移,<<左移。除了“ ~ ”为一元运算符以外,其他均为二元运算符。位运算符操作数只能为整型和字符型数据。(左结合)
5. 赋值运算符:“ = ”把右边操作数的值赋给左边操作数。提供数据类型的自动转换(二元运算符)(右结合)
数据类型的自动转换和强制转换:
自动转换:
C语言的二元运算符一般都要求参与运算的操作数是同一类型的。但如果参与运算的数据类型不一致,编译程序在翻译相应的计算机命令时,会添加机器指令将数据类型进行一定的转换,然后再进行运算。转换的原则简略地说就是把数据由占用字节较少的类型转换成占用字节较多的类型。另外,通常CPU不支持float类型的数值计算,而只有double类型的计算,因此即使算术运算符的两侧都是float类型的数据,也会先被自动转换为double类型再进行计算。
强制转换:
格式:(类型)(表达式)
强制转换是一种一元运算,不论表达式的计算结果是什么类型,计算机将把结果数据转换为指定的类型。可以用于把数据由占用字节较多的类型转换成占用字节较少的类型,这会采用“截断式”转换形式。“截断式”转换形式是指把占据字节数多的整数赋给占据字节数少的变量时,将按照整数的二进制表示从右边取与变量等长的位数存放到变量中。
eg:
int x=1234567;
short int y;
y=(short int)x;//也可以直接写成y=x,在后面将赋值运算符支持的数据转换会讲到
已知x占据4字节有32位,对应的二进制表示为
而y占据2字节,因此赋值操作只取上述二进制数的右边16位存到变量y中,y对应的二进制表示为
所以输出结果为:
-10617
可见,截断式转换是有损转换,转换后的数值可能与原数值不相等
ok,我们已经知道了数据类型的转换方式,赋值运算符也支持数据类型的转换。可以把数据由占用字节较少的类型转换成占用字节较多的类型,也可以把数据由占用字节较多的类型转换成占用字节较少的类型。
int a=4;
double b;
b=a;//把数据由占用字节较少的类型转换成占用字节较多的类型
int x=1234567;
short int y;
y=x;//把数据由占用字节较多的类型转换成占用字节较少的类型
6. 其他运算符:
- ++(自增运算符),–(自减运算符)(两个符号之间不能有空格,一元运算符)
自增,自减运算符可以放在变量的前面,也可以放在变量的后面,不受运算符优先级的影响。自增在前通常说成“先加1后用”,即先把变量的值加1,然后再看下一步那个变量做什么处理;自增在后通常说成“先用后加1”,即先取变量的值作为下一步处理的数据,然后再把变量的值加1。自减与自增一样。
int x=1,y;
y=++x; //x=2,y=2
int x=1,y;
y=x++; //y=1,x=2
- 复合的赋值运算符:+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=(两个符号之间不能有空格,二元运算符)
使用方法为:
变量 复合运算符 表达式;
等效于
变量=变量 运算符 (表达式);
- 逗号运算符:,
格式:表达式1,表达式2,……,表达式n
在这个逗号表达式中,计算机将依次计算这n个表达式的值,并以最后一个表达式的值作为整个逗号表达式的值。
eg:
x=(a=1,b=2,c=3,a+b+c)//先处理a=1,b=2,c=3,最后将a+b+c=6的值赋给x
- 条件运算符: (判断条件) ? (判断为真时执行的语句) : (判断为假时执行的语句) 。(三元运算符)
- 字节数运算符:sizeof (一元运算符)
用于求变量占用内存空间的字节数,或者某种类型的数据在存储时需要的字节数。使用sizeof 有3种形式:
- sizeof 变量
- sizeof (变量)
- sizeof (类型)
不同的环境中,存储同一类型的数据需要的字节数可能有所不同。
2.5 常用的数学函数
函数 | 描述 | 举例 | 结果 |
---|---|---|---|
abs(x) | 求整数的绝对值,|x| | a=abs(-3); | 3 |
fabs(x) | 求实数的绝对值,|x| | y=fabs(-3.5); | 3.5 |
pow(a,x) | 求幂,ax | y=pow(2.0,2.5); | 5.65685 |
sqrt(x) | 求平方根,√x | y=sqrt(2.0); | 1.41421 |
exp(x) | 求ex | y=exp(-1.5); | 0.22313 |
log(x) | 求以e为底的对数,logex | y=log(1.0); | 0.00000 |
log10(x) | 求以10为底的对数,log10x | y=log10(100.0); | 2.00000 |
sin(x) | 求正弦值,sinx | y=sin(3.14159); | 0.00000 |
cos(x) | 求余弦值,cosx | y=cos(3.14159/3) | 0.50000 |
tan(x) | 求正切值,tanx | y=tan(3.14159/4) | 1.00000 |
asin(x) | 求反正弦值,arcsinx | y=asin(0); | 0.00000 |
acos(x) | 求反余弦值,arccosx | y=acos(0.5); | 1.04720 |
atan(x) | 求反正切值,arctanx | y=atan(1.0); | 0.78540 |
注意:数学公式中被省略的乘号在C语言中不可以省略。如果程序中使用了库函数,那么需要在程序开头用#include包含相应的头文件。包含上述表格内容的头文件为:#include<math.h>或者#include“math.h”