本篇博客的内容
- 什么是指针
- 什么是自由存储区
- 如何使用运算符new和delete分配和释放内存
- 如何使用指针和动态分配编写稳定的应用程序
- 什么是引用
- 指针和引用的区别
- 什么情况下使用指针,什么情况下使用引用
什么是指针?
所谓的指针也是一种变量,不过它存储的是内存的地址。它是一种指向内存单元的特殊变量。内存单元地址通常使用的是十六进制表示法(前缀为0x)。
声明指针
指针作为一种变量也需要声明和初始化。未初始化的指针可能导致程序访问非法内存单元,进而导致程序崩溃。
PointedType * PointerVariableName = NULL; //initializing value
int * pointsToInt = NULL;
使用引用运算符(&)获取变量的地址
#include <iostream>
using namespace std;
int main()
{
int age = 30;
const double Pi = 3.1416;
// Use & to find the address in memory
cout << "Integer age is located at: 0x" << &age << endl;
cout << "Double Pi is located at: 0x" << &Pi << endl;
return 0;
}
本程序使用引用运算符(&)获取变量age和常量Pi的地址。作为一种约定,显示十六进制数时,应加上文本0x。
PS:引用运算符(&)也叫地址运算符。
使用指针存储地址
#include <iostream>
using namespace std;
int main()
{
int age = 30;
int* pointsToInt = &age;
// Displaying the value of pointer
cout << "Integer age is at: 0x" << hex << pointsToInt << endl;
return 0;
}
本程序,使用指针来存储使用引用运算符获得的地址。
#include <iostream>
using namespace std;
int main()
{
int age = 30;
int* pointsToInt = &age;
cout << "pointsToInt points to age now" << endl;
// Displaying the value of pointer
cout << "pointsToInt = 0x" << hex << pointsToInt << endl;
int DogsAge = 9;
pointsToInt = &DogsAge;
cout << "pointsToInt points to DogsAge now" << endl;
cout << "pointsToInt = 0x" << hex << pointsToInt << endl;
return 0;
}
本程序,不同的内存地址赋给指针变量,让它指向不同的值。
使用解除引用运算符(*)访问指向的数据
#include <iostream>
using namespace std;
int main()
{
int age = 30;
int dogsAge = 9;
cout << "Integer age = " << age << endl;
cout << "Integer dogsAge = " << dogsAge << endl;
int* pointsToInt = &age;
cout << "pointsToInt points to age" << endl;
// Displaying the value of pointer
cout << "pointsToInt = 0x" << hex << pointsToInt << endl;
// Displaying the value at the pointed location
cout << "*pointsToInt = " << dec << *pointsToInt << endl;
pointsToInt = &dogsAge;
cout << "pointsToInt points to dogsAge now" << endl;
cout << "pointsToInt = 0x" << hex << pointsToInt << endl;
cout << "*pointsToInt = " << dec << *pointsToInt << endl;
return 0;
}
使用解除运算符(*)访问指针指向的内存单元中的值。解除运算符(*)也叫间接运算符。
#include <iostream>
using namespace std;
int main()
{
int dogsAge = 30;
cout << "Initialized dogsAge = " << dogsAge << endl;
int* pointsToAnAge = &dogsAge;
cout << "pointsToAnAge points to dogsAge" << endl;
cout << "Enter an age for your dog: ";
// store input at the memory pointed to by pointsToAnAge
cin >> *pointsToAnAge;
// Displaying the address where age is stored
cout << "Input stored at 0x" << hex << pointsToAnAge << endl;
cout << "Integer dogsAge = " << dec << dogsAge << endl;
return 0;
}
本程序是将*pointsToInt作为左值,即给它赋值的情况。
将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( )作用于char,int,double得到的结果分别是1,4,8。但是将sizeof( )作用于char*,int*,double*得到的结果都是4。我的笔电测试出来是这样的。
动态分配内存
int myNums[100]; //100个整型的静态数组
静态数组存在的一些问题:首先限制了程序的容量,无法存储100个以上的数字;如果只需存储1个数字,却为100 个数组预留存储空间,这降低了系统的性能。所以我们就需要动态分配内存了,C++提供了两个运算符new和delete。指针是包含内存地址的变量,在高效地动态分配内存方面起到十分重要的作用。
使用new和delete动态地分配和释放内存
使用new来分配新的内存块。通常情况下,如果成功,new将返回一个指针,指向分配的内存,否则会引发异常。使用new时需要指定为那种数据类型分配内存:
Type * Pointer = new Type; //request memory for one element
Type * Pointer = new Type[numElements]; //request memory for numElements
为整型分配内存,可以使用如下语法:
int * pointToAnInt = new int;
int * pointToNums = new int[10];
使用new 分配的要用delete来释放:
Type * Pointer = new Type;
delete Pointer;
Type * Pointer = new Type[numElements];
delete[] Pointer;
ps:自己new的一定要自己delete,否则会造成内存泄漏,我们应该尽量避免这一情况的发生。
#include <iostream>
using namespace std;
int main()
{
// Request for memory space for an int
int* pointsToAnAge = new int;
// Use the allocated memory to store a number
cout << "Enter your dog's age: ";
cin >> *pointsToAnAge;
// use indirection operator* to access value
cout << "Age " << *pointsToAnAge << " is stored at 0x" << hex << pointsToAnAge << endl;
delete pointsToAnAge; // release dynamically allocated memory
return 0;
}
PS:delete 和 new是配套使用的,除此以外不能将delete用于任何包含地址的指针。
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "How many integers shall I reserve memory for?" << endl;
int numEntries = 0;
cin >> numEntries;
int* myNumbers = new int[numEntries];
cout << "Memory allocated at: 0x" << myNumbers << hex << endl;
// de-allocate before exiting
delete[] myNumbers;
return 0;
}
PS:运算符new和delete分配和释放自由存储区中的内存。自由存储区是一种内存的抽象,表现为一个内存池,应用程序可分配(预留)和释放其中的内存。
将递增和递减运算符用于指针的结果
将指针递增或者递减,其包含的地址将增加或是减少指向数据类型的sizeof(并不一定是1字节)。这样编译器将确保指针不会指向数据的中间或末尾,而是会指向数据的开头。
Type * pType = Address;
// 执行++pType后,pType将包含(指向)Address + sizeof(Type)
#include <iostream>
using namespace std;
int main()
{
cout << "How many integers you wish to enter? ";
int numEntries = 0;
cin >> numEntries;
int* pointsToInts = new int [numEntries];
cout << "Allocated for " << numEntries << " integers" << endl;
for(int counter = 0; counter < numEntries; ++counter)
{
cout << "Enter number "<< counter << ": ";
cin >> *(pointsToInts + counter);
}
cout << "Displaying all numbers entered: " << endl;
for(int counter = 0; counter < numEntries; ++counter)
cout << *(pointsToInts++) << " ";
cout << endl;
// return pointer to initial position
pointsToInts -= numEntries;
// done with using memory? release
delete[] pointsToInts;
return 0;
}
上面的示例程序写地不地道,在循环遍历的时候,一般会重新定义一个变量来指向首地址,移动新定义的指针遍历,而不是原来的指针pointsToInts。
将关键字const用于指针
const 指针有如下三种类型:
指针包含的地址是常量,不能修改,但可以修改指针指向的数据。
int daysInMonth = 30;
int* const pDaysInMonth = &daysInMonth; // 指针包含的地址是常量
*pDaysInMonth = 31 //OK!
int daysInLunarMonth = 28;
pDaysInMonth = &daysInLunarMonth // Not OK!
指针指向的数据为常量,不能修改,但可以修改指针包含的地址,即指针可以指向其他地方。
int hoursInDay = 24;
const int* pointsToInt = &hoursInDay;
int monthInYear = 12;
pointsToInt = &monthInYear; //OK
*pointsToInt = 13; //Not OK
int* newPointer = pointsToInt; //Not OK! Cannot assign const to non-const
指针包含的地址以及指向的值都是常量,不能修改。
int hoursInDay = 24;
const int* const pHoursInDay = &hoursInDay;
*pHoursInDay = 25; // Not OK!
int daysInMonth = 30;
pHoursInDay = &daysInMonth; // Not OK!
将指针传递给函数
#include <iostream>
using namespace std;
void CalcArea(const double* const ptrPi, // const pointer to const data
const double* const ptrRadius, // i.e. no changes allowed
double* const ptrArea) // can change data pointed to
{
// check pointers for validity before using!
if (ptrPi && ptrRadius && ptrArea)
*ptrArea = (*ptrPi) * (*ptrRadius) * (*ptrRadius);
}
int main()
{
const double Pi = 3.1416;
cout << "Enter radius of circle: ";
double radius = 0;
cin >> radius;
double area = 0;
CalcArea (&Pi, &radius, &area);
cout << "Area is = " << area << endl;
return 0;
}
上面的程序使用了两种指针:ptrPi和ptrRadius被声明为“指向const数据的const指针”,所以两者都不能修改。ptrArea是const指针,不能修改指针的值(地址),但可以修改它指向的数据。
数组和指针的类似之处
数组变量名是指向第一个元素的指针,可将数组变量赋给类型与之相同的指针。
#include <iostream>
using namespace std;
int main()
{
// Static array of 5 integers
int myNumbers[5];
// Pointer initialized to array
int* pointToNums = myNumbers;
// Display address contained in pointer
cout << "pointToNums = 0x" << hex << pointToNums << endl;
// Address of first element of array
cout << "&myNumbers[0] = 0x" << hex << &myNumbers[0] << endl;
return 0;
}
数组的两种访问方式。
#include <iostream>
using namespace std;
int main()
{
const int ARRAY_LEN = 5;
// Static array of 5 integers, initialized
int myNumbers[ARRAY_LEN] = {24, -1, 365, -999, 2011};
// Pointer initialized to first element in array
int* pointToNums = myNumbers;
cout << "Display array using pointer syntax, operator*" << endl;
for (int index = 0; index < ARRAY_LEN; ++index)
cout << "Element " << index << " = " << *(myNumbers + index) << endl;
cout << "Display array using ptr with array syntax, operator[]" << endl;
for (int index = 0; index < ARRAY_LEN; ++index)
cout << "Element " << index << " = " << pointToNums[index] << endl;
return 0;
}
PS:自己new的,一定要自己delete!!!
使用指针时常犯的编程错误
不同于C#和Java等基于运行时环境的新语言,C++没有自动垃圾收集器对程序已分配但不能使用的内存进行清理。
内存泄漏
#include <iostream>
using namespace std;
int main()
{
// uninitialized pointer (bad)
bool* isSunny;
cout << "Is it sunny (y/n)? ";
char userInput = 'y';
cin >> userInput;
if (userInput == 'y')
{
isSunny = new bool;
*isSunny = true;
}
// isSunny contains invalid value if user entered 'n'
cout << "Boolean flag sunny says: " << *isSunny << endl;
// delete being invoked also when new wasn't
delete isSunny;
return 0;
}
上面的代码,我用gcc跑完之后,发现输入n的时候,*isSunny输出的是131。我也是有点懵逼。
悬浮指针
#include <iostream>
using namespace std;
int main()
{
cout << "Is it sunny (y/n)? ";
char userInput = 'y';
cin >> userInput;
// declare pointer and initialize
bool* const isSunny = new bool;
*isSunny = true;
if (userInput == 'n')
*isSunny = false;
cout << "Boolean flag sunny says: " << *isSunny << endl;
// release valid memory
delete isSunny;
return 0;
}
上面的程序是对前一版程序的改版,这样的代码安全性更高,使我们应该学习的代码。
检查使用new发出的分配请求是否得到满足
#include <iostream>
using namespace std;
// remove the try-catch block to see this application crash
int main()
{
try
{
// Request a LOT of memory!
int* pointsToManyNums = new int [0x1fffffff];
// Use the allocated memory
delete[] pointsToManyNums;
}
catch (bad_alloc)
{
cout << "Memory allocation failed. Ending program" << endl;
}
return 0;
}

上面的代码采用了异常抛出的处理方法(向用户报告异常之后)再退出程序。
#include <iostream>
using namespace std;
int main()
{
// Request LOTS of memory space, use nothrow
int* pointsToManyNums = new(nothrow) int [0x1fffffff];
if (pointsToManyNums) // check pointsToManyNums != NULL
{
// Use the allocated memory
delete[] pointsToManyNums;
//cout<<"jump in."<<endl;
}
else
cout << "Memory allocation failed. Ending program" << endl;
return 0;
}
上面的程序使用了变种的new(nothrow),它在程序内存分配失败时不引发异常,而是返回NULL,让你在使用指针前检查其有效性。
指针编程最佳实践
引用是什么
引用就是变量的别名。
#include <iostream>
using namespace std;
int main()
{
int original = 30;
cout << "original = " << original << endl;
cout << "original is at address: " << hex << &original << endl;
int& ref1 = original;
cout << "ref1 is at address: " << hex << &ref1 << endl;
int& ref2 = ref1;
cout << "ref2 is at address: " << hex << &ref2 << endl;
cout << "Therefore, ref2 = " << dec << ref2 << endl;
return 0;
}
上面的程序说明了如何声明并使用变量。
是什么让引用很有用?
在函数的编写中,其形参使用引用参数,可以避免传参时候的值拷贝,以及接收返回值时候的值拷贝。函数还可以用引用参数来返回函数的结果。
#include <iostream>
using namespace std;
void GetSquare(int& number)
{
number *= number;
}
int main()
{
cout << "Enter a number you wish to square: ";
int number = 0;
cin >> number;
GetSquare(number);
cout << "Square is: " << number << endl;
return 0;
}
将关键字const用于引用
int original = 30;
const int& constRef = original;
constRef = 40; // Not allowed: constRef can't change value in original
int& ref2 = constRef; // Not allowed: ref2 is not allowed
const int& constRef2 = constRef; //OK
按引用向函数传递参数
引用的优点之一就是,可以避免将形参复制给形参,从而极大地提高性能。
#include <iostream>
using namespace std;
void GetSquare(const int& number, int& result)
{
result = number*number;
}
int main()
{
cout << "Enter a number you wish to square: ";
int number = 0;
cin >> number;
int square = 0;
GetSquare(number, square);
cout << number << "^2 = " << square << endl;
return 0;
}
上面的程序在编写函数的时候,用来const。在编程中使用const可以提高编程的质量。在多人协作的时候,更加凸显这样的优点。
特此声明:本篇博客是学习《21天学通C++》的记录。