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

指针地址与取值运算符

优质
小牛编辑
140浏览
2023-12-01
///01.地址.c
#include <stdio.h>
#include <stdlib.h>

int get()
{
    return 10;
}

//01.严格进行变量区分:
//  1.普通变量和指针变量
//  2.严格的变量类型:
//      常规类型+特殊类型
//02.对变量取地址的操作发起于寄存器当中
//  因此地址也生成于寄存器变量当中,C语言无法直接访问
//03.关于取地址符不能操作的两种情况:
//      &&intNum;-->C语言无法直接操作寄存器变量
//      &get();-->返回值存储与寄存器的(cache)区,或者程序的(备份内存),这两块儿内存都不可直接访问
int main01(void)
{
    int intNum;
    &intNum;//这里获取一个声明但没有初始化的普通变量的所属地址可以,但是如果是指针类型的变量就不行了
    //&&intNum;//&intNum这个表达式所代表操作执行于寄存器当中,在寄存器当中使用寄存器变量进行存储,因此C语言无法进行寄存器变量的操作
    //  而且&intNum所获得的数据来自于CPU-->寄存器-->代码区-->符号表中,因此不能通过取地址运算符进行操作(C语言相对于汇编语言的缺点)
    //&get();//get();的返回值,也就是函数的返回值可能存储于两个常见位置:寄存器的缓冲区(cache),应用程序的备份内存,因此返回值不可取地址

    system("pause");
}
///02.内容.c
#include <stdio.h>
#include <stdlib.h>

//01.取值运算符("*")与取地址运算符("&"):
//  1.星号("*")表示取值运算符,也就是根据指针访问内存块儿
//  2.与号("&")表示取地址运算符,也就是根据变量的内存实体获取变量的地址
//02.对指针变量使用取值运算符(星号:"*")的使用规律:
//  用原始指针类型的指针级别减去取地址运算符的个数;
//  就是当前所访问的具体变量(普通变量和指针变量)
//      结果为0-->普通变量
//      结果为1-->一级指针变量
//      结果为2-->二级指针变量         
//      结果为n-->N级指针变量
int main02(void)
{
    //*(001) = 1;//由于常量读取于代码区符号表,产生于CPU寄存器,因此C语言不能够直接进行访问
    int intVar = 10;
    *(&intVar) = 3;//星号(*)表示取值运算符,也就是根据指针访问内存区块儿
    printf("%d \n", intVar);

    system("pause");
}

//03.取值运算符(星号:"*")的操作对象实质必须是指针
//  注:*&intVar-->严格区分执行流程
int main03(void)
{
    int intVar = 10;
    *&intVar = 3;//*&这两个符号可以同时进行出现,操作流程为:取地址运算符(与号:"&")先进行取地址操作,再根据取值运算符(星号:"*")根据指针获取该指针所指向的内存实体|数据实体
    //**&intVar;//编译报错-->由于*&intVar之后的结果为普通变量类型(int)-->取值运算符的操作对象必须是指针类型,也就是含有(星号:"*")的类型
    printf("%d \n", intVar);

    system("pause");
}

//04.凡是涉及到的操作和运算的步骤都是由CPU在寄存器当中进行执行的,这里的强制类型转换属于"读取"并"转换"的操作
//  因此属于寄存器操作,要进行间接修改数据,必须得先获取到相应的指针,而且指针类型必须是可以修改的,这样就有可能涉及到
//  指针变量的类型转换操作
//05.*(int *)&num这段儿代码的详细解释:
//  0.这些符号都是用于对变量名进行操作的
//      注:对变量进行的操作实质其实是对内存实体(数据实体)直接进行操作
//  1.取地址运算符(与号:"&")最接近于变量名称,所以优先执行
//  2.强制类型转换操作(int *)次接近于变量名称,所以其次执行
//  3.取值运算符(星号:"*")再次接近于变量名称,所以再其次执行
int main04(void)
{
    const int num = 3;//在C语言中,Const关键字在处理普通变量的时候,只能够避免对变量的直接修改,但是避免不了间接修改(普通变量适用,指针变量稍有差别)
    //num = 1;//直接修改,编译报错
    (int)num;//对任何变量的强制类型转换操作,强制转换之后的数值产生于CPU的寄存器
    *(int *)&num = 10;//int *类型转换,这里要特别注意,const int需要被看做为整体的一个类型进行处理
    printf("%d \n", num);

    system("pause");
}

//06.关于空类型的指针与空指针的区分:
//  空类型的指针:
//      严格说是空类型的指针变量-->该指针变量没有具体类型,只是用于存储地址(没有类型含义)而已
//      因此不能直接对空类型的指针变量进行数据实体的访问操作,没有明确解析步长和解析方式
//  空指针:
//      就是用于标识指针变量没有存储任何有实际意义的地址-->空置的指针变量而已
//      实质形式:NULL-->(void *)0
//  区分:空类型的指针是左值,空指针属于右值
int main05(void)
{
    int intVar = 10;
    intVar = 3;
    void * pIntVar = &intVar;//空类型的指针多用于多线程的数据传送处理
    //printf("%d \n", *p);//不可以直接操作空类型的指针

    system("pause");
}

void change01(int * pNum)//副本机制,数组除外
{
    *pNum = 10;
}

int main06(void)
{
    int num = 20;
    change01(&num);
    printf("%d \n", num);

    system("pause");
}
///03.指针实战.c
#include <stdio.h>
#include <stdlib.h>

//01.如何快速确定一个变量的所属类型?
//  变量名称对称法(直接看声明变量的代码块儿,进行直观对称得出)
int main07(void)
{
    double db = 10.8;
    double dbl = 10.9;
    double * p = &db;
    printf("%d \n", sizeof(p));//指针变量-->类型(double *)-->占用4个内存字节
    printf("%d \n", sizeof(*p));//数据实体-->类型(double)-->占用8个内存字节
    *p = 10.6;
    printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 10.9 10.6
    p = &dbl;
    printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 10.9 10.9
    *p = 9.2;
    printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 9.2 9.2
    dbl = 2.1;
    printf("db = %lf, dbl = %lf, *p = %lf \n", db, dbl, *p);//10.6 2.1 2.1

    system("pause");
}
///04.指针副本机制.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

//01.函数形式参数的副本机制:
//  1.在C语言当中,函数形式参数传参的副本机制只有数组例外,因为提升效率,节约内存拷贝
//  2.跨函数改变变量(普通变量|指针变量)的传参要素
//      被掉函数修改主调函数当中的普通变量,需要普通变量的地址
//      被掉函数修改主调函数当中的指针变量,需要指针变量的地址
//  注:总之在被掉函数当中修改主调函数的变量,无论是普通变量还是指针变量,必须传递变量的地址
//      否则绝对不行!!!
//void change02(int a, int b);//副本机制,被掉函数需要修改主调函数当中的变量,需要由主调函数向被掉函数传递该待修改变量的所属地址
void change02(int * pa, int * pb)
{
    printf("change02:&pa = %p, &pb = %p \n", &pa, &pb);
    //int * pTemp;//某些编译器会编译不通过!
    int * pTemp = pa;
    pa = pb;
    pb = pTemp;
}

//02.区分指针变量和指针:
//  指针变量:含内存块儿
//      用于存储指针的变量
//  指针:不含有内存块儿
//      是一个具有类型含义的地址
//      是一个具有类型含义的数值
//  注:使用指针变量的实质就是使用指针!
int main08(void)
{
    int a;
    int b;
    int * pa = &a, *pb = &b;
    //scanf("%d%d", &a, &b);//scanf();函数后面所需的是"指针"
    scanf("%d%d", pa, pb);
    printf("main: &pa = %p, &pb = %p \n", &pa, &pb);
    if (*pa > *pb)
    {
        change02(pa, pb);//间接修改无法成功
        int temp = a;//直接修改
        a = b;
        b = temp;
    }
    printf("a = %d, b = %d \n", a, b);

    system("pause");
}

int main09(void)
{
    int a;
    int b;
    int * pa = &a, *pb = &b;
    scanf("%d%d", pa, pb);
    if (*pa > *pb)//对应指向的数据实体
    {
        int * pTemp = pa;//交换指针
        pa = pb; 
        pb = pTemp;
    }
    printf("a = %d, b = %d \n", a, b);
    printf("pa= %d, pb = %d \n", *pa, *pb);

    system("pause");
}

void change03(int * p1, int * p2)
{
    printf("change03: &p1 = %p, &p2 = %p \n", &p1, &p2);
    int pTemp = *p1;
    *p1 = *p2;
    *p2 = pTemp;
}

void change04(int * p1, int * p2)
{
    printf("change04: &p1 = %p, &p2 = %p \n", &p1, &p2);
    //int * pTemp;//野指针:没有经过初始化的指针-->某些编译器直接编译报错
    int * pTemp = NULL;//使用空指针来标识指针变量没有指向任何位置
    *pTemp = *p1;//错误:访问了不该访问的地址0
    *p1 = *p2;
    *p2 = *pTemp;
}

int main10(void)
{   
    int a;
    int b;
    int * pa = &a, * pb = &b;
    scanf("%d %d", pa, pb);
    if (*pa > *pb)//对比指向的数据实体
    {
        //change03(pa, pb);
        change02(*pa, *pb);//函数传递数据,传递的都只是副本而已
    }
    printf("a = %d, b = %d \n", a, b);
    printf("*pa = %d, *pb= %d \n", *pa, *pb);

    system("pause");
}

void change05(int * p1, int * p2)
{
    printf("change05: &p1 = %p, &p2 = %p \n", &p1, &p2);
    int * pTemp = p1;
    p1 = p2;
    p2 = pTemp;
}

//03.无论是指针变量的操作还是对指针的操作:
//  都必须明确其类型
int main11(void)
{
    int a;
    int b;
    int * pa = &a, *pb = &b;
    scanf("%d%d", pa, pb);
    printf("main: &pa =%p, &pb = %p \n", &pa, &pb);
    printf("main pa = %p, pb= %p", pa, pb);

    system("pause");
}