【C/C++笔记】:易错难点2

基础知识点

1. 易误解:如果int a[5], 那么a与&a是等价的,因为两者地址相同。

解答:

    一定要注意a与&a是不一样的,虽然两者地址相同,但意义不一样,&a是整个数组对象的首地址,而a是数组首地址,也就是a[0]的地址,a的类型是int[5],a[0]的类型是int,因此&a+1相当于a的地址值加上sizeof(int) * 5,也就是a[5],下一个对象的地址,已经越界了,而a+1相当于a的地址加上sizeof(int),即a[1]的地址。

2. 如何在类中定义常量成员并为其初始化?

解答:只能在初始化列表里对const成员初始化,像下面这样:

class CBook {
public:
    const double m_price;
    CBook() :m_price(8.8) { }
};

下面的做法是错误的:

class CBook {
public:
    const double m_price;
    CBook() {
        m_price = 8.8;
    }
};

3. 在定义类的成员函数时使用mutable关键字的作用是什么?

解答:

   当需要在const方法中修改对象的数据成员时,可以在数据成员前使用mutable关键字,防止出现编译出错。例子如下:

class CBook {
public:
    mutable double m_price;  // 如果不加就会出错
    CBook(double price) :m_price(price) { }
    double getPrice() const; // 定义const方法
};
double CBook::getPrice() const {
    m_price = 9.8;
    return m_price;
}

4. 在C++中,如果确定了某一个构造函数的创建过程,在该构造函数中如果调用了其它重载的构造函数,它将不会执行其它构造函数的初始化列表部分代码,而是执行函数体代码,此时已经退化成普通函数了。

样例代码如下:

class CBook {
public:
    double m_price;
    CBook() {
        CBook(8.8);
    }
    CBook(double price) : m_price(price) { }
};
int main() {
    CBook c;
    cout << c.m_price << endl;  // 此时并不会输出理想中的8.8
}

选择题

1. 函数栈数组越界覆盖问题

下面程序输出什么:

正确答案:

int function(const int src[16]){
    int val1=0;
    int dst[16]={0};
    int val2=0;
    const int *psrc=src;
    //问题1:如下代码行输出什么?
    fprintf(stdout,"size of src=%lu\n",sizeof(src));
    //问题2:如下代码有什么副作用?
    for(int i=0;i<=16;i++){
     dst[i]=*psrc+i;
     psrc++;
    }
    return 0;
}

A.16,val2会被覆盖   B.其他三项都不对   C.8,val2会被覆盖    D.8,val1会被覆盖

解析:

  🧩1.首先要看编译器,不同的编译器结果可能不一样;
  🧩2.sizeof(src),数组名作为形参时,是作为地址来看待的,下标[ ]中的数组是无用的,可以看作是指针,即求指针的大小;

  🧩3.一般情况,val1会被覆盖,变量入栈顺序与申明次序相同,栈的地址是从高到低生长的,而数组是从低地址到高地址的,也就是说dst[0]的地址小于dst[1]的地址。所以val1被覆盖。

2. C和C++中const问题

在c++中输出是多少?

正确答案:A

const int i = 0; 
int *j = (int *) &i; 
*j = 1; 
printf("%d,%d", i, *j)

A. 0,1             B. 1,1               C. 1,0                 D. 0,0

     解析:首先要注意这是C++中,const变量是编译时的常量,可以像#define定义的常量一样使用。故C++中const变量的值在编译时就已经确定了,直接对const变量进行了值的替换,在汇编阶段,引用到const修饰的量的地方会直接以值替换掉。实际上常量值已经改变了,只不过是被提起替换了而已。还要注意这是局部的const,全局的就不能修改了

🧩1.C中const是运行时const,编译时只是定义,在运行才会初始化,所以可以通过指针修改const值。这也是为什么不能用const变量作为数组定义的大小的原因。
🧩2.C++中涉及到常量折叠:它指const变量(即常量)值 放在编译器的符号表中 ,计算时编译器直接从表中取值,省去了访问内存的时间,从而达到了优化。

🧩3.const对存放位置没有影响,有影响的是static修饰的变量。

🧩4.const全局变量存储在全局存储空间,其值只有可读属性,不能修改;
      const局部变量存储在堆栈中,可通过指针修改其值;
      const变量在预处理是处理,编译器只对其值读取一次。

3、define问题

以下代码的输出结果是?

正确答案:

#define a 10
void foo(); 
main(){

  printf("%d..",a);
   foo();
   printf("%d",a);
}
void foo(){
   #undef a
   #define a 50
}

解析:

 define在预处理阶段就把main中的a全部替换为10了.
另外,不管是在某个函数内,还是在函数外,define都是从定义开始知道文件结尾,所以如果把foo函数放到main上面的话,则结果会是50 ,50。而#undef a #define a 50,作用域是接下来的代码。

4、局部静态对象和全局对象析构顺序

设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?

正确答案:A B C D

C c;
void main()
{
    A*pa=new A();
    B b;
    static D d;
    delete pa;
}

解析:

之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。

5、编译与预编译

下列说法错误的是:

正确答案:C

A: C语言由源代码生成的各阶段如下,C源程序-编译预处理-编译-优化程序-汇编程序-链接程序-可执行文件

B: 常见的预编译指令有#include,#define,#if、#else和#endif

C: 编译程序可以识别一些特殊的符号,比如__LINE__ ,表示当前行号的整数,这些是在编译阶段处理的

D: #define定义宏,可以多次使用

解析:

🧩预处理过程读入源代码之后,会检查代码里包含的预处理指令,完成诸如包含其他源文件、定义宏的处理。C选项中,诸如__DATA__、__FILE__、__LINE__、__STDC_VERSION__、__TIME__等,属于预定义宏,因此在预编译处理阶段。

下列正确的是

正确答案:B

A. 预处理命令行必须使用分号结尾       B. 凡是以#号开头的行,都被称为编译预处理命令行

C. 预处理命令行不能出现在程序的最后一行    D. 预处理命令行的作用域是到最近的函数结束处

解析:

 A 预处理命令行不能以分号结尾

 C 预处理命令行可以出现在程序的最后一行

 D 预处理命令行作用域是整个文件

6. 内置变量初始化问题

int *p1 = new int[10]; 
int *p2 = new int[10]();

p1申请的空间里的值是随机值,p2申请的空间里的值已经初始化为0。

7. 数据间转换

unsigned char a[3];
void main()
{
    for (int i=0;i<3;i++)
    {
        a[i] = 1-i;
    }
    printf("%u%u%u",a[0],a[1],a[2]);
    system("pause");
}

解析:关键在于a[2],-1的表示是取反加一,所以结果为2^8-1 = 255.

8、运算符间的优先级

int a =3;int b =7;
printf("%d",a&3==b&&7);

解析:先&再==再&&,结果0

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值