打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C/C++中指针和引用之相关问题研究


程序2:
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
  string s1("Hello");
  string s2("World");
  // printf("%s\n", s1); 不能用printf输出s1,而应该用cout
 
  cout << "s1的地址 = "<< &s1 << endl;                        // &s1 = 0012FF64
  cout << "s2的地址 = "<< &s2 << endl;                        // &s2 = 0012FF54
 
  string& rs = s1;                                                           // 1. 定义一个引用rs,rs引用s1
  cout << "引用rs的地址 = " << &rs << endl;                  // &rs = 0012FF64
 
  string* ps = &s1;                                                         //定义一个指针ps, ps指向s1
  cout << "指针ps的地址 = " << ps << endl;                    // ps = 0012FF64
 
  cout << rs << ", " << *ps << endl;                          // Hello, Hello
  // 如果没有#include <string>,上面的语句在编译的时候,会出现如下错误:
  // error C2679: binary '<<' : no operator defined which takes a right-
  // hand operand of type 'class std::basic_string<char,struct
  // std::char_traits<char>,class std::allocator<char> >'
  // (or there is no acceptable  conversion)
 
  rs = s2;                                                      // 2. rs仍旧引用s1, 但是s1现在的值是"World"
  ps = &s2;                                                   // ps现在指向s2
 
  cout << "引用rs的地址 = " << &rs << endl;                  // &rs = 0012FF64 未改变
  cout << "引用rs的值 = " << rs << endl;                       // rs = "World" 已改变
 
  cout << "指针ps的地址 = " << ps << endl;                        // ps = 0012FF54  已改变
  cout << "指针ps所指地址的内容 = " << *ps << endl;          // *ps = World    已改变
 
  cout << "s1的地址 = "<< &s1 << endl;                        // 3. &s1 = 0012FF64 未改变
  cout << "s1的值 = " << s1 << endl;                             // 4. s1 = World  已改变
 
  return 0;
}
 
可以认为:
引用就是变量的别名,在引用初始化的时候就已经确定,以后不能再改变。见程序2的粗体字语句。第1句,声明了rs引用s1,s1的值为”Hello”,从这以后,rs实际上就相当于变量s1了,或者从更本质的意义上来说,rs的地址就是初始化时s1的地址了,以后都不会再改变。这应该比较好理解,比如我们在程序中定义了一个变量a,不管我们如何给a赋值,但它的地址是不会改变的;
 
第2句,rs仍旧指向初始化时s1的地址,但此处的赋值就相当于重新给s1赋值,因此我们从第3句和第4句可以看到,s1的地址并没有发生变化,但是其值已经发生了变化。

二、作为参数传递
利用引用的这个特性,可以用它作为函数的传出参数。如程序3:
#include <iostream>
#include <string>
using namespace std;
int newEvaluation(string& aStr)
{
  string bStr("Hello,");
  aStr = bStr + aStr;
 
  return 0;
}
 
int main(void)
{
  string aStr("Patrick!");
  newEvaluation(aStr);
  std::cout << aStr << endl;                      // 输出结果:"Hello, Patrick!"
 
  return 0;
}
 
而一般变量,则不能从函数内部传值出来,比如程序4:
#include <iostream>
#include <string>
using namespace std;
 
int newEvaluation(string aStr)
{
  string bStr("Hello,");
  aStr = bStr + aStr;
 
  return 0;
}
 
int main(void)
{
  string aStr("Patrick!");
  newEvaluation(aStr);
  std::cout << aStr << endl;                     // 输出结果:"Patrick!",aStr的值没有变化
 
  return 0;
}
 
当然程序3引用传递的方式也可以写成指针传递的方式,如程序5:
#include <iostream>
#include <string>
using namespace std;
 
int newEvaluation(string* const aStr)
{
  string bStr("Hello,");
  *aStr = bStr + *aStr;
 
  return 0;
}
 
int main(void)
{
  string aStr("Patrick!");
  newEvaluation(&aStr);
  std::cout << aStr << endl;                      // 输出结果:"Hello, Patrick!"
 
  return 0;
}
 
注意程序中的陷井,如程序6:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
  int nNumber;
  nNumber = 25;
  //让指针指向nNumber
  pPointer = &nNumber;
}
 
void main()
{
  SomeFunction();    //为pPointer赋值
  //为什么这里失败了?为什么没有得到25
  cout << "Value of *pPointer: " << *pPointer << endl;
}
 
这段程序先调用了SomeFunction函数,创建了个叫nNumber的变量,接着让指针pPointer指向了它。可是问题出在哪儿呢?当函数结束后,nNumber被删掉了,因为这一个局部变量。局部变量在定义它的函数执行完后都会被系统自动删掉。也就是说当SomeFunction 函数返回主函数main()时,这个变量已经被删掉,但pPointer还指着变量曾经用过的但现在已不属于这个程序的区域。
 
尽管在SomeFunction中使用所谓的动态分配内存。程序7中也存在陷井:
#include <iostream.h>
int *pPointer;
 
void SomeFunction()
{
    int intNumber = 25;
    // 让指针指向一个新的整型
    pPointer = new int;
    pPointer = &intNumber;
}
 
void main()
{
    SomeFunction();                                           // 为pPointer赋值
    cout<< "Value of *pPointer: " << *pPointer << endl;
    delete pPointer;
}
原因也如上面所言,intNumber的作用范围仅限于SomeFunction中,离开了SomeFunction,那么intNumber就不存在了,那么&intNumber即intNumber的地址就变得没有意义了,因此,该地址所指向的值是不确定的。如果改为下面的程序就不会有问题了。

程序8:
#include <iostream.h>
int *pPointer;
 
void SomeFunction()
{
    int intNumber = 25;
    // 让指针指向一个新的整型
    pPointer = new int(intNumber);
}
 
void main()
{
    SomeFunction();                                           // 为pPointer赋值
    cout<< "Value of *pPointer: " << *pPointer << endl;
    delete pPointer;
}

三、指针的指针
前面说到,指针是没有级数限制的。
程序9:
#include<stdio.h>
#include<stdlib.h>
 
void main(void)
{
    int i, j;
    int a[10], b[3][4], *p1, *p2, **p3;  
    for(i = 0; i < 10; i++)
       scanf("%d", &a[i]);              
 
    for(i = 0; i < 3; i++)
       for(j = 0; j < 4; j++)
           scanf("%d", &b[i][j]);        
 
    p1 = a;
    p3 = &p1;
    for(i = 0; i < 10; i++)
       printf("%4d", *(*p3+i));           
    printf("\n");
 
    for(p1 = a; p1 - a < 10; p1++)   
    {
       p3 = &p1;
       printf("%4d", **p3);
    }
    printf("\n");
 
    for(i = 0; i < 3; i++)               
    {
       p2 = b[i];
       p3 = &p2;
       for(j = 0; j < 4; j++)
       printf("%4d",*(*p3+j));
       printf("\n");
    }
 
    for(i = 0; i < 3; i++)               
    {
       p2 = b[i];
       for(p2 = b[i]; p2-b[i] < 4; p2++)
       {
           p3 = &p2;
           printf("%4d", **p3);
       }
       printf("\n");
    }
}

输出的结果:
    1   2   3   4   5   6   7   8   9   10
    1   2   3   4   5   6   7   8   9   10
    11  12  13  14
    15  16  17  18
    19  20  21  22
    11  12  13  14
    15  16  17  18
    19  20  21  22

四、函数指针和函数引用
函数指针是C++最大的优点之一。和使用普通指针相比,高级程序员只要有可能都更愿意使用引用,因为引用更容易处理一些。然而,当处理函数时,函数引用对比函数指针就未必有这个优势了。现有的代码很少使用函数引用。下面将向介绍如何函数指针、如何使用函数引用以及分别在什么情况下使用它们。

① 函数指针的例子
#include <iostream>
void print(int i)
{
    std::cout << i << std::endl;
}
 
void multiply(int& nDest, int nBy)
{
    nDest *= nBy;
}
 
void print_something()
{
    std::cout << "something" << std::endl;
}
 
int sayHello()
{
    std::cout << "Hello, World!" << std::endl;
    return 10;
}
 
int main()
{
    void (*pFunction_1)(int);
    pFunction_1 = &print;
    pFunction_1(1);
    // 输出结果为1
 
    void (*pFunction_2)(int&, int) = &multiply;
    int i = 1;
    pFunction_2(i, 10);
    std::cout << "i = " << i << std::endl;
    // 输出结果为10
  
    void (*pFunction_3)();
    pFunction_3 = &print_something;
    pFunction_3();
    // 输出结果为something
 
    int (*pFunction_4)();
    pFunction_4 = &sayHello;
    int a = pFunction_4();
    // 输出结果为Hello, World!
    std::cout << a << std::endl;
    // 输出结果为10
  
    return 0;
}
 
② 函数引用的例子
#include <iostream>
void print(int i)
{
    std::cout << i << std::endl;
}
 
void print2(int i)
{
    std::cout << i << std::endl;
}
 
void multiply(int& nDest, int nBy)
{
    nDest *= nBy;
}
 
void print_something()
{
    std::cout << "something" << std::endl;
}
 
int sayHello()
{
    std::cout << "Hello, World!" << std::endl;
    return 10;
}
 
 
int main()

    // void (&rFunction_1)(int);
    // 错误:未初始化引用!引用必须初始化
 
    void (&rFunction_2)(int) = print;
    rFunction_2(1);
    // 输出1
 
    rFunction_2 = print2;
    rFunction_2(2);
    // 输出2
 
    void (&rFunction_3)(int&, int) = multiply;
    int i = 1;
    rFunction_3(i, 10);
    std::cout << i << std::endl;
    // 输出10
 
    void (&rFunction_4)() = print_something;
    rFunction_4();
    // 输出something
 
    int (&rFunction_5)();
    rFunction_5 = sayHello;
    int a = rFunction_5();   // 输出Hello, World!
    std::cout << a << std::endl;
    // 输出10
 
    return 0;
}
 
③ 函数指针和函数引用作为函数参数
#include <iostream>
 
void print(int i)
{
    std::cout << i << std::endl;
}
 
void print2(int i)
{
    std::cout << i * 2 << std::endl;
}
 
void printSomething()
{
    std::cout << "Something" << std::endl;
}
 
void sayHello()
{
    std::cout << "Hello, World!" << std::endl;
}
 
void call_p_func(void (*func)(int))
{
    func(1);
    func(2);
    func(3);
}
 
void call_r_func(void (&func)(int))
{
    func(1);
    func(2);
    func(3);
}
 
void call_p_function(void (*func)())
{
    func();
}
 
int main()
{
    std::cout << "函数指针作为参数" << std::endl;
    call_p_func(&print);
    call_p_func(&print2);
    call_p_function(&printSomething);
    call_p_function(&sayHello);
    call_p_function(sayHello);
    // 上面两句对于某些编译器来说是一样的,但是推荐使用前者的写法,
    // 这样可以是程序的可读性更好一些
 
    std::cout << "函数引用作为参数" << std::endl;
    call_r_func(print);
    call_r_func(print2);
 
    return 0;
}
 
总结:
函数指针的声明使用方式:
<想要指向的函数之返回类型>(*函数指针的名称)<想要指向的函数之参数类型…>
如要想声明一个函数指针指向以下函数:
void print(int i)
{
    std::cout << i << std::endl;
}
那么就可以如下操作:
void (*pFunction)(int);
然后如下用函数的地址给pFunction赋值:
pFunction = &print;
在然后,pFunction就可以和函数print一样使用了,比如,
pFunction(1);
等等。
 
函数引用的声明和使用方式:
<欲引用的函数之返回类型>(&函数引用的名称)<欲引用的函数之参数类型…>=<欲引用的函数的名称>,至所以如此,是引用在声明的时候必须初始化,引用不能指向空值。
如要想声明一个函数引用指向以下函数:
void print(int i)
{
    std::cout << i << std::endl;
}
那么就可以如下操作:
void (&rFunction)(int)=print;
在然后,rFunction就可以和函数print一样使用了,比如,
rFunction(1);
等等。

五、const修饰指针和引用
大致而言,const修饰指针和引用分三种情况,即const修饰指针、const修饰引用和const修饰指针的引用。下面分别讨论之。
 
① const修饰指针
   const修饰指针又分为三种情况,即const修饰指针本身、const修饰指针所指的变量(或对象)以及const修饰指针本身和指针所指的变量(或对象)。
 
    a. const修饰指针本身
    在这种情况下,指针本身是常量,不能改变,任何修改指针本身的行为都是非法的,例如:
    double pi = 3.1416;           
    double* const PI = &pi;
 
    double alpha = 3.14;
    PI = &alpha;                   // 错误。因为指针PI是常量,不能再被改变。
    *PI = alpha;                   // OK。虽然指针PI不能被改变,但指针所指的变量或者对象可变。
  
    b. const修饰指针指向的变量(或对象)
    在这种情况下,指针本身可以改变,但const所修饰的指针所指向的对象不能被改变,例如:
    double pi = 3.1416;
    const double* PI = &pi;
 
    double alpha = 3.14;
    *PI = alpha;    // 错误。因为PI所指向的内容是常量,因此*PI不能被改变。
    PI = &alpha;    // OK。虽然指针所指的内容不能被改变,但指针PI本身可改变。从而通过这种方式改变*PI。
   
    c. const修饰指针本身和指针所指向的变量(或对象)
    在这种情况下,指针本身和指针指向的变量(或对象)均不能被改变,例如:
    double pi = 3.146;
    const double* const PI = &pi;
    //double const* const PI = &pi;
    cout << "PI = " << PI << endl;
    cout << "*PI = " << *PI << endl;
 
    double alpha = 3.14;
    //*PI = alpha;                 // 错误。因为PI所指向的内容是常量,因此*PI不能被改变。
    //PI = &alpha;                 // 错误。因为指针PI是常量,不能再被改变。
 
② const修饰引用
   const修饰引用没有指针修饰指针那么复杂,只有一种形式。引用本身不能被改变,但所指向的对象是可以被改变的,见上面“一、基本知识”。
    double pi = 3.1416;
    //const double& PI = pi;
    double const& PI = pi;                  //和上面一句是等价的
    //double& const PI = pi;                //有问题。很多编译器会产生warning
    cout << PI << endl;
 
③ const修饰指针引用
   我们用例子来说明。
   double pi = 3.14;
   const double* pPI = &pi;
   //const double*& rPI = &pi;             //错误。不能将double* 转换成const double *&
   const double*& rPI = pPI;               //OK。声明指针引用的正确方法

说明:const double*& rPI = &pi; 为什么会出现错误呢?我们知道,引用是被引用对象的别名,正因为如此,由于rPI是pPI的别名,因此rPI和pPI的类型必须完全一致。从上面的代码段我们可以看到,rPI的类型是const double*,而&pi的类型是double*,因此这句程序是错误的。
 
下面这段代码和 ① 中的b中的情形对应(即内容不可变,指针可变):
double pi = 3.1416;
double api = 3.14;
const double* pPI = &pi;
const double* pAPI = &api;
const double*& rPI = pPI;
const double*& rAPI = pPI;

*rAPI = api;                 // 错误。指针所指向的值不能被直接改变
rAPI = pAPI;               // OK。指针本身可以被改变

指针引用的用法还有其它的情形,由于罕用,故此不谈及。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C++开发者都应该使用的10个C++11特性
与LSGO一起学“C++上机小练习10”参考代码
C++ const型变量与函数重载
尽量不要使用#define
物联网教程—新手入门:C++中布尔类型
C++引用
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服