第3章引用和指针2

面试题14 指针的隐式转换

#include <stdio.h>

int main()
{
    int ival    = 1024;
    int ival2   = 2048;
    int *pi1    = &ival;
    int *pi2    = &ival2;
    int **pi3   = 0;

    ival    = *pi3; //编译错误,ival是int型,*pi3是 int*类型,不能隐式转换
    *pi2    = *pi3; //编译错误,*pi2是int型,*pi3是 int*类型,不能隐式转换
    ival    = pi2;  //编译错误,ival是int型,pi2是 int*类型,不能隐式转换
    pi2     = *pi1; //编译错误,pi2是int*型,*pi1是 int类型,不能隐式转换
    pi1     = *pi3; //运行时错误,pi3是NULL指针,试图得到*pi3的值会发生运行错误
    ival    = *pi1;
    pi1     = ival; //编译错误,pi1是int*型,ival是 int类型,不能隐式转换
    pi3     = &pi2;

    return 0;
}

【答案】
代码第11、12、13、14、17行编译错误。
第15行运行错误。


面试题15 指针常量与常量指针的区别

【解析】
       这里有个小规则,像这样连着的两个词,前面的一个通常是修饰部分,中心启是后面一个词。
      常量指针,表达为“是常量的指针”,它首先应该是一个指针。
      指针常量,表述为“是指针的常量”,它首先应该是一个常量。
接下来进行详细分析。
       常量指针,它是一个指向常量的指针。设置常量指针指向一个常量,为的就是防止写程序过程中对指针误操作出出了修改常量这样的错误,编译系统就会提示我们出错信息。因此,常量指针就是指向常量的指针,指针所指向的地址的内容是不可修改的。
       指针常量,它首先是一个常量,然后才是一个指针。指针常量就是不能修改这个指针所指向的地址,一开始初始化指向哪儿,它就只能指向哪儿了,不能指向其他的地方了,就像一个数组的数组名一样,是一个固定的指针,不能对它移动操作。如果使用p++,系统就会提示出错。但是注意,这个指向的地方里的内容是可以替换的,这和上面说的常量指针是完全不同的概念。总之,指针常量就是指针的常量,它是不可改变地址的指针,但是可以对它所指向的内容进行修改。

【答案】
常量指针就是指向常量的指针,它所指向的地址的内容是不可修改的。
指针常量就是指针的常量,它是不可改变地址的指针,但是可以对它所指向的内容进行修改。


面试题16 指针的区别

【解析】
       如果const位于*号的左侧,则const就是有来修饰指针所指向的变量,即指针指向为常量;如果const位于*号的右侧,const就是修饰指针本身,即指针本身是常量。因此,p1指针本身是常量,但它指向的内容可以被修改。p2和p3的情况相同,都是指针所指向的内容为常量。p4则表示指针本身是常量,并且它指向的内容也不可被修改。

【答案】
p1是指针常,它本身不能被修改,指向的内容可以被修改。
p2和p3是常量指针,它本身可以被修改,指向的肉容不可以被修改。
p4本身是常量,并且它指的内容也不可被修改。


面试题17 找错——常量指针和指针常量的作用

#include <stdio.h>

int main()
{
    const char *node1 = "abc";
    //char *const node2 = "abc";//C++ 11不支持这种写法
    printf("node1====%s\n", node1);
    //node1[2] = 'k';//node1是常量指针,所指向的内容是不能修改的
    //*node1[2] = 'k';//node1是常量指针,所指向的内容是不能修改的
    //*node1 = "xyz";//node1是常量指针,所指向的内容是不能修改的
    node1 = "xyz";//正确,node1指向了“xyz”所在的地址。
    printf("node1====%s\n", node1);

    //node2[2] = 'k';
    //*node2[2] = 'k';
    //*node2 = "xyz";
    //node2 = "xyz";

    return 0;
}

【答案】
代码第8、9、10行出现编译错误。
第11行正确
代码第6行C++11不支持这种写法,所以后面13、14、15、16行都是错的。
运行结果如下:


面试题18 this指针的正确叙述

下列关于this指针的叙述中,正确的是()
A,任何与类相关的函数都有this指针。
B,类的成员函数都有this指针。
C,类的友元函数都有this指针。
D,类的非静态成员函数才有this指针。

【解析】
A错误。类的非静态成员函数是属于类的对象,含有this指针。而类的static函数属于类本身,不含this指针。
B错误。类的非静态成员函数是属于类的对象,含有this指针。而类的static函数属于类本身,不含this指针。
C错误。友元函数是非成员函数,所以它无法通过this指针获得一份拷贝。
D正确。


面试题19 看代码写结果——this指针

#include <iostream>
using namespace std;

class MyClass
{
public:
    int data;
    MyClass(int data)
    {
        this->data = data;
    }
    void print()
    {
        cout << data << endl;
        cout << "hello!" << endl;
    }
};

int main()
{
    MyClass *pMyClass;
    pMyClass = new MyClass(1);
    pMyClass->print();
    pMyClass[0].print();
    pMyClass[1].print();
    pMyClass[1000000].print();

    return 0;
}

【解析】
        这里需要明白类函数是如何被编译以及如何被执行的。
        对于类成员函数而言,并不是一个对象对应一个单独的成员函数体,而是此类的所有对象共用这个成员函数体,当程序被编译之后,此成员函数地址即已确定。我们常说,调用类成员函数时,会将当前对象的this指针传给成员函数。没错,一个类的成员函数体只有一份,而成员函数之所以能把属于此类的各个对象的数据区别开,就在于每次执行类成员函数时,都会把当前对象的this指针(对象首地址)传入成员函数,函数体内所有对类数据成员的访问,都会被转化为this->数据成员的方式。
        如果print函数里没有访问对象的任何数据成员,那么此时传进来的对象this指针实际上是没有任何用处的。这样的函数,其特征与全局函数并没有太大区别。但如果取消第14行的注释,由于print函数要访问类的数据成员data,而类的数据成员是伴随着对象声明而产生的。但是,我们只new了一个MyClass,显然,下标“1”和下标“1000000”的MyClass对象根据不存在,那么对它们的数据成员访问也显示是非法的。

【答案】

注释代码第14行,运行结果如下:

取消代码第14行注释后的输出:

 

面试题20 指针数组与数组指针的区别

【解析】
指针数组指一个数组里存放的都是同一个类型的指针,例如:int *a[10]
数组a里面存放了10个int*型变量,由于它是一个数组,已经在栈区分配了10个(int*)的空间,也就是32位机上是40个byte,
每个空间都可以存放一个int型变量的地址。这个时候,你可以为这个数组的每个元素初始化。
数组指针指一个指向一维或者多维数组的指针,例如:int *b = new int[10];
指针b指向含有10个整型数据的一维数组。注意,这个时候释放空间一定要delete[],否则会造成内存泄露。
参考下面的源代码:

#include <iostream>
using namespace std;

int main()
{
    int x1[4] = {1, 2, 3, 4};
    int x2[2] = {5, 6};
    int x3[3] = {7, 8, 9};
    int *a[2];
    int *b = x1;
    int i = 0;

    a[0] = x2;
    a[1] = x3;

    cout << "输出a[0]:";
    for(i = 0; i < sizeof(x2)/sizeof(int); i++)
    {
        cout << a[0][i] << "  ";
    }
    cout << endl;

    cout << "输出a[1]:";
    for(i = 0; i < sizeof(x3)/sizeof(int); i++)
    {
        cout << a[1][i] << "  ";
    }
    cout << endl;

    cout << "输出b:";
    for(i = 0; i < sizeof(x1)/sizeof(int); i++)
    {
        cout << b[i] << "  ";
    }
    cout << endl;

    return 0;
}

这个程序中有指针数组a和数组指针b。a里的两个指针元素分别指向了数组x2和x3,数组指针b指向了数组x1。输出如下:

总之,指针数组表示它是一个数组,并且数组中的每一个元素是指针,而数组指针表示它是一个指针,并且指向了一个数组。
【答案】
指针数组表示它是一个数组,并且数组中的每一个元素是指针。
数组指针表示这是一个指针,并且指向了一个数组。


面试题21 找错——指针数组和数组指针的使用

#include <stdio.h>

int main()
{
    char *str[] = {"Welcom", "to", "Fortemedia", "Nanjing"};
    char **p = str + 1;//p指向str[1]的地址
    str[0] = (*p++) + 2;//++的优化级高于*号  此时p指向了str[2]的地址
    str[1] = *(p+1);
    str[2] = p[1] + 3;//p指向的是str[2]的地址,str[2]的地址变了,所以p指向的地址也根着改修改了,p[0] = "jing"
    str[3] = p[0] + (str[2] - str[1]);
    printf(" str[0] = %s\n", str[0]);
    printf(" str[1] = %s\n", str[1]);
    printf(" str[2] = %s\n", str[2]);
    printf(" str[3] = %s\n", str[3]);

    return 0;
}

【解析】
本题的每次执行都较强地依赖于上一个语句执行的情况,好几次一个语句,同时修改str和p的值。
代码第5行结束时,str是下面数组的第一个值。
(1)第1个字符串的首地址的存放地址,标记为A,其内容为“Welcome”。
(2)第2个字符串的首地址的存放地址,标记为B,其内容为“to”。
(3)第3个字符串的首地址的存放地址,标记为C,其内容为“Fortemedia”。
(4)第4个字符串的首地址的存放地址,标记为D,其内容为“Nanjing”。
代码第6行结束时,p指向B
代码第7行结束时,p指向C。此时str[0]指向第4个字符串“Nanjing”后面的元素,因此其内容为空。
代码第8行结束时,p没有移动,str[1]指向p的后一个元素的地址,即D。
代码第9行,此时p[1]指向D。p[1]+3即指向字符串的元素的第4个元素,即"j"字符。此行执行之后,str[2]等于'j'的地址,而p是指向str[2]的地址的,因此,此时p指向的也是'j'的地址,即*p[0] = "jing"
代码第10行,由第8行和第9行可知str[2]-str[1]等于3,而p[0]指向‘j’的地址。因此str[4]指向“Nanjing”字符串中的最后一个字符'g'的地址。
【答案】


面试题22 函数指针与指针函数的区别

【解析】
        指针函数是指带指针的函数,即本质是一个函数,并且返回类型是某一类型的指针。其定义如下:
        返回类型标识符 *返回名称(形式参数表) { 函数体 }
        事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。
       函数指针是指向函数的指针变量,因而它本身首先应是指针变量,只不过该指针变量指向函数。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型的变量一样。
请看下面这个例子程序:

#include <iostream>
using namespace std;

int max(int x, int y)
{
    return (x > y ? x : y);
}

float *find(float *p, int x)
{
    return p + x;
}

int main()
{
    float score[] = {10, 20, 30, 40};
    int (*p)(int, int);
    float *q = find(score + 1, 1);
    int a;

    p = max;
    a = (*p)(1, 2);

    cout << "a = " << a << endl;
    cout << "*q = " << *q << endl;

    return 0;
}


       这里,函数find()被定义为指针函数,指针p被定义为函数指针类型。main函数中调用find()函数时,将数组中第2个元素的地址和偏移量1传入,返回的应该是数组中第3个元素的地址。对于指针p,在第21行被赋为max()函数的地址,因此在第22行使用指针p就能完成调用max()函数的目的。输出如下:


【答案】
指针函数是返回指针类型的函数。
函数指针是指向函数地址的指针。


面试题23 数组指针与函数指针的定义

定义下面的几种类型变量:
a.含有10个元素的指针数组
b.数组指针
c.函数指针
d.指向函数的指针数组
【答案】
a.int *a[10]
b.int *a = new int[10];
c.void (*fn)(int, int)
d.void (*array[10])(int, int);


面试题24 各种指针的定义

写出函数指针、函数返回指针、const指针、指向const的指针、指向const的const指针。
【答案】
void (*fn)(int, int),    fn是指向void max(int x, int y)类型的函数指针。
int *fn(int, int),        fn是返回int指针类型的函数
const int *ptr,            ptr是一个指向const的指针,指向一个常量
int* const ptr,            ptr是一个const指针。
const int* const ptr,    ptr是一个指向const的const指针,地址和地址所在的值都不能修改。


面试题25 代码改错——函数指针的使用

【解析】
这道程序题中函数指针的使用存在错误。
代码第14行,声明max函数方法错误。
在代码第16行中,p指向max函数地址,这里会出现指针不能转换的错误。p被声明为一个int*类型的指针,但是max地址却为(int*)(int, int)类型。
【答案】
正确的代码如下:

#include <iostream>
using namespace std;

int max(int x, int y)
{
    return x > y ? x : y;
}

int main()
{
    int (*p)(int, int);
    int a, b, c;
    int result;
    int max(int, int);

    p = max;
    cout << "Please input three integer " << endl;
    cin >> a >> b >> c;
    result = (*p)((*p)(a, b), c);
    cout << "result = " << result << endl;

    return 0;
}


面试题26 看代码写结果——函数指针的使用

#include <stdio.h>

int add1(int a1, int b1);
int add2(int a2, int b2);

int main(int argc, char *argv[])
{
    int numa1 = 1, numb1 = 2;
    int numa2 = 2, numb2 = 3;
    int(*op[2])(int a, int b);
    op[0] = add1;
    op[1] = add2;
    printf("op[0] = %d op[1] = %d\n", op[0](numa1, numa2), op[1](numb1, numb2));
    return 0;
}

int add1(int a1, int b1)
{
    return a1 + b1;
}

int add2(int a2, int b2)
{
    return a2 + b2;
}

【解析】
在代码第10行,定义了一个函数指针数组op,它含有两个指针元素。在第11行和第12行把这两个元素分别指向了add1和add2两个函数地址。最后在第13行打印出使用函数指针调用add1和add2这两个函数返回的结果
【答案】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值