指针入门

一 C++指针定义

int *p;
int* p;
int*p;
int * p;
// 指针类型与指针名由*隔开,中间的空格不影响申明

下面我们看一段关于指针的入门代码段,有助于理解‘*’与‘&’两个运算符

int a =100;
int* p = &a;
cout << "Value of a: " << a << endl;
cout << "Address of a: " << &a << endl;
cout << "Value of *p: " << *p << endl;
cout << "Value of p: " << p << endl;

&是取地址运算符,获得变量的内存地址,在定义指针后,指针名p表示地址,*p用来获取指针指向内存的数值。因此*p与a完全等价。顺便提一句,计算机使用十六进制来显示内存地址。

请注意,在C++创建指针时,计算机将分配用来存储地址的内存,但是不会分配用来存储指针所指向数据的内存。因此一定要在指针应用接触引用运算符(*)之前,将指针初始化为一个确定的适当的地址。否则可能会发生危险。请看下面的例子:

int * ptr;
*ptr = 666;

p确实是一个指针,但是指向不明;而且上述代码没有将地址赋值给p,数值666将放在哪儿也不确定。因此,由于p没有被初始化,这可能导致计算机出现难以察觉的bug。

二 使用new来分配内存

指针真正强大之处在于,在程序运行阶段动态的分配内存,此时我们就可以使用new运算符。

int * p = new int;

new int 告诉程序,需要适合存储int的一段内存。new运算符根据类型来确定分配多少字节的内存,然后他找到这样的内存,并返回其地址。

int a_val = 666;
int* ptr = new int;
*ptr = 666;

当需要内存时,使用new运算符动态分配内存,而当这段内存使用完毕后,为了最有效的使用内存,我们需要将刚刚申请的内存还给内存池,此时,我们使用delete来释放内存。

int* ptr = new int;   // allocate memory with new
/*代码段*/            // use memory
delete ptr;          // free memory with delete

这将释放ptr指向的内存,但是这并不会删除ptr指针本身,可以将ptr指向新分配的内存块。请牢记,在动态分配内存时,一定要将new与delete成对使用,否则就会发生内存泄漏(memory leak),也就是说使用new新分配的内存块再也没法使用了。

当使用new创建动态数组时,通用格式为 type_name* point_name = new type_name[num_of_element];

此时,释放内存的格式为 delete [] pointer_name; 

总之,使用new与delete时,需要遵守以下几个规则。①不能使用delete释放不是new分配的内存②不能使用delete释放同一内存块两次③使用new [] 为数组分配内存,使用delete []释放。④使用new为一个实体分配内存,使用delete释放。⑤对空指针使用delete时安全的。

using namespace std;
double* ptr = new double;
ptr[0]=1.11;
ptr[1]=2.22;
ptr[2]=3.33;
cout << "ptr[0] is " << ptr[0] << endl;
ptr = ptr+1;    // increment the pointer 
cout << "Now ptr[0] is " << ptr[0] << endl;
ptr = ptr -1;   // oint back to beganning
delete[] ptr;
return 0;

三 指针与数组

3.1 数组定义与数组名的理解

int arr1[10];   // 定义了数组名为arr1的数组,并且数组元素全部初始化为零。
int arr2[10] {1,2,3};  // 定义了一个名为arr2的数组,并初始化了前三个元素
int arr3[];     // 这是一种错误的定义方式,没有指定需要的内存空间的大小

std::cout << arr2 << std::endl;
std::cout << *arr2 << std::endl;
std::cout << arr2[0] << std::endl;

观察输出,可以发现第一个输出为地址,后两个输出为数组第一个元素的值。因此我们可以得出结论,数组名代表两层含义,第一,数组的名称,第二,数组名代表数组第一个元素的地址。

3.2 数组遍历与指针遍历

// 数组遍历
// 法一
int num = 10;
int arr[num] {1,2,3,4,5,6,7,8,9,0};
for(int i=0; i

四 指针与字符串

首先分析一下两行简单的代码:

char flower[10] = "rose"
std::cout << flower << "s are red." << std::endl;

在上面的讨论中我们知道数组名时数组第一个元素的地址,因此cout语句中的flower时v包含字符r 的char元素地址。cout认为char的地址就是字符串的地址,因此他打印该地址处的字符,并且继续打印后面的字符直到遇到空字符为止(‘\0’)。

事实上,cout和其他大部分C++表达式中,char数组名,char指针,以及用引号括起来的字符串常量,狗会被解释为字符串中第一个字符的地址。

char animal[20] = "bear";
const char* bird = "wren";
char* ps;
cout << animal << " and " << bird << endl;
cout << "Enrter a kind of animal: ";
cin >> animal;     
ps = animal;        // set ps point to a string
cout << ps << "!" << endl;
cout << "Before using strcpy_s() : " << endl;
cout << animal << " at " << (int *)animal << endl;
cout << ps << " at " << (int *)ps << endl;

ps = new char[strlen(animal) + 1];   // get new storage
strcpy_s(ps, animal);
cout << "After using the strcpy_s(): " << endl;
cout << animal << " at " << (int *)animal << endl;
cout << ps << " at " << (int *)ps << endl;
delete[] ps;

五 自动储存、静态存储、动态存储

5.1 自动存储

在函数内部定义的常规变量使用自动存储空间,称他们为自动变量(automatic variable),这意味着他们在所属函数调用时产生,在函数结束时消亡。因此,自动变量时局部变量,作用域为包含他的代码块。自动变量通常存储在栈中,这意味着在执行代码块时,其中的变量一次加入到栈中,在结束时按相反的顺序被释放,被称为先进后出(LIFO)。因而在代码执行阶段,栈不断地增大与缩小。

5.2 静态存储

静态存储是在整个程序执行期间都存在的存储方式,是变量成为静态变量的方式有两种,一种是在函数外面定义它,另一种是在申明变量时使用关键字static。

5.3 动态存储

运算符new和delete提供了一种比自动变量以及静态变量更加灵活的方法。他们管理了一个内存池,这在C++中称为自由存储空间或者堆(heap)。此时变量的生存周期不在局限于程序或函数的生存周期。这使得程序员对于程序使用内存有了更大的控制权。

 

 

 

你可能感兴趣的:(C++PrimerPlus)