0
点赞
收藏
分享

微信扫一扫

C变量本质

雷亚荣 2022-01-22 阅读 105
c语言

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)浮点类型的运算速度较慢
————————————————

举报

相关推荐

0 条评论