C++自学 | 4 指针 & 引用

本内容仅属于个人自学记录,欢迎交流和指正,请勿转载或发表不当言论。

主要学习网址:https://www.runoob.com/


4.1 Cplus/C++ 指针

 

1.  读取变量的内存地址

cout << &var <<endl;

 

 

2.  指针变量声明:

所有指针的值的实际数据类型,都是一个代表内存地址的长十六进制数。

type *var-name;

 

【举例

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

 

 

3.  NULL空指针:

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL值,空指针的值是 0 。

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

如需检查一个空指针,可以使用 if 语句:

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

 

 

4.  算术运算:

指针可以进行的算术运算

ptr++;     //如果 int *ptr 指向地址1000,ptr++ 指向地址 1004。ptr每增加一次,都将指向下一个整数位置,即当前位置往后移4个字节。
ptr--;     //如果 char *ptr 指向地址1000,ptr-- 指向地址999,因为上一个字符的位置在地址999。
ptr+n;
ptr-n;

 

【实例:递增指针代替数组

#include<iostream>
using namespace std;

int main() 
{
    const int MAX = 3;
    int var[MAX] = { 10,100,200 };
    int* ptr;
    
//递增指针时,将第一个元素的地址赋给指针 ptr
= var;
//递减指针时,将最后一个元素的地址赋给指针 ptr = &var[MAX-1]
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; }

 

执行后得到以下结果:

Address of var[0] = 0099FA10
Value of var[0] = 10
Address of var[1] = 0099FA14
Value of var[1] = 100
Address of var[2] = 0099FA18
Value of var[2] = 200

【实例:输出数组地址时可以不用 & 运算符 

修改上述程序中的代码时:

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

 

输出如下:

Address of var[0] = 00DEFA34
Value of var[0] = 00DEFA34
Address of var[1] = 00DEFA38
Value of var[1] = 00DEFA34
Address of var[2] = 00DEFA3C
Value of var[2] = 00DEFA34

 

 

对数组及数组中各元素输出的结果:

cout << "var        " << var << endl;
cout << "var+1      " << var + 1 << endl;
cout << "&var       " << &var << endl;
cout << "&var+1     " << &var + 1 << endl;
cout << "var[0]     " << var[0] << endl;
cout << "&var[0]    " << &var[0] << endl;
cout << "&var[0]+2  " << &var[0]+2 << endl;
cout << "&var[0]+3  " << &var[0]+3 << endl;

cout << "ptr        " << ptr << endl;
cout << "*ptr       " << *ptr << endl;

 

 

分别得到的结果是:

var        004FFAF0   //数组第一个元素地址
var+1      004FFAF4   //数字第一个元素地址 + 4个字节(int),即数组第二个元素的地址
&var       004FFAF0   //整个数组的地址
&var+1     004FFAFC   //跳过整个数组的地址后,第一个地址 (0+C = 十进制12,3个4字节地址)
var[0]     10         //数组第一个元素的值
&var[0]    004FFAF0   //数组第一个元素地址
&var[0]+2  004FFAF8   //数组第三个元素地址
&var[0]+3  004FFAFC   //跳过整个数组的地址后,第一个地址
ptr        004FFAF0   //数组第一个元素的地址
*ptr       10         //数组第一个元素的值

 

从上面可以总结出:

  • 数组第一个元素的地址:var, &var[0]
  • 整个数组的地址:&var

 

还可以利用指针来表示数组:

【实例:

#include<iostream>
using namespace std;

int main()
{
    const int MAX = 3;
    int  var[MAX] = { 10, 100, 200 };
    cout << *var << endl << endl;

    for (int i = 0; i < MAX; i++)
    {
        *var = i;    // 这是正确的语法
        cout << *var << endl;
        cout << var[0] << endl << endl;
    }
    return 0;
}

 

*var 的语句操作是把 i 的值赋给了 var 数组的第一个元素,修改了数组的值。

var++ 是不可以实现的,修改var的值是非法的,这是因为 var 是一个指向数组第一个元素的常量地址,不可以修改。

但以下语句是合法的:

*(var+2) = 500;

 

 

 

5.  指针数组:

指向整数的指针数组的声明如下:该数组 ptr 为一个 int 类型的指针数组,由 MAX 个整数指针组成,其中的每个元素都是一个指向 int 值的指针。即存储指针的数组。

int *ptr[MAX];

 

【实例:

#include<iostream>
using namespace std;

const int MAX = 3;

int main()
{
    int var[MAX] = { 10,100,1000 };
    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;
}

 

【实例:对于 char *names[MAX] 字符型的指针数组,可以理解为一个二维字符数组,其中每一行存储一个 char 类型的一维数组。

#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++)
        for (int j = 0; j < 8; j++)
        {
            cout << "Value of names[" << i << "] = ";
            cout << *(names[i] + j)<< endl;
        }
    return 0;
}

 

执行后的输出如下:

Value of names[0] = Z
Value of names[0] = a
Value of names[0] = r
Value of names[0] = a
Value of names[0] =  
Value of names[0] = A
Value of names[0] = l
Value of names[0] = i
Value of names[1] = H
Value of names[1] = i
Value of names[1] = n
Value of names[1] = a
Value of names[1] =  
Value of names[1] = A
Value of names[1] = l
Value of names[1] = i
Value of names[2] = N
Value of names[2] = u
Value of names[2] = h
Value of names[2] = a
Value of names[2] =  
Value of names[2] = A
Value of names[2] = l
Value of names[2] = i
Value of names[3] = S
Value of names[3] = a
Value of names[3] = r
Value of names[3] = a
Value of names[3] =  
Value of names[3] = A
Value of names[3] = l
Value of names[3] = i

 

 

 

6.  数组指针:

在C++运算符的优先级中,* 小于 [],ptr 先与 [] 结合成为数组,然后再与 int* 结合,使得数组的元素类型变为 int* 类型。因此以下的语句声明了一个 指针数组:

int *ptr[3];

 

通过 ()调整优先级后,* 和 ptr 先结合成为指针,然后再与 [] 结合成为一个数组。因此下列语句声明了一个 指向特定数组的指针,即数组指针:

int (*ptr)[3];

区别:

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。

 

 

7.  指向指针的指针(多级间接寻址):

通常情况下,一个指针包含第二个指针的地址,第二个指针指向实际值的位置。

声明如下:

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;
    cout << "var 地址为 :" << &var << endl;
    cout << "ptr=&var 值为var的地址:" << ptr << endl;
    cout << "ptr地址为:" << &ptr << endl;
    cout << "*pptr=ptr=&var  值为var的地址:" << *pptr << endl;
    cout << "pptr 地址为:" << &pptr << endl;
    return 0;
}

 

执行上述代码得到的结果如下:

var 值为 :3000
*ptr 值为:3000
**pptr 值为:3000
var 地址为 :00D3FA38
ptr=&var 值为var的地址:00D3FA38
ptr地址为:00D3FA2C
*pptr=ptr=&var  值为var的地址:00D3FA38
pptr 地址为:00D3FA20

 

 

 

8.  传递指针给函数:

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

【实例:

#include <iostream>
#include <ctime>
using namespace std;

void getSeconds(unsigned long* par);

int main()
{
    unsigned long sec = 0;
    cout << "Number of seconds  " << sec << endl;

    getSeconds(&sec);   //函数的参数为指针,因此传入变量的地址
    cout << "Number of seconds  " << sec << endl;

    return 0;
}

void getSeconds(unsigned long* par)
{
    *par = time(NULL);
    return;
}

 

【实例:能接受指针作为参数的函数,也能接受数组作为参数。

#include <iostream>

using namespace std;

double getAverage(int* arr, int size);

int main()
{
    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;
    int sum = 0;
    double avg;

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

    avg = double(sum) / size;

    return avg;
}

 

 

 

9.  从函数返回指针:

C++允许从函数返回指针,需要事先声明一个返回指针的函数,如下:

int* myFunction()
{
    ...
}

 

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

【实例:随机数生成器

#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;
}

 

执行上述代码得到如下结果:

14005
4858
13033
22168
15372
18777
6884
25243
7261
10901
*(p+0) : 14005
*(p+1) : 4858
*(p+2) : 13033
*(p+3) : 22168
*(p+4) : 15372
*(p+5) : 18777
*(p+6) : 6884
*(p+7) : 25243
*(p+8) : 7261
*(p+9) : 10901

 

 


4.2 Cplus/C++ 引用

 

1.  概念:

引用变量是一个别名,即某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。

引用通常用于函数参数列表和函数返回值。

 

2.  与指针的区别:

  • 不存在 空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。而指针可以在任何时候指向另一个对象。
  • 引用必须在创建时被初始化。而指针可以在任何时间被初始化。

引用更接近 const 指针,一旦与某个变量关联起来,就会一直指向该变量。

int rats = 10;
int &rodents = rats;

 

上述代码可以等同于下述代码:

int rats = 10;
int * const pr = &rats;

 

 

 

3.  创建引用:

可以通过原始变量名称或引用来访问变量的内容,例如:

int i = 17;
//我们可以为 i 声明引用变量
int& r = i;
double& s = d;

 

在声明中,& 读作引用。

第一个声明可以读作 “r 是一个初始化为 i 的整型引用”;第二个声明可以读作 “s 是一个初始化为 d 的double型引用”

 

 

4.  引用作为参数:

以引用作为参数的函数,可以传入变量,但不能传入常量。

【实例:使用 引用 来实现 引用调用函数

#include <iostream>
using namespace std;
 
// 函数声明
void swap(int& x, int& y);
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;
 
   /* 调用函数来交换值 */
   swap(a, b);
 
   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;
 
   return 0;
}
 
// 函数定义
void swap(int& x, int& y)
{
   int temp;
   temp = x; /* 保存地址 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y  */
  
   return;
}

 

【补充:C++的函数传参:

  • 将变量名作为实参和形参。此时传给形参的是变量的值,传递是单向的。如果在执行函数期间,形参的值发生变化,并不会回传给实参。因为在调用函数时,形参和实参不是同一个存储单元的。
  • 传递变量的指针。形参是指针变量,实参是一个变量的地址。调用函数时,形参 指向 实参变量单元,这种形式可以通过指针改变实参的值。
  • 传递变量的引用。形参是引用变量,和实参是一个变量。调用函数时,形参 指向 实参变量单元,这种形式可以通过引用改变实参的值。

【再补充:传递变量中指针与引用的区别和优势:

  • 选择成员时,引用 使用 . 来查找;指针使用 -> 来查找。
  • 指针可能传递 NULL,因此在使用前必须检查有效性;引用则必然代表某个对象。
  • 两者在函数中执行时,都不需要创建副本,因此效率更高。

 

 

5.  引用作为返回值:

使用引用来代替指针,使函数返回一个引用,增强了 C++ 程序的可读性和维护能力。

当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。

【实例:

#include <iostream>
 
using namespace std;
 
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
 
double& setValues( int i )
{
  return vals[i];   // 返回第 i 个元素的引用
}
 
// 要调用上面定义函数的主函数
int main ()
{
 
   cout << "改变前的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
 
   setValues(1) = 20.23; // 改变第 2 个元素
   setValues(3) = 70.8;  // 改变第 4 个元素
 
   cout << "改变后的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
   return 0;
}

 

执行后得到以下结果:

改变前的值
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
改变后的值
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50

 

 

此外,当返回一个引用时,注意被引用的对象不能超出作用域,除非是对 static 变量的引用。

 

posted @ 2020-06-24 01:00  伏延  阅读(154)  评论(0编辑  收藏  举报