第3章引用和指针

 

面试题1 一般变量引用

仔细阅读下面的代码,并分析变量的结果和程序运行结果:

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

int main(int argc, char* argv[])
{
    int a = 10;
    int b = 20;
    int &rn = a;	//声明rn是变量a的一个引用,也就是rn是a的一个别名
    int equal;

    rn = b;
    cout << "a =" << a << endl;
    cout << "b =" << b << endl;

    rn = 100;

    cout << "a =" << a << endl;
    cout << "b =" << b << endl;

    equal = (&a == &rn) ? 1 : 0;
    cout << "equal = " << equal << endl;

    return 0;
}

运行结果:


面试题2 指针变量引用

仔细阅读下面的代码,并分析变量的结果和程序运行结果:

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    int a = 1;
    int b = 10;
    int *p = &a;	//这一行的意思是声明整型的指针变量P,并指向a,也就是p指向a
    int*  &pa = p;  //声明p的一个指针引用pa, pa实际也是指向a

    (*pa)++;        //pa指向的内容加1
    cout << "a = " << a << endl;    //2
    cout << "b = " << b << endl;    //10
    cout << "*p = " << *p << endl;  //2

    pa = &b;        //将pa指向变量b的地址,由于pa也是p的引用,此时p也指向了b
    (*pa)++;
    cout << "a = " << a << endl;    //2
    cout << "b = " << b << endl;    //11
    cout << "*p = " << *p << endl;  //11

    return 0;
}

运行结果:


面试题3 看代码找错误——变量引用

#include <iostream>
using namespace std;

int main()
{
    int a = 1, b = 2;
    int &c;//声明引用没有初始化
    int &d = a;
    &d = b;//引用只能在声明的时候被赋值,以后不都能修改引用其它别名
    int *p;

    *p = 5;//p没有被初始化,因此此时的p是个野指针。

    return 0;
}

【解析】
代码第6行,正确,声明并初始化整型变量a和b。
代码第7行,编译错误。声明了一个引用类型的变量c,但是没有初始化,这时会出现编译错误,因为引用类型的变量在声明的同时必须初始化。
代码第8行,正确,声明了一个变量a的引用d。
代码第9行,编译错误,把d作为变量b的别名。这是因为引用只能在声明的时候被赋值,以后都不能再把该引用名作为其他变量名的别名。
代码第10行,正确,声明了一个整型的指针变量p。
代码第12行,运行错误,把p的内容赋为5。由于p没有被初始化,因此此时的p是个野指针,对野指针的内容赋值是非常危险的,会导致程序运行时崩溃。


面试题4 如何交换两个字符串

#include <iostream>
#include <string>

void swap(char *&x, char *&y)
{
    char *temp;
    temp = x;
    x = y;
    y = temp;
}

int main()
{
    char apr[] = "hello";
    char bpr[] = "how are your?";
    char *ap = apr;
    char *bp = bpr;

    std::cout << "*ap === " << ap << std::endl;
    std::cout << "*bp === " << bp << std::endl;

    swap(ap, bp);
    
    std::cout << "swap ap, bp === " << std::endl;
    std::cout << "*ap === " << ap << std::endl;
    std::cout << "*bp === " << bp << std::endl;

    return 0;
}

这里swap函数是利用传指针引用实现字符串交换的。由于swap函数是指针引用类型,因此传入函数的就是实参,而不是形参。
如果不用指针引用,那么指针交换只能在swap函数中有效,因为在函数体中,函数栈会分配两个临时的指针变量分别指向两个指针参数,对实际的ap和bp没有影响。
函数执行结果为:

从执行结果来看,swap函数确实起到了交换 两个字符串的目的。
当然,如果不用引用,还可以用二维指针达到同样的目的。可以把swap()函数的定义改为下面的形式。
void swap(char **x, char **y)
{
    char *temp;
    temp = *x;
    *x = *y;
    *y = temp;
}
把源代码swap(ap, bp);改为swap(&ap, &bp);
用这传指针地址的方式,同样可以起到交换两个字符串的目的。


面试题5 程序查错——参数引用

#include <iostream>

const float pi = 3.14f;
float f;

float f1(float r)
{
    f = r * r * pi;
    return f;
}

float& f2(float r)
{
    f = r * r * pi;
    return f;
}

int main()
{
    float f1(float = 5);//声明函数f1()的默认参数调用,其默认值为5
    float &f2(float = 5);//声明函数f2()的默认参数调用,其默认值为5
    float a = f1();//将变量a赋为f1()的返回值。
    //float &b = f1();
    float c = f2();//f2()函数在返回值时直接将全局变量f返回变量c
    float &d = f2();//返回全局变量的引用给d

    d += 1.0f;//将d的值加1.0,此时d是全局变量f的引用,因此f的值变成79.5

    std::cout << "a = " << a << std::endl;
    //std::cout << "b = " << b << std::endl;
    std::cout << "c = " << c << std::endl;
    std::cout << "d = " << d << std::endl;
    std::cout << "f = " << f << std::endl;
}

【解析】
代码第23行,将变量b赋为f1()的返回值。因为在f1()函数里,返回的是一个f的临时变量temp=78.5,对临时变量进行引用会发生错误。

将代码第23行和第30行注释,运行结果如下:


面试题6 参数引用的常见错误

#include <iostream>
using namespace std;

class Test
{
public:
    void f(const int& arg);
private:
    int value;
};

void Test::f(const int &arg)
{
    //arg = 10;//arg是常引用,值不能被修改
    cout << "arg = " << arg << endl;
    value = 20;
}

int main()
{
    int a = 7;
    const int b = 10;
    //int &c = b;//b是常量类型,不能赋给非常量的引用
    const int &d = a;

    a++;
    //d++;//不能使用常量引用修改变量的值

    Test test;
    test.f(a);
    cout << "a = " << a << endl;
    return 0;
}


面试题7 指针和引用有什么区别

解析】
区别如下:
(1)初始化要求不同。引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
(2)可修改性不同。引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指会在任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它的原始对象的绑定关系 。
(3)不存在NULL引有,引用不能使用指向空值的引用,它必须总是指向某个对象;而指针则可以是NULL,不需要总是指向某些对象,可以把指针指向任意的对象,所以指针更加灵活,也容易出错。
(4)测试需要的区别。由于引用不会指向空值,这意味着使用引用之前不需要测试它的合法性;而指针则需要经常进行测试。因此使用引用的代码效率比使用指会的要高。
(5)应用的区别。如果是指一旦指向一个对象后就不会改变指向,那么应该使用引用。如果有存在指向NULL(不指向任何对象)或在不同的时刻指向不同的对象这些可能性,应该使用指针。
       实际上,在语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换。总体来说,引用既具有指针的效率,又具有变量使用的方便性和直观性。


面试题8 为什么传引用比传指针安全

【解析】
由于不存在空引用,并且引用一旦初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。
对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const指针仍然存在空指针,并且有可能产生野指针。


面试题9 复杂指针的声明

【答案】
a. int a;//An integer
b. int *a; // A pointer to an integer
c. int **a; //A pointer a pointer to an integer
d. int a[10]; //An array of 10 integers
e. int *a[10]; //An array of 10 pointer to integeres
f. int(*a)[10];//A pointer to an array of 10 integers
g. int(*a)(int);//A pointer to a function a that takes an integer argument and retrns an integer
h. int(*a[10])(int);//An array of 10 pointers to functions that take an integer argument and return an integer


面试题10 看代码写结果——用指针赋值

#include <stdio.h>

int main()
{
    char a[] = "hello,world";
    char *ptr = a;

    printf("%c\n", *(ptr+4));
    printf("%c\n", ptr[4]);
    printf("%c\n", a[4]);
    printf("%c\n", *(a+4));

    *(ptr+4) += 1;
    printf("%c\n", *(ptr+4));
    printf("%s\n", a);

    return 0;
}


面试题11 指针加减操作

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = (int *)(&a + 1);

    printf("%d\n", *(a+1));
    printf("%d\n", *(ptr-1));

    return 0;
}

【解析】
代码第6行,ptr是一个int型的指针&a+1,即取a的地址,该地址的值加sizeof(a)的值,即&a+5*sizeof(int),也就是a[5]的地址,显然,当前指针已经越过了数组的界限。(int*)(&a+1)则是把上一步计算出来的地址,强制转换为int*类型,赋值给ptr.


面试题12 指针比较

#include <iostream>
using namespace std;

int main()
{
    char str1[]         = "abc";
    char str2[]         = "abc";
    const char str3[]   = "abc";
    const char str4[]   = "abc";
    const char *str5    = "abc";
    const char *str6    = "abc";
    //char *str7 = "abc";
    //char *str8 = "abc";
    char *str7 = str1;
    char *str8 = str2;

    cout << "(str1 == str2) = " << (str1 == str2) << endl;
    cout << "(str3 == str4) = " << (str3 == str4) << endl;
    cout << "(str5 == str6) = " << (str5 == str6) << endl;
    cout << "(str6 == str7) = " << (str6 == str7) << endl;
    cout << "(str7 == str8) = " << (str7 == str8) << endl;

    cout << "str1 == " << &str1 << endl;
    printf("str1====%p\n", str1);
    cout << "str2 == " << &str2 << endl;
    printf("str2====%p\n", str2);
    cout << "str3 == " << &str3 << endl;
    printf("str3====%p\n", str3);
    cout << "str4 == " << &str4 << endl;
    printf("str4====%p\n", str4);
    cout << "str5 == " << &str5 << endl;
    printf("str5====%p\n", str5);
    cout << "str6 == " << &str6 << endl;
    printf("str6====%p\n", str6);
    cout << "str7 == " << &str7 << endl;
    printf("str7====%p\n", str7);
    cout << "str8 == " << &str8 << endl;
    printf("str8====%p\n", str8);
    return 0;
}

 

面试题13 看代码找错误——内存访问违规

#include <iostream>
using namespace std;

int main()
{
    char a;
    char *str1 = &a;
    const char *str2 = "AAA";

    strcpy(str1, "hello");
    cout << "str1====" << str1 << endl;

    //str2[0] = 'B';
    cout << "str2====" << str2 << endl;

    return 0;
}

【解析】
代码第10行,str1指向一个字节大小的内存区。由于复制"hello"字符串最少需要6个字节,显然内存大小不够。因此会因为越界进行内存读写而导致程序崩溃。
代码第13行,str2指向”AAA“这个字符串常量,因为是常量,所以对str2[0]的赋值操作是不合法的,也会导致程序崩溃。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值