| 
 | 
 
什么是指针 
程序中的每个变量和字面量都有一个内存地址。程序使用的函数也同样有其内存地址。这些地址取决于运行程序时,将程序加载到内存的什么地方。而 
指针是一个可用于存储内存地址的变量。 
但是如果指针只存储内存地址,是不行的,为了使用存储于内存地址上的实体,除了知道该实体在哪里,还需要知道该实体是什么。实体存储在内存中是占用一定字节数的,要使用指针所存储的地址上的数据项,就需要知道数据项的类型。 
如此,指针不止存储了地址,还同时表示了数据项的类型。 
声明指针 
声明指针类似于声明一般变量,但变量类型后面跟一个星号: 
- long* pLongNumber;      //指向一个长整型的指针。
 
  复制代码 这里,指针存储(将来要赋值的)实体的地址,同时它接受的实体类型是长整型;也就是说,这个指针是“指向long”的指针。 
编译器还接受另一种类似的声明方式: 
- long *pLongNumber, longNumber;      //第一个变量是(指向长整型的)指针,第二个是长整型的一个整数变量
 
  复制代码 注意在C++中,通常约定指针的变量名以p开头,从而使源代码更清晰规泽并容易理解。 
初始化指针 
指针使用之前一定要初始化。原因是未初始化的指针,如果保存一个垃圾值,之后取值和存值的时候,都能产生程序出错等等危害。下面简要说明一下如何初始化指针: 
1、初始化为已定义的变量的地址: 
- int number = 0;                      //初始化变量
 
 - int* pNumber = &number;       //用变量初始化指针的时候,必须先初始化变量本身。
 
  复制代码 2、初始化为空(不指向任何实体): 
- int* pIntNumber = 0;      //不指向任何实体的指针
 
  复制代码 这种方式叫做初始化为空指针。但是这种初始化方式,如果在给这个指针赋值前就试图获取它的地址(和上面的值,叫解引用),程序就会失败。 
有一个办法可以避免这种失败,在程序中定义一个相同数据类型的值,然后初始化指针的时候就指向该值,比如 string GlobalInitialString = "I WILL NEVER FAIL (Global initial String)." 
那么一个好习惯就是在解引用任何指针之前,先测试它是否是空指针: 
- if (pIntNumber == 0)
 
 -     cout << endl << "pIntNumber is null. " << endl;
 
 - else
 
 -     cout << endl << "The dereference value is: " << *pIntNumber << endl;
 
  复制代码 注意上面代码中的解引用,即*pIntNumber还没有正式说明,将在后面具体详细说明,这里先了解是获取指针对应的被赋与指针的实体就可以了。 
测试非空还有其他几种书写样式,一般选一种心仪的即可: 
- if (!pIntNumber)
 
 -     cout << endl << "pIntNumber is null. " << endl;
 
 - else
 
 -     cout << endl << "The dereference value is: " << *pIntNumber << endl;
 
 - //Or:
 
 - if (pIntNumber != 0)
 
 -     cout << endl << "The dereference value is: " << *pIntNumber << endl;
 
  复制代码 在C语言中也可用NULL代表0,(标准库),但C++编译器不一定都是用NULL,所以为了通用起见都用0来表示空。 
使用指针 
对于已经初始化并已经赋值的指针,就可以加以应用,包括获取指针指向的内存地址,或者指向的实体的实际值。 
1、取址运算: 
取址运算符&是一个一元运算符,它可以获取实体的内存地址。一般给指针赋值的时候用到它。 
- long longNumber = 12345L;
 
 - long* pLongNumber;
 
 - pLongNumber = &longNumber;      //取址运算符,获取变量的保存地址,赋给指针。
 
  复制代码 2、提领运算符: 
提领运算指获取指针指向位置的所在位置的实际值。也就是取值运算。 
- #include <iostream>
 
 - using std::cout;
 
 - using std::endl;
 
 - int main(){
 
 -     long number = 123L;
 
 -     long* pnumber = &number;
 
 -     cout << endl
 
 -          << "The number stored in address pnumber is :"
 
 -          << *pnumber               //提领
 
 -          << endl
 
 -          << endl;
 
 -     return 0;
 
 - }
 
  复制代码 注意在指针相关运算中,“*”号既可以作提领运算符,也可以用作声明指针,这就是指针让人混乱的秘密了。 
3、指针与数组(包含数组与字符串的知识)指针和数组之间有很密切的联系。数组名其实就是指针(保存了一个地址),在输出语句中,可以将数组名像指针那样操作。 
如果输出语句只使用数组名,而该数组不是char类型的数组,则直接输出该数组在内存中的十六进制地址(就是说和直接输出指针相同)。 
数组名也可以直接用来初始化指针: 
double d_values[10]; 
double* p_d_values = d_values; 
但是也要注意,指针和数组名只是类似性,其实是不一样的,这主要因为指针的内容可以修改,而数组名所表示的地址是固定的。 
 
4、函数指针 
5、字符数组(字符串) 
char类型的数组有两个含义,1)表示字符数组  2)表示字符串。 其中的区别是字符串的最后一个元素(也可能不仅仅是最后一个元素)要为空字符'\0'。 
如果字符数组的最后一个元素不是空字符,则该char仅仅是字符数组而非字符串。 
char vowls[5] = {"a", "e", "i", "o", "u"}; 
仅仅是包含5个元素的字符数组 
char vowls2[6] = {"a", "e", "i", "o", "u", "\0"}; 
则是字符串。 
要初始化字符串还有两种方法: 
char volws2[] = "aeiou"; 
char volws2[10] = "aeiou";     注意这种方法将在第6~第10个位置写入空字符'\0'。 
 
 
使用指针,可以在不规定数组大小的情况下,创建非空字符串字面量 
注意用指针初始字符串一定加上const修饰符(若不加const修饰符,则后期可以更改该内容,则更改后的内容如果没以空字符结尾——'\0',取值的时候就会突破指针界限,一直取值直到遇到第一个空字符为止。所以必须添加const) 
const char*   psomechar = "A lucky star."; 
cout << psomechar << endl; 
注意cout将指向char的指针看作非空字符串,然后输出该字符串。 
 
对于char字符串数组,可以建立指针数组,然后引用数组下标访问每一个元素: 
- int main()
 
 - {
 
 -     const char* pnames[] = { "Arnold Schwarzenegger", "Paul Newman", "David Dean"};
 
 -     for( int i = 0; i < 3; ++i)
 
 -     {
 
 -         cout << pnames[i] << endl;
 
 -     }
 
 -     return 0;
 
 - }
 
 
  复制代码 
注意以上每个指针数组的元素也是指针,因为每个元素指向不同的字符串,每个字符串没有冗余,所以使用指针数组占用的内存较少。另外排序和转存都只挪动指针,也可以节省时间和空间。 
- //获取指针数组的大小:
 
 - const int namesSize = sizeof pnames / sizeof pnames[0];
 
 - //这里的原理还是用到每一个指针数组的元素都是一个——相同大小的——指针
 
  复制代码 
6、为什么使用指针 
可以在函数中访问函数外部定义的大块数据 
可以动态的为新变量分配内存空间。即在程序执行过程中分配。 
 
 
 
 
 
 
 
 
 
 
 
 
 |   
 
 
 
 |