C++笔记--指针和引用

Posted Kobe10

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++笔记--指针和引用相关的知识,希望对你有一定的参考价值。

  • 面试一:指针与引用的区别?

    答案:

    • (1)非空区别。在任何情况下都不能使用指向空值的引用。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。

      (2)合法性区别。在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。

      (3)可修改区别。指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一个不同对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。


  • 面试题二:下面5个函数哪个能成功进行两个数的交换?

#include<iostream>
using namespace std; 
void swap1(int p,int q)
{
    int temp;
    temp=p;
    p=q;
    q=temp;
}
void swap2(int *p,int *q)
{
   int *temp;
    *temp=*p;
    *p=*q;
    *q=*temp;
}
void swap3(int *p,int *q)
{
    int *temp;
    temp=p;
    p=q;
    q=temp;
}
void swap4(int *p,int *q)
{
    int temp;
    temp=*p;
    *p=*q;
    *q=temp;
} 
void swap5(int &p,int &q)
{
    int temp;
    temp=p;
    p=q;
    q=temp;
}
int main ()
{
    int a=1,b=2;
    //swap1(a,b);
    //swap2(&a,&b);
    //swap3(&a,&b);
    //swap4(&a,&b);
    //swap5(a,b);
    cout << "a:"<< a <<endl;
    cout << "b:"<< b <<endl;
    return 0;
}


解析:
这道题考察的是参数传递、值传递、指针传递(地址传递)和引用传递。

     swap1传递的是值的副本,在函数中只是修改了形参p、q(实际是a、b的一个拷贝),p、q的值确实交换了,但是它们是局部变量,不会影响到主函数a和 b 。当函数swap1生命周期结束时,p、q所在的栈也就被删除了。

     swap2传递的是一个地址进去,在函数体内的形参*p、*q是指向实际的参数a、b地址的两个指针。

     这里要注意:

             int *temp;

             *temp=*p;

     是不符合逻辑的,int *temp新建了一个指针(但是没分配内存)。*temp=*p不是指向而是拷贝。把*p所指向的内存的值(也就是a 的值)拷贝到*temp所指向内存里了。但是int *temp不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让它存值。分配的随机地址是个“意外”,且函数结束后不回收,造成内存泄漏。

     swap3传递的是一个地址,在函数体内的参数*p、*q是指向实际参数a、b地址的两个指针。

     这里要注意:

             int *temp;

             temp=p;

     int *temp新建了一个指针(但是没分配内存)。temp=p是指向而不是拷贝。temp指向了*p所指向的地址(也就是a )。而代码:

             int *temp;

             q=temp;

     但是函数swap3不能实现两数的交换,这是因为函数体内只是指针的变化,而对地址中的值却没有变化。

     swap4可以实现两数的交换,因为它修改的是指针所指向地址中的值。

     swap5函数与swap4相似,是一个引用传递,修改的结果直接影响实参。
答案:
  swap3,swap5


 

  • 面试题三:下面这个程序测试会有什么结果?

    #include<iostream>
    using namespace std;
    void GetMemory(char *p,int num)
    {
        p=(char *)malloc(sizeof(char) * num);
    }
    int main ()
    {
        char *str = NULL;
        GetMemory(str,100);
        strcpy(str,"hello");
        return 0;
    }

    解析:毛病出在函数GetMemory中,编译器总是要为函数的每个参数制作临时副本,在本例中,void GetMemory(char *p , int num)中的*p实际上是主函数中str的一个副本,而在函数GetMemory中只是把p所指向的内存地址改变了,但是str丝毫未变,因为函数GetMemory没有返回值,因此str并不指向p所申请的那段内存,所以函数GetMemory并不能输出任何东西,如下图所示。事实上,每次执行一次GetMemory就会申请一块内存,但是申请的内存却不能有效释放,结果是内存一直被独占,最终造成内存泄露。

    1

    •   如果一定要用指针去申请内存,那么应该采用指向指针的指针,传str 的地址给函数GetMemory。代码如下:
      #include<iostream>
      using namespace std;
      void GetMemory(char **p,int num)
      {
          *p=(char *)malloc(sizeof(char) * num);
      }
      int main ()
      {
          char *str = NULL;
          GetMemory(&str,100);
          strcpy(str,"hello");
          cout << *str << endl;
          cout << str << endl;
          cout << &str << endl;
          return 0;
      }

      这样的话程序就可以运行成功了,我们分别打印 *str 、 str 、 &str 可以发现,结果分别是 h 、 hello 、 0024FA80 。str就是字符串的值;*str 是字符串首字符,&str 就是字符串的地址值。

      当然也可以用函数返回值来传递动态内存。这种方法更简单,代码如下:

      #include<iostream>
      using namespace std;
      char *GetMemory(char *p,int num)
      {
          p=(char *)malloc(sizeof(char) * num);
          return p;
      }
      int main ()
      {
          char *str = NULL;
          str = GetMemory(str,100);
          strcpy(str,"hello");
          cout << *str << endl;
          cout << str << endl;
          cout << &str << endl;
          return 0;
      }
  • 面试题四:这个函数有什么问题?该如何修改?

    char *strA()
    {
       char str[] = "hello world";
        return str;
    }

    解析:这个str里存在的地址是函数strA栈里“hello world”的首地址。函数调用完成,栈帧恢复调用strA之前的状态,临时空间被重置,堆栈“回缩”,strA栈帧不再属于应该访问的范围。这段程序可以正确输出结果,但是这种访问方法违背了函数的栈帧机制。

          但是只要另外一个函数调用的话,你就会发现,这种方式的不合理及危险性。

          如果想获得正确的函数,改成下面这样就可以:

    char *strA()
    {
        char *str = "hello world";
        return str;
    }

    首先要搞清楚char *str 和 char str[] :

    1
    char str[] = "hello world";

    是分配一个局部数组。局部数组是局部变量,它所对应的是内存中的栈。局部变量的生命周期结束后该变量不存在了。

    1
    char *str = "hello world";

     

    是指向了常量区的字符串,位于静态存储区,它在程序生命期内恒定不变,所以字符串还在。无论什么时候调用 strA,它返回的始终是同一个“只读”的内存块。

          另外想要修改,也可以这样:

    1
    2
    3
    4
    5
    char *strA()
    {
        static char str[] = "hello world";
        return str;
    }

          通过static开辟一段静态存贮空间。


  • 面试题五:写出下面程序运行的结果。

    #include<iostream>
    using namespace std;
    int main ()
    {
        int a[3];
        a[0]=0; a[1]=1; a[2]=2;
        int *p , *q;
        p=a;
        q=&a[2];
        cout << a[q-p] <<endl;
    }

    解析:本程序的结构如下:

    (1)先声明了一个整型数组a[3],然后分别给数组赋值。

    (2)又声明了两个整型指针 p、q,但是并没有定义这两个指针所指向的地址。

    (3)使整型指针 p 的地址指向 a(注意 a 就是a[0]),使整型指针 q 的地址指向 a[2]。

         可实际验证程序如下:

    #include<iostream>
    using namespace std;
    int main ()
    {
       int a[3];
        a[0]=0; a[1]=1; a[2]=2;
        int *p , *q;
        p=a;
        cout << p <<endl;
        cout << *p <<endl;
        q=&a[2];
        cout << q <<endl;
        cout << *q <<endl;
        cout << a[q-p] <<endl;
    }

    上面的输出结果分别是:

         002DFD24

         0

         002DFD2C

         2

         2

         2

         q 的实际地址是 002DFD2C,p 的实际地址是 002DFD24。 002DFD2C-002DFD24=0x08(十六进制减法),相差是 8。

         q-p的实际运算是(q的地址值(002DFD2C)- p的地址值(002DFD24))/sizeof(int),即结果为 2 。


  • 面试题六:写出函数指针,函数返回指针,指向const的指针,指向const的const指针,const指针

    void (*f)();//f是一个指向函数的指针变量,它可以代替这个函数例如: int c;c=(*f)(a,b);或者int max(a,b);int (*f)(a,b);p=max;--这里*P指向了max的开端,所以*p也变成了max函数了。
    void * f();
    const int * a;
    const int *const a;
    int *const a;


    指针数组和数组指针   
      a是指针数组,是指一个数组里面都装着指针。例如:int *ptr[].
      b是一个数组指针,是指指向一个数组的指针。例如:int (*a)[3];

以上是关于C++笔记--指针和引用的主要内容,如果未能解决你的问题,请参考以下文章

重学C++:笔记C++基础容器&C++指针引用

重学C++:笔记C++基础容器&C++指针引用

重学C++:笔记C++基础容器&C++指针引用

C++学习笔记-1:指针和引用

《More Effective C++》阅读笔记

c++复习笔记——智能指针详细解析(智能指针的使用,原理分析)