C语言中变量是程序可操作性的存储区域的名称,每个变量类型决定了变量的存储空间大小和布局。在C语言中,(1)变量是一段实际连续存储空间的别名;(2)程序中通过变量来申请并命名存储空间;(3)通过变量的名字可以使用存储空间。
类型 描述
char 通常是一个字节(八位), 这是一个整数类型。
int 整型,4 个字节,取值范围 -2147483648 到 2147483647。
float
单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数。
double
双精度浮点值。双精度是1位符号,11位指数,52位小数。
void
表示类型的缺失。
* 指针类型变量用来指定特定类型的变量地址,大小32位计算机固定是4字节,64位固定式8字节
C 语言也允许定义各种其他类型的变量,比如枚举、指针、数组、结构、共用体等等,这将会在后续的章节中进行讲解。
1. C 中的变量定义
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表,如下所示:
type variable_list;
在这里,type 必须是一个有效的 C 数据类型,可以是 char、w_char、int、float、double 或任何用户自定义的对象,variable_list 可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。下面列出几个有效的声明:
int i, j, k;
char c, ch;
float f, salary;
double d;
行 int i, j, k; 声明并定义了变量 i、j 和 k,这指示编译器创建类型为 int 的名为 i、j、k 的变量。
变量可以在声明的时候被初始化(指定一个初始值)。初始化器由一个等号,后跟一个常量表达式组成,如下所示:
type variable_name = value;
下面列举几个实例:
extern int d = 3, f = 5; // d 和 f 的声明与初始化
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。
2. C 中的变量声明
变量声明想编译器保证变量以指定的类型和名称存在,这样编译器再不需要知道变量完整细节的情况下也能继续编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。C语言中变量的存储空间分为栈和堆两种方式,其中全局变量存储在指定的静态堆空间,局部变量在栈中生成。变量声明有两种情况:
1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
除非有extern关键字,否则都是变量的定义。
extern int i; //声明,不是定义
int i; //声明,也是定义
我们知道,程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
然而,如果全局变量不在文件的开头定义,有效的作用范围将只限于其定义处到文件结束。如果在定义点之前的函数想引用该全局变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
3. C中左值和右值
C 中有两种类型的表达式:
左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:
int g = 20;
但是下面这个就不是一个有效的语句,会生成编译时错误:
10 = 20;
小结:
数据类型的本质是一个模子
(2)数据类型代表需要占用的内存大小
(3)变量的本质是一段内存的别名
(4)变量隶属于某一种数据类型
(5)变量所在的内存大小取决其所属的数据类型
4. 有符号与无符号
计算机的符号位:数据类型的最高位用于标识数据的符号(最高位为1——负数,最高位为0——正数)
#include <stdio.h>
int main()
{
char c = -5;
short s = 6;
int i = -7;
//判断最高位是否是0,0表示正数,1表示负数
printf("%d\n", ((c & 0x80) != 0 )); //1,按位与,结果非0为负数
printf("%d\n", ((s & 0x8000) != 0)); //0,为正数
printf("%d\n", ((c & 0x80000000) != 0)); //1,为负数
return 0;
}
4.1 有符号数的表示法
(1)在计算机内部用补码表示有符号数
①正数的补码为正数本身
②负数的补码为负数的绝对值各位取反加1
8位整数 5的补码为: 0000 0101
8位整数-7的补码为: 1111 1001
16位整数20的补码为: 0000 0000 0001 0100
16位整数-13的补码为: 1111 1111 1111 0011
4.2 无符号数的表示法
(1)在计算机内部用原码表示无符号数
①无符号数默认为正数
②无符号数没有符号位
(2)对于固定长度的无符号数
①MAX_VALUE+1 → MIN_VALUE
②MIN_VALUE-1 → MAX_VALUE
(3)signed和unsigned
①C语言中变量默认为有符号的类型
②用unsigned关键字声明变量为无符号类型——只有整数类型能够声明为unsigned(浮点数不能声明为无符号数)
int i; //默认为有符号整数;
signed int j; //显式声明变量为有符号整数;
unsigned int k ; //声明为无符号整数
【编程实验1】当无符号数遇上有符号数时→有符号会被看作无符号数!
#include<stdio.h>
int main()
{
unsigned int i = 5;
int j = -10;
if ((i + j) > 0) //j转换为无符号数,变成为大的正数
{
printf("i+j>=0\n"); //该结果为运行后最终的输出结果!
}
else
{
printf("i+j<=0\n");
}
return 0;
}
【编程实验2】错误地使用unsigned
int main()
{
unsigned int i = 0; //无符号数永远为正数,即最小值为0
//当i减小到为0时,然后再减1就变成无符号int型数的最大值,
//以下代码会进入死循环,所以不应用无符号数来作为循环变量!
for (i = 9; i >= 0; i--) //i为负数即退出循环,但i为无符号数,不可能为负数
{
printf("i=%u\n", i);
}
return 0;
}
4. 小结
(1)有符号数用补码表示
(2)无符号数用原码表示
(3)unsigned只能修饰整数类型的变量
当无符号数与有符号数混合计算时,会将有符号数转换为无符号数后再进行计算,结果为无符号数。
浮点数的秘密
1. 内存中的浮点数——存储方式:符号位、指数、尾数的符号
float与double类型的数据在计算机内部的表示法是相同的,但由于所占存储空间不同,分别能够表示的数据值范围和精度不同。
4.3 浮点数的存储示例
2.1 浮点数的转换步骤
(1)将浮点数转换成二进制
(2)用科学计数法表示二进制浮点数
(3)计算指数偏移后的值:(需加上偏移量,float型:+127,double型:+1023)
示例:对于指数6,偏移后的值如下:
float: 127 + 6→ 133
double: 1023 +6 → 1029
2.2 以float型的实数8.25在内存表示法为例,演示转换过程
(1)8.25的二进制表示法:1000.01,再转为指数形式:1.00001*(2^3)
①符号位:0
②指数为3: 127+3 =130 → 10000010
③小数:00001。 //要转为尾数,须在后面补0
(2)内存中8.25的float表示:0 1000 0010 000 01000000 0000 0000 0000 =0x41040000
(红色部分为符号位,指数为绿色,尾数为紫色部分加补零后的23位)
【编程实现1】十进制浮点数的内存表示
#include<stdio.h>
int main()
{
float f = 8.25;
//为了用16进制显示出浮点数的内存表示的二进制数值,可将那
//段内存以整数形式去显示那串二进制数,以便人类阅读
unsigned int* p = (unsigned int*)&f; //整型指针指向f的内存
printf("0x%08X\n", *p); //以整型输出0x41040000
return 0;
}
4.4 浮点类型的秘密
(1)思考:int和float都占4个字节的内存,而int类型的范围:[-231,231-1],float类型的范围:[-3.4*1038,3.4*1038],为float却比int的范围大得多呢?
(2)秘密
①float能表示的具体数字个数与int相同
②float可表示的数字之间是不连续的,存在跳跃
③float只是一种近似的表示法,不能作为精确数使用
④由于内存表示法相对复杂,float的运算速度比int慢很多
(3)注意:double与float具有相同的内存表示法,因为double也不是精确的。由于double占用的内存较多,所能表示的精度比float高。
【编程实现】float类型的不精确示例
#include<stdio.h>
int main()
{
float f1 = 3.1415f;
float f2 = 123456789;
//精确到小数点后面的10位
printf("%0.10f\n", f1);//3.1414999962 -->不精确
printf("%0.10f\n", f2);//123456792.0000000000 -->不连续
//同理可实验得,float型的123456780-123456788之间的在内存中都
//是用123456784.0000000000这个数表示的。
return 0;
}
小结
(1)浮点类型与整数类型的内存表示法不同
(2)浮点类型的内存表示更复杂
(3)浮点类型可表示的范围更大
(4)浮点类型是一种不精确的类型
(5)浮点类型的运算速度较慢
————————————————