练习(五)

1、

[单选题]
以下关于指针的说法,正确的是()
  • int *const p与int const *p等价
  • const int *p与int *const p等价
  • const int *p与int const *p等价
  • int *p[10]与int (*p)[10]等价

2、

[单选题]
设有以下函数void fun(int n,char *s)(......),则下面对函数指针的定义和赋值均是正确的是:()
  • void (*pf)(int,char);   pf=&fun;
  • void (*pf)(int n,char *s);   pf=fun;
  • void *pf();  *pf=fun;
  • void *pf();  pf=fun;

3、

[单选题]
有以下程序段:
1
2
3
4
5
char *p, *q;
p = ( char *)malloc(sizeof( char ) * 20 );
q = p;
scanf(“%s %s”, p, q);
printf(“%s %s\n”, p, q);
若从键盘输入:abc def↙,则输出结果是( )
  • def def
  • abc def
  • abc d
  • d d

4、

[单选题]
下面叙述错误的是( )
  • acX与acY的内容可以修改
  • szX与szY指向相同的地址
  • acX占用的内存空间比acY占用的大
  • szX的内容修改后,szY的内容也会被更改

5、

[单选题]
在32位机器上,下列代码中
1
2
3
4
5
6
7
8
9
10
11
12
class
 
       int i; 
       union
      
           char buff[13]; 
           int i; 
       }u; 
       void foo() {    } 
       typedef char * (*f)( void *); 
       enum {red, green, blue} color; 
  }a; 

sizeof(a)的值是()


  • 20
  • 21
  • 22
  • 24
  • 非以上选项

6、

[单选题]
一个函数定义的返回值是float,它不能在return语句中返回的值的类型是: 
  • char
  • int
  • float
  • long
  • double
  • 以上皆可以

    7、

    [不定项选择题]
    下面哪些属于使用"常引用"的原因?
    • 提高程序的效率
    • 节省存储空间
    • 保护传递给函数的数据不在函数中被改变
    • 以上都不正确

    8、

    [单选题]

    若有以下程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
    int  f( int  a[ ], int  n)
    {
         if (n >  1 )
         {
             int  t;
             t=f(a, n- 1 );
             return  t > a[ n - 1  ] ? t:a[n- 1 ];
         }
         else
             return  a[ 0 ];
    }
    main()
         int  a[ ]= { 8 , 2 , 9 , 1 , 3 , 6 , 4 , 7 , 5 };
         printf( "%d\n" , f(a,  9 ));
    }

    则程序的输出结果是?

    • 9
    • 1
    • 8
    • 5

    9、

    [单选题]
    内联可能会寻致二进制可执行文件尺寸变大吗?
    10、链接: https://www.nowcoder.com/questionTerminal/b96e32b66109470bba0a42dbea7c9ece
    来源:牛客网

    [单选题]
    对以下数据结构中data的处理方式描述正确的是()
    1
    2
    3
    4
    5
    struct Node
    {
        int size;
        char data[0];
    };
    • data将会被编译成一个char *类型指针
    • 全部描述都不正确
    • 编译器会认为这就是一个长度为0的数组,而且会支持对于数组data的越界访问
    • 编译器会默认将数组data的长度设置为1

    11、

    [单选题]
    在C++中,为了让某个类只能通过new来创建(即如果直接创建对象,编译器将报错),应该()
    • 将构造函数设为私有
    • 将析构函数设为私有
    • 将构造函数和析构函数均设为私有
    • 没有办法能做到
    12、

    链接:https://www.nowcoder.com/questionTerminal/40a2ed91959b4586b8a7255bbabdad7d
    来源:牛客网

    [不定项选择题]
    下面的说法那个正确
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #define NUMA 10000000
    #define NUMB 1000
    int a[NUMA], b[NUMB];
      
    void pa()
    {
         int i, j;
         for (i = 0; i < NUMB; ++i)
             for (j = 0; j < NUMA; ++j)
                 ++a[j];
    }
    void pb()
    {
         int i, j;
         for (i = 0; i < NUMA; ++i)
             for (j = 0; j < NUMB; ++j)
                 ++b[j];
    }

    • pa 和 pb 运行的一样快
    • pa 比 pb 快
    • pb 比 pa 快
    • 无法判断
    13、

    链接:https://www.nowcoder.com/questionTerminal/8c0ff66458504501929b6a37cacf4752
    来源:牛客网

    [不定项选择题]
    下列关于内存分配和释放的函数及其区别描述正确的有?
    • C++语言的标准内存分配函数:malloc,calloc,realloc,free等。
    • C中为new/delete函数。
    • malloc和calloc的区别是1块与n块的区别和初始化
    • realloc调用形式为(类型*)realloc(*ptr,size):将ptr内存大小增大到size。
    14、

    链接:https://www.nowcoder.com/questionTerminal/170c98fb9ade4e01974ef9703ab52575
    来源:牛客网

    [单选题]
    下列关于 clone 和 fork 的区别描述正确的有?
    • clone和fork最大不同在于fork不再复制父进程的栈空间,而是自己创建一个新的。
    • clone和fork最大不同在于clone不再复制父进程的栈空间,而是自己创建一个新的。
    • clone是fork的升级版本,不仅可以创建进程或者线程,还可以指定创建新的命名空间(namespace)、有选择的继承父进程的内存、甚至可以将创建出来的进程变成父进程的兄弟进程等等
    • fork是clone的升级版本,不仅可以创建进程或者线程,还可以指定创建新的命名空间(namespace)、有选择的继承父进程的内存、甚至可以将创建出来的进程变成父进程的兄弟进程等等

    15、链接:https://www.nowcoder.com/questionTerminal/e3954b75f99c43a5ab27b613329fabce
    来源:牛客网

    单选题]
    下面程序的输出结果是
    1
    2
    3
    4
    5
    6
    #include<iosteam.h>
    void main(){
         int n[][3] = {10,20,30,40,50,60};
         int (*p)[3];
         p=n;
         cout<<p[0][0]<< "," <<*(p[0]+1)<< "," <<(*p)[2]<<endl;
    1
    }

    • 10,30,50
    • 10,20,30
    • 20,40,60
    • 10,30,60

    16、链接:https://www.nowcoder.com/questionTerminal/11e2ac133a0b4323a54cefd1b80adefd
    来源:牛客网

    [单选题]
    下列关于C/C++的宏定义的说法中,不正确的是:
    • 宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用 const常量
    • 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
    • 相对于函数调用,宏定义可以提高程序的运行效率
    • 宏定义不检查参数正确性,会有安全隐患

    17、

    链接:https://www.nowcoder.com/questionTerminal/cb59a271f49b4f03804122d5786e8e2c
    来源:牛客网

    [单选题]
    有一个类A,其数据成员如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {
    ...
    private :
          int a;
    public :
          const int b;
          float * &c;
          static const char * d;
          static double * e;
    };
    则构造函数中,成员变量一定要通过初始化列表来初始化的是:______。
    • a b c
    • b c
    • b c d e
    • b c d
    • b
    • c
    18、链接: https://www.nowcoder.com/questionTerminal/45bb35c18c434829af740c0d843fcb1e
    来源:牛客网

    [单选题]
    关于以下代码,哪个说法是正确的?
    1
    2
    3
    4
    5
    6
    7
    8
    myClass::foo(){
         delete this ;
    }
    ..
    void func(){
         myClass *a = new myClass();
         a->foo();
    }
    • 它会引起栈溢出
    • 都不正确
    • 它不能编译
    • 它会引起段错误

    19、链接:https://www.nowcoder.com/questionTerminal/6027723bb7f945339125f20d183138fe
    来源:牛客网

    [不定项选择题]
    下述有关c++的虚类和java接口的描述,说法错误的是?
    • c++虚类相当与java里面的抽象类
    • c++中没有接口的概念,与之对应的是纯虚类,对应的是java的接口
    • 纯虚函数和虚函数的区别在于前者只包含定义,而后者还可以包含函数体。
    • 一个抽象类和接口中的方法必须是抽象方法

    20、链接:https://www.nowcoder.com/questionTerminal/05ac9639c3df4c3aaba673c053efb50b
    来源:牛客网

    [不定项选择题]
    下面有关C++的类和C里面的struct的描述,正确的有?
    • 在C++中,来自class的继承默认按照private继承处理,来自struct的继承默认按照public继承处理
    • class的成员默认是private权限,struct默认是public权限
    • c里面的struct只是变量的聚合体,struct不能有函数
    • c++的struct可有构造和析构函数


    解析:

    1、

    那如何区分这几类呢? 带两个const的肯定是指向常量的常指针,很容易理解,主要是如何区分常量指针和指针常量:

    一种方式是看 * 和 const 的排列顺序,比如
         int const* p; //const * 即常量指针
         const int* p; //const * 即常量指针
         int* const p; //* const 即指针常量
    还一种方式是看const离谁近,即从右往左看,比如
         int const* p; //const修饰的是*p,即*p的内容不可通过p改变,但p不是const,p可以修改,*p不可修改;
         const int* p; //同上
         int* const p; //const修饰的是p,p是指针,p指向的地址不能修改,p不能修改,但*p可以修改;

    2、

    B. 函数指针只需要把fun 改成(*pf) ,赋值 直接 pf = fun;即可 函数名赋值.指针函数赋值时候,可以直接用函数名赋值(书上一般都是这样赋值的) .但是也可以用&fun ,取地址操作符复制给函数指针.   pf = &fun;也是可以的.亲测过

    但是A答案,在char参数后面少了一个*,所以是错的.如果加上*.也是正确的

    3、p和q是指向同一地址,故答案肯定是输出一样的字符串,输入字符串后,p q最开始指向abc ,当输入def后,开始的地址被覆盖,一起指向def

    4、

    正确答案应该选 D,而不是选B
    char *szX = "abc";与char *szY = "abc";
    "abc"是保存在文字常量区,属于字符串常量,所以该数据不能修改,默认是只读属性。
    因此不能通过指针szX或szY的指向来修改此值,而且szX和szY指向字符串常量"abc"相同的地址,可推出选D而不选B。

    char acX[] = "abc";
    "abc"是保存在栈空间数组里. 其数组名为acX,,数组名为数组的首地址(属于指针常量),
    因此可以通过 acX[i] = 'x' 或 *(acX+i) = 'x' 的形式来修改数组内容。

    链接:https://www.nowcoder.com/questionTerminal/606213e60b0c48159967c36fe6d69772
    来源:牛客网

    关于字符串指针不可修改的问题

    C/C++ code

    char*m = " hello "; *(m+ 1) = ' s '; for(;*m != ' \0 ';m++){ printf( " %c\n ",*m); }

    但是出运行时错误。
    ----------------------------------------------------------
    我用数组下标的方式是可以修改的:

    C/C++ code
    int i = 0; char w[] = " hello "; w[ 1] = ' s '; while(w[i] != ' \0 '){ printf( " %c\n ",w[i]); i++; }
     
    答复一:
    char *m = "hello";
    "hello"保存在静态数据区,该数据不能修改.
    由指针m指向. 不能通过指针m来修改静态数据区的值.

    char w[] = "hello";
    "hello"保存在栈空间数组里. 数组名为w, 函数名为数组的首地址.
    可以通过w[i]='a', 或*(w+i)='a'的形式来修改数组内容.
    疑问:
    这种说法是错误的。如下:
     static int a = 10;
     int *b = &a;
     *b = 20;
     cout<<a<<endl;
    输出结果为20.我们依然通过指针修改了存储在静态存储区里的数据源。
     
    //---------------------------------------------------
    答复二:
     
    字符串“hello”本身就是一个常量字符指针,而对于指针m,无非就是一个地址的拷贝,也就是“hello”地址的拷贝,相当于m指向一个字符串常量,字符串常量是不予许改变的!

    而对于w[]来说就不一样了,虽然hello本身是常量,不过此时拷贝给w[]的不是地址,而是内容,也就是“hello”,也就是w本身拥有一个自己的hello副本,可以对其进行想要的合法操作,比如改变等!!
     
     
    回复三:
    char *m = "hello";
    "hello"保存在文字常量区,该数据不能修改,默认有只读属性.
    由指针m指向. 不能通过指针m来修改此值.

    char w[] = "hello";
    "hello"保存在栈空间数组里. 数组名为w, 函数名为数组的首地址.
    可以通过w[i]='a', 或*(w+i)='a'的形式来修改数组内容.

    5、

    联合体
    用途:使几个不同类型的变量共占一段内存(相互覆盖)
    结构体是一种构造数据类型
    用途:把不同类型的数据组合成一个整体-------自定义数据类型

    结构体的内存分配:
    结构体在内存中分配一块连续的内存,但结构体内的变量并不一定是连续存放的,这涉及到内存对齐。
    原则1  数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
    原则2  结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
    原则3  收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    联合的内存分配:
    Union的大小为其内部所有变量的最大值,并且按照联合体中 类型 最大值的整数倍进行内存对齐:
    1
    2
    3
    4
    5
    union U
           {
               char buff[13];
               int i;
           }u;
    首先按照char buff[13]分配13个字节,然后按照int的4个字节对齐,最终sizeof(u2)=16; (大于等于13且能被4整除的最小的数,即16)
    根据原则2,u在class内要从u内部 类型最大的整数的倍开始储存,本题就是从4开始存16个字节。这时已经是4+16=20

    成员函数不管是否为空,都不会占用空间,因为是this指针调用,有虚函数时除外, vtbl(virtual table)和vptr(virtual table pointer)会多出一个指针

    typedef 是声明,枚举则是int 实现.20+4=24
    6、

    都可以,不过会有警告 warning C4244: “return”: 从“double”转换到“float”,可能丢失数据

    7、

    在C++面向对象程序设计中,经常用常指针和常引用作函数参数。这样既能保证数据安全,是数据不能被随意修改,在调用函数时又不必建立实参的拷贝。

    8、解析:首先是执行f(a,9),递归执行(a,8)直到(a,2),得到a[0]=8,然后判断a[0]是否大于a[1 ],本题实际上就是求出数组a元素的最大值。注意递归---结果一定要返回之前的函数。即第一次返回a[0],即t=a[0]----然后执行return语句a[0]>a[1];则第二次返回a[0]赋值给t,----执行return 语句 a[0]>a[2],由于8>9不成立,则返回a[2]。.........然后一直进行下去。因为9是最大的,因为在后面的执行retrun 语句,都一直返回的是9。

    9、

    链接:https://www.nowcoder.com/questionTerminal/6e615ac0d5a0494ca3e310accc84da6a
    来源:牛客网

    内联函数有什么用?

    当编译器内联展开一个函数调用时,该函数的代码会被插入到调用代码流中(概念上类似于展开#define宏)。能够改善性能,因为优化器能够顺序集成被调用代码,即将被调用代码直接优化进调用代码中。

    函数设定为内联,这只是个请求,而编译器可以忽略它。编译器可能会展开内联函数调用,也可能不展开。(这看上去非常模糊,但不要为之沮丧。这种灵活性其实有很大优点:这可以让编译器能够区别对待很长的函数和短的函数,另外如果选择了正确的编译选项,还能使编译器生成易于调试的代码。)
    内联函数能改善性能么?

    可能会,也可能不会。有时可以。也许可以。

    内联函数可能会使代码速度更快,也可能使速度变慢。可能会使可执行文件变大,也可能变小。可能会导致系统性能下降,也可能避免性能下降。内联函数可能(经常是)与速度完全无关。

    内联函数可能会使代码速度更快正如上面所说,顺序集成可能会移除很多不必要的指令,这可能会加快速度。

    内联函数可能会使代码速度更慢过多的内联可能会使代码膨胀,在使用分页虚拟内存的系统上,这可能会导致性能下降。换句话说,如果可执行文件过大,系统可能会花费很多时间到磁盘上获取下一块代码。

    内联函数可能会增加可执行文件尺寸:这就是上面所说的代码膨胀。例如,假设系统有100个内联函数,每个展开后有100字节,并且被调用了100次。这就会增加1MB的大小。增加这么1MB会导致问题吗?谁知道呢,但很可能就是这1MB导致系统性能下降。

    内联函数可能会减少可执行文件尺寸:如果不内联展开函数体,编译器可能会要产生更多代码来压入/弹出寄存器内容和参数。对于很小的函数来说会是这样。如果优化器能够通过顺序集成消除雕大量冗余代码的话,那么对大函数也会起作用(也就是说,优化器能够使大函数变小)。

    内联函数可能会导致系统性能下降内联可能会导致二进制可执行文件尺寸变大,由此导致系统性能下降。

    内联函数可能会避免系统性能下降:即使可执行文件尺寸变大,当前正在使用的物理内存数量(即需要同时留在内存中的页面数量)却仍然可能降低。当f()调用g()时,代码经常分散在2个不同的页面上。当编译器将g()的代码顺序集成到f()后,代码通常会放在一个页面上。

    内联函数可能会降低缓存的命中率:内联可能会导致内层循环跨越多行的内存缓存,这可能会导致内存和缓存频繁交换,从而性能下降。

    内联函数可能会提高缓存的命中率:内联通常能够在二进制代码中就近安排所用到的内容,这可能会减少用来存放内层循环代码的缓存数量。最终这会使CPU密集型程序跑得更快。

    内联函数可能与速度无关大多数系统不是CPU密集型的,而使I/O密集型的、数据库密集型的或是网络密集型的。这表明系统的瓶颈存在于文件系统、数据库或网络。除非你的“CPU速度表”指示是100%,否则内联函数可能不会使你的系统速度更快。(即使是CPU密集型的系统,也只有在被用到瓶颈之处时,内联才会有帮助。而瓶颈通常只存在于很少一部分代码中。)

    内联函数如何在安全和速度上取得折衷?

    在 C 中,你可以通过在结构中设置一个 void* 来得到“封装的结构”,在这种情况下,指向实际数据的 void* 指针对于结构的用户来说是未知的。因此结构的用户不知道如何解释void*指针所指内容,但是存取函数可以将 void* 转换成适当的隐含类型。这样给出了封装的一种形式。
    不幸的是这样做丧失了类型安全,并且即使仅仅是访问结构体中的一个很不重要的字段也必须进行函数调用。(如果你允许直接存取结构的域,那么任何人都能直接存取该结构体了,因为他们必须了解如何解释 void* 指针所指内容;这样将使改变底层数据结构变的困难)。

    虽然函数调用开销是很小的,但它会被累积。C++类允许函数调用以内联展开。这样让你在得到封装的安全性时,同时得到直接存取的速度。此外,内联函数的参数类型由编译器检查,这是对 C 的 #define 宏的一个改进
    和宏不同的,还有内联函数的参数类型被检查,并且被正确地进行必要的转换。
    10、

    链接:https://www.nowcoder.com/questionTerminal/b96e32b66109470bba0a42dbea7c9ece
    来源:牛客网

    看不懂char data[0];请去百度  柔性数组,它只能放在结构体末尾,是
    申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配 请仔细理解后半部分,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!
     对于0长数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等:
    注意:构造缓冲区就是方便管理内存缓冲区,减少内存碎片化,它的作用不是标志结构体结束,而是扩展
    柔性数组是C99的扩展,简而言之就是一个在struct结构里的标识占位符(不占结构struct的空间)
    链接: https://www.nowcoder.com/questionTerminal/b96e32b66109470bba0a42dbea7c9ece
    来源:牛客网

    对于变长数组和变长结构体,这是在C99才加入标准的。
    对于变长数组,举个例子就能解释了:
    int main() {
        int n = 10;
        int arr[n];
    }
    对于变长结构体就比较复杂一点(也不算很复杂:))。很多人其实会有这种疑惑,就是为什么不用指针去代替变长结构体,比如:
    structNode
    {
       intsize;
       char*data;
    };
    就这个问题,我总结了一下用指针和用变长结构体的区别:
    1.在位置方面:指针可以放在任何地方,但是变长结构体的变长部分一定要放在结构体的最后。
    2.在内存占用方面:指针会占一个指针的大小的内存空间,但是变长数组是不占内存的,它只是一个占位符。
    3.在内存布局方面:指针指向的内存和结构体的内存可以是不连续的,但是变长部分和结构体的内存必须是连续。
    4.在内存释放方面:使用指针,就要先释放指针所指的内存在释放整个结构体的内存,否则会照成内存泄露。
    但是使用变长结构体直接释放整个结构体的空间就可以了
    5.一个限制:指针可以用在C++的类中,但是变长结构体就不可以了。因为有些编译器会将一些额外的信息放在类的最后,
    比如vptr或者虚基类的内容,使用了变长的类,就会把这部分的值改变,这种行为是未定义的,谁也不知道会发生什么。
    11、

    链接:https://www.nowcoder.com/questionTerminal/9ca9a4991164463b90b6ba0fef227030
    来源:牛客网

     译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。 因此, 将析构函数设为私有,类对象就无法建立在栈(静态)上了,只能在堆上(动态new)分配类对象 

    12、链接:https://www.nowcoder.com/questionTerminal/40a2ed91959b4586b8a7255bbabdad7d
    来源:牛客网

    转自:http://www.cnblogs.com/jfei1982/articles/891716.html一楼回复.

    其实,这个问题的主要原因是CPU内部的指令执行机制。现在,基本上CPU内部都有分支指令预测,就是当执行(现在大多将这一阶段提前到预取指令时执行)到转移指令时,都会直接从分支目标缓存(BTB)中取出目标指令的地址,然后将要执行的指令提前预取到CPU的指令预取指令队列中。这样,显然大大提高了效率。举个例子,一个10次的一层循环在执行时,除了在第一次和最后一次会预测错误外,其他8次都会预取成功,避免了执行转移指令时重新取出新指令造成的时间浪费。 
    所以,当有两层循环,外层循环数为A,内层为B,A远大于B,那么最终造成的预测错误数为A*2+2,而如果外层数为B,内层数为A,预测错误数为B*2+2,显然后者要节省更多时间,而且这个时间是很可观的。A比B越大,这个时间差越明显。 

    13、

    函数原型:void *calloc(size_t n, size_t size); 功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。 malloc,relloc,calloc,free是C语言的; new,delete是C++; relloc动态内存调整, 指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。 新的大小可大可小(但是要注意,如果新的大小小于原内存大小,可能会导致数据丢失,慎用!)

    14、链接:https://www.nowcoder.com/questionTerminal/170c98fb9ade4e01974ef9703ab52575
    来源:牛客网

    fork()是全部复制
    vfork()是共享内存
    clone()是可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的clone_flags来决定。另外,clone()返回的是子进程的pid。

    15、链接:https://www.nowcoder.com/questionTerminal/e3954b75f99c43a5ab27b613329fabce
    来源:牛客网

    nt (*p)[3];
    这里首先确定:p是一个指针,一个指向数组的指针。
    p = &(p[0])       p是二维指针
    p[0] = &(p[0][0]) p[0]是一维指针

    p[0] + 1表示在列上移动。  e.g:  p[0] + 1 = &p[0][0] + 1 = &p[0][1]
    p + 1   表示在行上移动。  e.g: p + 1 = &(p[0]) + 1 = &p[1]

    因此:
    *(p[0]+1) = p[0][1] = 20
    (*p)[2] = p[0][2] = 30

    16、

    根据effective C++ 中条款03 中,尽可能使用Const ,将某些东西声明为const可帮助编译器侦测出错误方法,const 可以被施加于任何作用于内的对象,函数参数,函数返回类型,成员函数本体。
    另外:
    宏定义的常量是在预处理阶段展开,const常量是在编译运行阶段使用
    宏定义的常量没有类型,不做任何类型检查;const常量是有具体类型的,在编译运行阶段会做类型检查.
    为了代码健壮性起见,还是尽量使用const,故 A是错的

    使用const比使用define有一下几种好处:

    (1)const会进行数据类型检查,而define不会


    (2)const效率高,因为const定义的常量,没有在内存中存储,而是在符号表中,每次访问这个数据的时候,少了从内存中读取和存储过程,效率高。

    因此尽量还是使用const常量

    17、

    链接:https://www.nowcoder.com/questionTerminal/cb59a271f49b4f03804122d5786e8e2c
    来源:牛客网

    选择B:
    构造函数初始化时必须采用初始化列表一共有三种情况,  
    1.需要初始化的数据成员是对象(继承时调用基类构造函数)  
    2.需要初始化const修饰的类成员  
    3.需要初始化引用成员数据
    因为static属于类并不属于具体的对象,所以 static成员是不允许在类内初始化的,那么static const 成员是不是在初始化列表中呢?
    答案是NO
    一是static属于类,它在未实例化的时候就已经存在了,而构造函数的初始化列表,只有在实例化的时候才执行。
    二是static成员不属于对象。我们在调用构造函数自然是创建对象,一个跟对象没直接关系的成员要它做什么呢

    初始化从无到有,创建了新对象;如:string foo = "Hello World!"
    赋值没有创建新对象,而是对已有对象赋值。 如:string bar; bar = "Hello World!"

    有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。
    如果成员是 const或者是 引用的话,必须将其初始化。类似的,当 成员属于某种类类型且该类没有定义默认构造函数时,也必须将这个成员初始化。(比如:类A中有一成员是B b,但类B中没有定义默认构造函数时,就必须对A中b成员进行初始化)

    随着构造函数体一开始执行(即大括号里面部分),初始化就完成了(构造函数体内只是赋值操作)。因此,上面三种情况,比如初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值,形如:ConstRef::ConstRef(int n) : i(n), j(n) { }
    18、

    链接:https://www.nowcoder.com/questionTerminal/45bb35c18c434829af740c0d843fcb1e
    来源:牛客网

    在类的成员函数中能不能调用delete this?答案是肯定的,能调用,而且很多老一点的库都有这种代码。假设这个成员函数名字叫release,而delete this就在这个release方法中被调用,那么这个对象在调用release方法后,还能进行其他操作,如调用该对象的其他方法么?答案仍然是肯定 的,调用release之后还能调用其他的方法,但是有个前提:被调用的方法不涉及这个对象的数据成员和虚函数。说到这里,相信大家都能明白为什么会这样 了。

    根本原因在于delete操作符的功能和类对象的内存模型。当一个类对象声明时,系统会为其分配内存空间。在类对象的内存空间中,只有数据成员和虚函数表指针,并不包含代码内容,类的成员函数单独放在代码段中。在调用成员函数时,隐含传递一个this指针,让成员函数知道当前是哪个对象在调用它。当 调用delete this时,类对象的内存空间被释放。在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。

    为什么是不可预期的问题?delete this之后不是释放了类对象的内存空间了么,那么这段内存应该已经还给系统,不再属于这个进程。照这个逻辑来看,应该发生指针错误无访问权限之类的令系统崩溃的问题才对啊?这个问题牵涉到操作系统的内存管理策略。delete this释放了类对象的内存空间,但是内存空间却并不是马上被回收到系统中,可能是缓冲或者其他什么原因,导致这段内存空间暂时并没有被系统收回。此时这段内存是可以访问的,你可以加上100,加上200,但是其中的值却是不确定的。当你获取数据成员,可能得到的是一串很长的未初始化的随机数;访问虚函数表,指针无效的可能性非常高,造成系统崩溃。


    大致明白在成员函数中调用delete this会发生什么之后,再来看看另一个问题,如果在类的析构函数中调用delete this,会发生什么?实验告诉我们,会导致堆栈溢出。原因很简单,delete的本质是“为将被释放的内存调用一个或多个析构函数,然后,释放内存” (来自effective c++)。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃

    19、

    1、一个子类只能继承一个抽象类(虚类),但能实现多个接口;
    2、一个抽象类可以有构造方法,接口没有构造方法;
    3、一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有声明;
    4、一个抽象类可以是public、private、protected、default,
       接口只有public;
    5、一个抽象类中的方法可以是public、private、protected、default,
       接口中的方法只能是public和default
    20、



    链接:https://www.nowcoder.com/questionTerminal/05ac9639c3df4c3aaba673c053efb50b
    来源:牛客网

    C++的类具有数据封装,其包含属性访问级别可以为Private,public 和protect,还具有实现类接口功能和辅助功能的操作函数,而struct属性访问全县只有public,没有数据封装功能,也就没有实现信息隐藏着一面向对象的思想的机制,struct本身不含有操作函数, 只有数据



    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    HySmiley

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值