指针
指针:指针是存储内存地址的变量。
使用指针存储内存地址、变量的地址(& + 类型)、解引用(*)三者密不可分。
关于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;
}
上述例子说明:将 sizeof( )用于指针时,结 果取决于编译程序时使用的编译器和针对的操作系统,与指针指向的变量类型无关。
指针的sizeof结果均是8字节,说明编译器是64bit,并且在64bit操作系统运行该程序。
动态分配内存:new、delete
使用 new 时,需要指定要为哪种数据类型分配内存;使用 new 分配的内存最终都需使用对应的 delete 进行释放:
Type* Pointer = new Type;
delete Pointer;
为多个元素分配的内存:
Type* Pointer = new Type[numElements];
delete[] Pointer;
不再使用分配的内存后,如果不释放它们,这些内存仍被预留并分配给您的应用程序。
这将减少可供其他应用程序使用的系统内存量,甚至降低应用程序的执行速度。这被称为内存泄露,应不惜一切代价避免这种情况发生。
运算符 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;
}
分配的起始地址: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;
}
- 声明pb指针时,立即初始化合法的内存地址
- 使用const关键字,使得pb的指向不可更改,但可以修改pb的指向的值
- 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;
}
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 引用可提高编程质量。