专业的编程技术博客社区

网站首页 > 博客文章 正文

C++|指针变量的己型、己址、己值,他型、他址、他值

baijin 2024-08-10 13:38:33 博客文章 17 ℃ 0 评论

变量是一段内存单元首地址的命名(寄存器变量另说), 有三个维度:型、址、值。

型,也就是类型(type),决定了它需要内存空间的大小,它的编码规则(如原码、补码、IEEE 754标准的浮点数编码、ASCII、unicode等字符编码),值空间,运算规则等。

址,也就是地址(address),数据、代码都要存储到内存空间,可以被寻址。

值,也就是数据值(value),地址所指示的一段内存空间,按其类型所指示的长度和编码规则,对其全部的二进制位所解析出的值。

指针变量也是变量,具有上述变量所说的型、址、值,但他的特殊性在于是一指针变量,是一指向他处的变量,存储的是一地址值,其特点就是“我中有他”,所以除了己型、己址、己值以外,还蕴涵有他型、他址、他值的内容。

其中己值就是他址,看以下简单示例:

#include <iostream>
using namespace std;
#include <typeinfo>
int main()
{
    int t = 31;
    int* p = &t; // 己址、己值、他址、他值、他型、己型

    cout << "己址:" << &p << endl;
    cout << "己值:" << p << endl; // 或&t,己值就是他址
    cout << "他址:" << p << endl; // 或&t,他址就是己值
    cout << "他值:" << *p << endl; // 解引用
    cout << "他型:" << typeid(*p).name() << endl;
    cout << "己型:" << typeid(p).name() << endl;
	
    cin.get();
	  return 0;
}
/*
己址:0012FF40
己值:0012FF44
他址:0012FF44
他值:31
他型:int
己型:int *
*/

指针变量除了可用于申请堆空间,构造链式存储等数据结构等功能以外,还可以用于传址。

C++语言中的函数都有一段自己独属的内存空间,称为栈帧。如果主调函数给被调函数传值,前者传完值后,彼此互不影响。如果是传址就不一样了,被调函数因为拿到的是主调函数的地址,便有机会去操作这个地址对应的内存空间了。

区分了己型、己址、己值,他型、他址、他值的概念,便可以轻松领会传址的一些细微之处:

void func(int* p)
{
    int i = 44;
    *p = i;     // 左值是他值(p的解引用)才可以影响到他
    p  = &i;    // 左值是己值(或他址)影响不到他(因为p没有解引用),改变的是自己
}

分析以下代码:

void GetMemory0(char *p, int n)
{
    p = new char[n]; // 左值p这里是己值,指向一段堆空间,对主调函数没有影响
                     // 左值解引用p(*p)才可以影响p对应的值

    delete[] p; // 按成对编码原则,需要在主调函数中释放p并置NULL
}   // 此函数一旦被调用,p因为在栈空间,会被释放,主调函数将没有机会去delete[] p

void Test0(void)
{
    char *str = NULL;
    GetMemory(str,100);         // str还是NULL,同时内存泄漏
    
    strcpy(str, "hello world"); // 复制给NULL属非法操作
    printf(str);
    delete[] str;               // delete[] 操作空指针也是错误
    str = NULL;
}

当指针变量用做函数参数时,如果想通过被调函数修改主调函数的值,需要在函数体中解引用做为左值。一个什么样的变量解引用后还是一个指针?那就是二级指针:

void GetMemory(char **p, int num)
{
    *p = new char[num]; // 左值是p的解引用,是他值

    //delete[] p; // 按成对编码原则,需要在被调函数中释放并赋值NULL
}

void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100); // 实参是一个二级指针
    strcpy(str, "hello");
    printf(str);
    delete[] str;
    str = NULL;
}

当然也可以是指针引用,因为引用是一个由解译器实现了解引用的常量,一个指针类型的常量:

void GetMemory2(char *&p, int num) 		 //p引用一个指针char*, 
{
	   p = new char[num]; // 可以被返回地址的函数赋值
}
void Test2(void)	
{	
	   char *str = NULL;	
	   GetMemory2(str, 100);	// 注意参数是str,而不是&str
	   strcpy(str, "hello");	
	   cout<< str << endl;	
	   delete[] str;	
     str = NULL;
}

引用因为由编译器实现了对指针的解引用,所以在函数体中无需再解引用,直接使用,所以更简洁。

对于上述实例,也可以返回一个指针:

char *GetMemory3(int num)
{
    char *p = new char[num];
    return p;
}
void Test3(void)
{
    char *str = NULL;
    str = GetMemory3(100); 
    strcpy(str, "hello"); 
    cout<< str << endl; 
    delete[] str;
    str = NULL;
}

ref

http://mallocfree.com/interview/c-13-leak.htm

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表