当前位置: 首页 > 文档资料 > C 语言程序设计 >

变量和常量

优质
小牛编辑
141浏览
2023-12-01

变量数据类型的作用

  • 程序员写代码时识别用:知道变量中应该存放什么类型的数值
  • 给编译器看的:说明数值在存储时需要的内存空间字节数;说明存储结构

普通变量数据类型转换的本质

将数据的空间大小和数据的存储结构转变后,存入另一个变量空间

数据类型转换会导致数据存储空间大小和存储结构的变化

变量的本质

变量三要素:名称、大小、作用域

既能读又能写的内存对象,称为变量;若一旦初始化后不能修改的对象则称为常量。

程序通过变量来申请和命名内存空间 int a = 0

通过变量名访问内存空间,变量是(一段连续)内存空间的别名(是一个门牌号)

修改变量:直接修改;间接修改,内存有地址编号,拿到地址编号也可以修改内存

总结:

  • 对内存,可读可写
  • 通过变量往内存读写数据
  • 不是向变量读写数据,而是向变量所代表的内存空间中写数据
void main44()
{
    int a ;
    int b; 
    char *p ;
    //p = 0xaa11
    a = 10; //1 直接赋值  //cpu里面执行

    printf("&a: %d\n", &a);

    //2间接赋值 ==直接通过内存
     *((int*)1245024) = 200;

     printf("a: %d\n", a);

     {
        p = 1245024;  // 间接赋值 通过指针
        *p = 300;
     }

     //3 对内存空间能不能在取别名
    //1245024

    printf("hello...\n");
    system("pause");
    return ;
}

变量内存原理

#include <stdio.h>

int main01(void)
{
    int a = 10;
    printf("%p \n", &a);//查看变量a的内存地址
    printf("%d", a);

    system("pause");
    return 1;
}

//01.变量表的概念:
//  (1).在声明变量之前的时候,系统会为应用程序建立一张变量表
//  (2).所有在应用程序当中声明的变量都会被注册到变量表当中
//  (3).凡是需要使用到变量的时候,都会去变量表当中查找该变量
//      如果该变量被注册了,并且初始化了,则可以进行使用,否则不行
//          如果没有被初始化,将会读取到一个被编译器赋予默认垃圾值的数据
//          操作系统默认情况之下是不会自动清空内容当中的垃圾数据的
//          编译器检测到为初始化的变量,就会为变量赋予一个编译器指定的垃圾值
//              负号(-)表示垃圾,数值过大表示异常数据
//      如果该变量没有被注册,也就不能使用,编译报错
int main02(void)
{
    int a, b, c;//建立变量表,管理所有的变量

    //不能引用未曾声明的变量,也就是变量表当中的变量
    //printf("%d \n", d);

    system("pause");
    return 1;
}

//02.在寄存器当中所执行的操作:
//  (1).常量的生成:
//      1).这里的常量不是const常量(常变量),本质还是变量,表象为常量,处于内存当中
//      2).该常量处于代码区的符号表当中,生成流程为
//          (1.CPU读取代码区符号表当中的常量
//          (2.在CPU的寄存器当中生成该常量
//  (2).所有的运算操作
//  (3).所有的赋值操作
int main03(void)
{
    int a = 1;
    int b = 2;
    int c;
    printf("a = %p, b = %p, c = %p", &a, &b, &c);

    c = a + b;

    system("pause");
    return 1;
}

//03.赋值操作的特点:
//  (1).赋值的对象必须是变量(C语言)
//      汇编语言的赋值对象可以是寄存器
//  (2).变量都是处于在内存当中,也就是所谓的操作内存
int main04(void)
{
    int a = 3;//初始化:第一次的赋值操作,只能给变量进行赋值操作,变量处于内存当中
    //a + 1 = 4;//a+1处于寄存器当中,C语言没有操作权限,汇编语言具备操作权限

    int b = 0;
    printf("%p", &b);

    b = a + 1;
    b = a + 2;

    system("pause");
    return 1;
}

//03.所有的常量使用特点:
//  (1).所有的常量起源于代码区当中的符号表
//  (2).生成于CPU的寄存器当中
//  (3).C语言不具备操作寄存器的特权,所有无法采用取地址符进行操作
//      C语言当中可以采用内嵌汇编技术
int main05(void)
{
    int a;
    printf("%p \n", &a);
    //变量赋值,都是在寄存器当中的完成

    //a = 10;//代码区当中的常量符号表10,100
    //printf("%p", &10);//10这个常量在被操作的时候,其实是出于在寄存器当中
    //由于C语言不具备操作寄存器的特权,所以取地址符无法进行操作

    //汇编语言解释特点:
    __asm
    {
        mov eax, 10//将代码区符号表当中的常量移植到寄存器当中
        mov a, eax//在寄存器当中完成赋值操作,用于将寄存器当中的常量赋值给内存当中的变量
    }
    printf("a = %d \n", a);

    __asm
    {
        mov eax, a//将内存当中变量a的数据读取到CPU的寄存器当中
        add eax, 5//将寄存器当中读取到的数据与从代码区符号表当中移植进寄存器的数据进行加法操作
        mov a, eax//将寄存器当中进行加法操作之后的最终数据赋值给内存当中的变量a
    }
    printf("a = %d \n", a);

    system("pause");
    return 1;
}

变量为何一定要初始化

#include <stdio.h>
#include <stdlib.h>

//01.变量为何一定要初始化?
//  1.使用未初始化的变量特点:
//      (1).声明变量的含义:
//          1).软件程序向操作系统申请内存使用权限
//          2).操作系统为软件程序赋予内存使用权限
//      (2).操作系统只会负责内存使用权限的分配
//          操作系统不会对内存数据进行清理
//          编译器会检测是否对变量进行初始化,如果
//          没有进行初始化,那么分编译器特点:
//          VC6.0会赋予一个垃圾数值(负的很大值),定值
//          VS2015会赋予一些随机值,不定值
//      (3).所谓的内存释放特点:
//          并不是清空内存当中的数据,而是软件程序
//          将对某块儿内存的使用权限回交给操作系统
//  2.不进行初始化,就会使用到前期程序使用内存之后
//      遗留下来的垃圾值,或者编译器指定的垃圾值
int main01(void)
{
    int num;
    num += 1;

    //error C4700: 使用了为初始化的局部变量 “num”
    //新老编译器,新编译器提示错误,老版本不会进行提示
    //垃圾数据

    system("pause");
    return 1;
}

变量交换

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int a = 10;
    int b = 5;

    printf("a = %d, b = %d \n", a, b);

    /*
    //01.这种方式不行:
    //  原因:数据在第一次赋值的时候,就已经发生了数据丢失
    //      变量a的数据完全丢失了,找不回来了,当时有效数据只有变量b
    a = b;
    b = a;
    */

    //02.采用水桶原理进行变量数据交换:
    //  时空复杂度评析:
    //      temp让空间复杂度+1
    //      三次赋值操作让时间+3
    //  缺点:
    //      1.数据容易越界
    //      2.非位运算,效率低下
    int temp = a;//temp=10,a=10,b=5
    a = b;//a=5,b=5,temp=10;
    b = temp;//a=5,b=10

    //03.复杂方式,采用a+b记住变量数据原理:
    //  缺点:容易导致数据越界,非位运算效率低下
    a = a + b;
    b = a - b;
    a = a - b;

    //04.最佳方式采用位运算(位移或):
    //  优点:
    //  1.位运算效率高
    //  2.不会出现数据越界
    // 3.没有增加额外的空间复杂度
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;

    //05.变量数据交换总结:
    //  (+ - * /)会导致数据越界,最安全的做法是采用异或
    printf("a = %d, b = %d \n", a, b);

    system("pause");
    return 1;
}

常量

#include <stdio.h>
#include <stdlib.h>

//01.常量内容解析:
//  1.常量深度解剖:
//      宏常量(#define)是真正意义上的常量
//          由于其不具备变量层意义,所以一旦定义就不可修改
//      常变量(const)不是真正意义上的常量
//          常变量内部具有变量的含义,加上const
//          只能限制直接意义上的赋值,而不能限制间接意义上的赋值
//  2.注意地址类型的区别:
//      (1).常量地址和变量地址的特点
//      (2).内容的可修改性
//          常量地址所指向的变量不可进行修改
//          变量地址所指向的变量可以进行修改
int main01(void)
{
    const int 吴伟的颜值 = 99;

    //吴伟的颜值 = 67;不能直接进行修改
    printf("%d \n", 吴伟的颜值);
    printf("%p \n", &吴伟的颜值);

    //星号(*)用于根据地址取出指针指向的变量内容
    //(const int *)常量地址类型转化为变量地址类型
    //方可根据该地址所指向的变量当中的数据
    *(int *)(&吴伟的颜值) = 98;//不算真正意义上的常量
    printf("%d \n", 吴伟的颜值);

    system("pause");
    return 1;
}

//02.宏常量内容分析:
//  1.所有预编译指令(井号"#"作为开头的指令),都没有分号作为使用结尾
//  2.宏常量的使用特点:
//      (1).真正意义上的常量
//      (2).修改特点:
//          只有在源头上面进行宏常量的修改
//          后期既不能直接修改宏常量也不能间接修改变宏常量
//      (3).软件可拓展性的进行使用
//  3.编程语言的使用特点:
//      C语言操作硬件的级别只能到达内存一级
//      汇编语言操作硬件的级别可以达到寄存器一级
#define 吴伟的颜值 98
//define不能加分号(";"),加了就会被一起作为整体进行替换
//意义明确,实现批量修改,软件可拓展一点
int main02(void)
{
    printf("%d \n", 吴伟的颜值);
    //吴伟的颜值 = 100;//对宏常量进行再次赋值,直接报错
    //真正意义上的常量,一次赋值,永远不能直接赋值,也不能间接赋值
    //  只能从源头进行赋值操作
    //C语言能操作内存,不能操作寄存器
    //汇编语言同时干两个活儿

    system("pause");
    return 1;
}

void go()
{
    printf("%d \n", 吴伟的颜值);
}