21天学通C++——第八章

指针:指针是存储内存地址的变量。

使用指针存储内存地址、变量的地址(& + 类型)、解引用(*)三者密不可分。


关于sizeof()用于指针的结果

 #include <iostream>
using namespace std;

int main()
{   
    cout << "sizeof fundamental types -" << endl; 
    cout << "sizeof(char) = " << sizeof(char) << endl; 
    cout << "sizeof(int) = " << sizeof(int) << endl; 
    cout << "sizeof(double) = " << sizeof(double) << endl; 
    cout << "sizeof pointers to fundamental types -" << endl; 
    cout << "sizeof(char*) = " << sizeof(char*) << endl; 
    cout << "sizeof(int*) = " << sizeof(int*) << endl; 
    cout << "sizeof(double*) = " << sizeof(double*) << endl;
    return 0;
}

t

上述例子说明:将 sizeof( )用于指针时,结 果取决于编译程序时使用的编译器和针对的操作系统,与指针指向的变量类型无关。

指针的sizeof结果均是8字节,说明编译器是64bit,并且在64bit操作系统运行该程序。


动态分配内存:new、delete

使用 new 时,需要指定要为哪种数据类型分配内存;使用 new 分配的内存最终都需使用对应的 delete 进行释放

Type* Pointer = new Type;

delete Pointer;

为多个元素分配的内存:

Type* Pointer = new Type[numElements];

delete[] Pointer;

  1. 不再使用分配的内存后,如果不释放它们,这些内存仍被预留并分配给您的应用程序。

    这将减少可供其他应用程序使用的系统内存量,甚至降低应用程序的执行速度。这被称为内存泄露,应不惜一切代价避免这种情况发生。

  2. 运算符 new 和 delete 分配和释放自由存储区中的内存。自由存储区是一种内存抽象,表 现为一个内存池,应用程序可分配(预留)和释放其中的内存。


++、−−用于指针的结果

将指针递增或递减时,其包含的地址将增加或减少指向的数据类型的 sizeof。即:

Type* pType = Address;

则执行++pType 后,pType 将包含(指向)Address + sizeof(Type)。

#include <iostream>
using namespace std;

int main()
{   
    cout << "How many integers you wish to enter? " << endl; 
    int numEntries = 0;
    cin >> numEntries;

    int* pointsToInts = new int[numEntries];
    cout << "Allocated address:" << pointsToInts << endl;
    cout << "Allocated for " << numEntries << " integers" << endl;
    for(int i = 0;i < numEntries;++i)
    {
        cout << "Enter number " << ": ";
        cin >> *(pointsToInts + i);
    }
    
    cout << "Displaying all numbers entered: " << endl;
    for (int i = 0; i < numEntries; i++)
    {
        /* code */
        cout << *(pointsToInts++) << " ";
    }
    cout << endl;
    //After tranversing, pointer:
    cout << "pointsToInts:" << pointsToInts << endl;

    pointsToInts -= numEntries;
    //return to initial position
    cout << "pointsToInts:" << pointsToInts << endl;

    delete[] pointsToInts;
    
    return 0;
}

image-20230102104808373

分配的起始地址:0xeb4630;

在显示数据时,通过对指针三次++操作,结果是:0xeb463c。说明3次++地址增加了12,即每次++,指针变化sizeof(int)

最后指针回退到起始地址。


将const 用于指针

#include <iostream>
using namespace std;

int main()
{   
    //1.指针指向的地址不可变:
    int num1 = 20;
    int num2 = 10;
    int* const num1Point = &num1;
    cout << "num1:"<<*num1Point << "'s address(num1Point): " << num1Point << endl;
    //num1Point = &num2; 提示错误:表达式必须是可修改的左值
    //但是可修改该地址指向的内容
    *num1Point = 30;
    cout << "num1's value is(access by pointer) :" << *num1Point << endl;
    cout << "num1's value is(access by variable's name) :" << num1 << endl;

    //2.指针指向的变量内容不可变
    const int* num2Point = &num2;
    cout << "num2:" << *num2Point << "'s address(num2Point): " << num2Point << endl;
    //*num2Point = 30; 提示错误:表达式必须是可修改的左值
    //但是可以修改指针的指向
    num2Point = &num1;
    cout << "pointer num2Point's value: " << *num2Point << endl;

    //3.指针的指向和指针指向的内容均不可变
    int num3 = 100;
    const int* const num3Point = &num3;
    cout << "pointer num3Point: " << num3Point <<", its value: " << *num3Point << endl;
    /*
         修改指针指向:num3Point = &num1; 
        修改指针指向的内容:*num3Point = 123; 
    */
    
    return 0;
}

数组与指针的类似之处

数组变量是指向第一个元素的指针;

可将用于指针的解除引用运算符(*)用于数组,将数组运算符([])用于指针;

#include <iostream>
using namespace std;

int main()
{   
    int numarray[] = {1, 2, 3, 4, 5};
    int * p = numarray;
    cout << "numarray(array address):" << numarray << endl;
    cout << "numarray(pointer):" << p << endl;

    //1.对数组使用*
    for(int i = 0;i < 5;i++)
    {
        cout << *(numarray + i) << " ";
    }

    //2.对指针使用索引[]
    for(int i = 0;i < 5;i++)
    {
        cout << p[i] << " ";
    }
    return 0;
}

使用指针的注意事项

1.内存泄漏

new的内存空间使用完后,立即delete;

不要对同一个内存地址调用 delete 多次

2.指针指向无效存储空间

指针在声明时就要初始化

3.悬浮指针

初始化指针/释放指针后,立即设置为NULL;在对指针解引用前检查指针内容是否有效

#include <iostream>
using namespace std;

int main()
{   
    cout << "Is it sunny (y/n)? "; 
    char userInput = 'y'; 
    cin >> userInput; 

    bool* const pb = new bool;
    *pb = true;

    if(userInput == 'n')
    {
        *pb = false;
    }
    cout << "Boolean flag sunny says: " << *pb << endl;

    delete pb; 

    return 0;
}
  1. 声明pb指针时,立即初始化合法的内存地址
  2. 使用const关键字,使得pb的指向不可更改,但可以修改pb的指向的值
  3. pb指向的值初始化为true,增强程序输出的可读性(3和指针的使用无关)

4.检查new的请求是否满足

C++提供了两种确认指针有效的方法

默认方法是使用异常即如果内存分配失败,将引发 std::bad_alloc 异常。这导致应用程序中断执行,除非提供了异常处理程序,否则应用程序将崩溃,并显示一条类似于“异常未处理”的消息。

#include <iostream>
using namespace std;

int main()
{   
    try{
        int* pointsToManyNums = new int [0x1fffffff]; 
        delete[] pointsToManyNums;
    } 
    catch (bad_alloc) 
    { 
        cout << "Memory allocation failed. Ending program" << endl; 
    } 
    return 0;
}

方法2:new(nothrow),这个new的变种在内存分配失败时不引发异常, 而返回 NULL

#include <iostream>
using namespace std;

int main()
{   
    int* pointsToManyNums = new(nothrow) int [0x1fffffff]; 

    if (pointsToManyNums) 
    { 
       delete[] pointsToManyNums; 
    } 
    else 
    cout << "Memory allocation failed. Ending program" << endl;
    return 0;
}

引用

1.再谈引用

引用是变量的别名,即相应变量的另一个名字

声明引用时,需要将其初始化为一个变量,因此引用只是另一种访问相应变量存储的数据的方式。

引用的用法:

VarType original = Value;

VarType& ReferenceVariable = original;

#include <iostream>
using namespace std;

int main()
{   
    int num = 30;
    cout << "num(" << num << ")" \
            << ", its address is:" << &num << endl;
    
    int& ref1 = num;
    cout << "ref1's address:" << &ref1 \
            << ", ref1's value:" << ref1 << endl;
    
    int& ref2 = ref1;
    cout << "ref2's address:" << &ref2 \
            << ", ref2's value:" << ref2 << endl;
    return 0;
}

image-20230102130233559


2.将const 用于引用

作用:禁止通过引用修改它指向的变量的值

#include <iostream>
using namespace std;

int main()
{   
    int original = 30; 
    const int& constRef = original;
    //constRef = 31; 引用constRef不可修改
    /* int& ref2 = constRef;
        将 "int &" 类型的引用绑定到 "const int" 类型的初始值设定项时,限定符被丢弃C/C++(433)
    */
    const int& constRef1 = original;
    cout << constRef1; // 30
    return 0;
}

3.按引用向函数传递参数

引用传递函数参数的优点:

//函数声明
ReturnType DoSomething(Type parameter); 
//调用函数
ReturnType Result = DoSomething(argument);

上述函数调用将 argument 的值复制给 Parameter,再被函数 DoSomething( )使用;当DoSomething( )返回值时,这个值被复制给 Result。

如果 argument 占用了大量内存,这个复制步骤的开销将很大。

避免这些复制步骤,让函数直接使用调用者栈中的数据:使用引用的方式传递参数,避免将形参复制给形参,从而极大地提高性能。


使用const引用参数,确保被调用函数不能修改调用函数中的变量:

#include <iostream>
using namespace std;

void getPower(const double& number, double& result)
{
    //number *= 2;  表达式必须是可修改的左值C/C++(137)
    result = number * number;
}

int main()
{   
    double input = 0.0, power_input = 0.0;
    input = 2;
    getPower(input, power_input);
    cout << input << "'s power is: " << power_input << endl;
    //2's power is: 4
    return 0;
}

const 引用将参数标识为输入参数,并禁止对其进行修改

在多名程序员合作编程时,编写第一个版本的人和改进的 人可能不同,通过使用 const 引用可提高编程质量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值