指针地址与取值运算符
优质
小牛编辑
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");
}