C++学习——指针!!

1.指针的含义

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

 type *var-name;

示例:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

2.指针的使用

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include <iostream>

using namespace std;

int main ()
{
 int  var = 20;   // 实际变量的声明
 int  *ip;        // 指针变量的声明

 ip = &var;       // 在指针变量中存储 var 的地址

 cout << "Value of var variable: ";
 cout << var << endl;

 // 输出在指针变量中存储的地址
 cout << "Address stored in ip variable: ";
 cout << ip << endl;

 // 访问指针中地址的值
 cout << "Value of *ip variable: ";
 cout << *ip << endl;

 return 0;
}

3.Null指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

#include <iostream>
using namespace std;
int main ()
{
  int  *ptr = NULL;
  cout << "ptr 的值是 " << ptr ;
  return 0;
}

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,您可以使用 if 语句,如下所示:

  if(ptr)     /* 如果 ptr 非空,则完成 */
  if(!ptr)    /* 如果 ptr 为空,则完成 */

因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

4.C++ 指针的算术运算

函数指针是一个用数值表示的地址。
因此,可对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

  • 示例
    假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,
    ptr++
    执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节

注意:如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

4.1 指针递增

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

#include <iostream>

using namespace std;
const int MAX = 3;

int main ()
{
 int  var[MAX] = {10, 100, 200};
 int  *ptr;

 // 指针中的数组地址
 ptr = var;
 for (int i = 0; i < MAX; i++)
 {
   cout << "Address of var[" << i << "] = ";
  cout << ptr << endl;

  cout << "Value of var[" << i << "] = ";
  cout << *ptr << endl;

  // 移动到下一个位置
  ptr++;
 }
 return 0;
}

4.2 指针递减

同样地,对指针进行递减运算,即把值减去其数据类型的字节数。
在此不再赘述。

4.3 指针的比较

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

#include <iostream>

using namespace std;
const int MAX = 3;

int main ()
{
 int  var[MAX] = {10, 100, 200};
 int  *ptr;

 // 指针中第一个元素的地址
 ptr = var;
 int i = 0;
 while ( ptr <= &var[MAX - 1] )
 {
   cout << "Address of var[" << i << "] = ";
  cout << ptr << endl;

  cout << "Value of var[" << i << "] = ";
  cout << *ptr << endl;

  // 指向上一个位置
  ptr++;
  i++;
 }
 return 0;
}
  • 注意:
    & 并不是不能用在数组名上,只是用在数组名和数组名[i]有所区别。

    int height[10];//int型的数组
    cout << &height << endl;//&用在数组名上
    cout << &height[0] << endl;//&用在数组第一个元素上
    

上面 &height[0] 就是取得是数组第一个元素的地址,假设地址为 1000;

  1. height 等价于 &height[0],

  2. &height 是直接对数组名进行取地址,这个时候就是取得是 height 整个数组的地址,指向包含 10 个元素的 int 型数组的第一个地址

  3. 但是&height+1 就是将地址增加 10*4 个字节

  4. height+1 = height + 2 字节 = height + 1 个 short int 也即 一个数组元素。

  5. height+9 为 height[] 中最后一个元素的地址,height+10 为该数组结束后的第一个地址。

  6. &height +1=height+10,即执行 &height+1 的结果是地址跳到整个数组之后第一个地址。

     short int height[10]; //int型的数组(short int 每个数据2字节)
     cout <<  "height       "<< height << endl 
    <<  "height+1     "<< height + 1 << endl
     <<  "&height[0]   " <<  &height[0] << endl
      <<  "&height+1    "<< &height + 1<< endl
    <<  "height+9     "<< height+9 << endl
    << "height+10    " << height + 10 <<endl
    << "&height      " << &height << endl;
    
  • 注意:
    指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组.
    但是,数组名是一个指向数组开始地址的常数指针,因此,var++等类似的运算都是不允许的。

5.指针数组

我们讲解指针数组的概念之前,先让我们来看一个实例,它用到了一个由 3 个整数组成的数组:

#include <iostream>

using namespace std;
const int MAX = 3;

  int main ()
  {
   int  var[MAX] = {10, 100, 200};

   for (int i = 0; i < MAX; i++)
 {
  cout << "Value of var[" << i << "]   = ";
  cout << var[i] << endl;
   }
   return 0;
}

5.1 指针数组与int

可能有一种情况,我们想要让数组存储指向 int 或 char 或其他数据类型的指针。下面是一个指向整数的指针数组的声明:
int *ptr[MAX];

在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

#include <iostream>

using namespace std;
const int MAX = 3;

int main ()
{
 int  var[MAX] = {10, 100, 200};
 int *ptr[MAX];

   for (int i = 0; i < MAX; i++)
 {
   ptr[i] = &var[i]; // 赋值为整数的地址
 }
 for (int i = 0; i < MAX; i++)
 {
    cout << "Value of var[" << i << "] = ";
  cout << *ptr[i] << endl;
 }
 return 0;
}

5.2 指针数组与字符串

也可以用一个指向字符的指针数组来存储一个字符串列表,如下:

  #include <iostream>

  using namespace std;
  const int MAX = 4;

  int main ()
  {
   const char *names[MAX] = {
               "Zara Ali",
               "Hina Ali",
               "Nuha Ali",
               "Sara Ali",
   };

   for (int i = 0; i < MAX; i++)
{
  cout << "Value of names[" << i << "] = ";
  cout << names[i] << endl;
   }
   return 0;
}

6.多级间接寻址

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。

指针的指针就是将指针的地址存放在另一个指针里面。
通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。


image.png

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号

int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:

#include <iostream>

using namespace std;

int main ()
{
int  var;
int  *ptr;
int  **pptr;

var = 3000;

// 获取 var 的地址
ptr = &var;

// 使用运算符 & 获取 ptr 的地址
pptr = &ptr;

// 使用 pptr 获取值
cout << "var 值为 :" << var << endl;
cout << "*ptr 值为:" << *ptr << endl;
cout << "**pptr 值为:" << **pptr << endl;

return 0;
}

var 值为 :3000
*ptr 值为:3000
**pptr 值为:3000

7.传递指针给函数

C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

// 在写函数时应习惯性的先声明函数,然后在定义函数
void getSeconds(unsigned long *par);

getSeconds( &sec );

7.2 能接受指针作为参数的函数,也能接受数组作为参数。

#include <iostream>
 using namespace std;

// 函数声明
double getAverage(int *arr, int size);

int main ()
{
   // 带有 5 个元素的整型数组
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;

   // 传递一个指向数组的指针作为参数
   avg = getAverage( balance, 5 ) ;

     // 输出返回值
   cout << "Average value is: " << avg <<         endl; 

   return 0;
}

double getAverage(int *arr, int size)
{
  int    i, sum = 0;       
  double avg;          

  for (i = 0; i < size; ++i)
  {
    sum += arr[i];
   }

  avg = double(sum) / size;

  return avg;
}

8.从函数返回指针

在上一章中,我们已经了解了 C++ 中如何从函数返回数组,类似地,C++ 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,

int * myFunction()
{
.
.
.
}

示例:

#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

// 要生成和返回随机数的函数
int * getRandom( )
{
  static int  r[10];

  // 设置种子
  srand( (unsigned)time( NULL ) );
  for (int i = 0; i < 10; ++i)
  {
    r[i] = rand();
    cout << r[i] << endl;
  }

  return r;
  }

    // 要调用上面定义函数的主函数
    int main ()
{
   // 一个指向整数的指针
   int *p;

   p = getRandom();
   for ( int i = 0; i < 10; i++ )
   {
   cout << "*(p + " << i << ") : ";
   cout << *(p + i) << endl;
   }

   return 0;
}

另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static变量。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容