C语言指针内存类错题

来源:互联网 发布:淘宝未发货退款要多久 编辑:程序博客网 时间:2024/05/22 00:54

一、C语言

(1.1)指针内存类

(1.1.1)内存使用型

有以下程序

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int fun( char s[ ] )
{
    char *p = s ;
    while ( *p ! = 0 ) p+ +;
    return ( p - s) ;
}
main()
{
    printf ( "% d\n ", fun ( "OABCDEF" ) ) ;
}

程序运行后的输出结果是?

  • 1
  • 6
  • 7  传入数组引用,退化为指针,指向头字符
  • 0


如下代码输出结果是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
char*myString()
{
    charbuffer[6] = {0};
    char*s = "Hello World!";
    for(inti = 0; i < sizeof(buffer) - 1; i++)
    {
        buffer[i] = *(s + i);
    }
    returnbuffer;
}
intmain(intargc, char**argv)
{
    printf("%s\n", myString());
    return0;
}
  • Hello
  • Hello World!
  • Well
  • 以上全部不正确
当函数返回值之后,其函数内部的栈空间均会被销毁;
在函数内部,若程序员没有为指针分配空间,则函数退出时,其栈空间也就不存在了;



1
2
3
4
5
6
7
8
9
10
11
voidgetmemory(char*p)
{
    p=(char*)malloc(100);
}
voidtest(void)
{
   char* str = null;
   getmemory(str);
   strcpy(str,”hello,world”);
   printf(str);
}
请问运行test函数会有什么样的结果? 
  • 编译错误
  • 输出"hello world"
  • 输出""
  • segmentation fault
指针p作为函数的参数,在函数中直接使用指针而不是指针所指向的数据,那么是不能改变指针的,只是将传入的实参作为形参的一个副本,实参本身并没有发生任何改变
 strcpy ( str ,”hello , world ”); 写越界,造成segmentation fault。


 请指出以下程序的错误: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void GetMemory(char**p, intnum)
{
    if(NULL==p && num<=0)
        return;
    *p=(char*)malloc(num); //1
    return;
}
voidmain(void){
    char*str=NULL;
    GetMemory(&str,100); //2
    if(NULL!=str){
        strcpy(&str,"hello"); //3
        printf(str);
    }
    returntrue//4
}

3处的错误在于strcpy(para1,para2)中的para1位char*类型,而str本身就是char*类型,&str为char**类型
4处的错误是:main函数是个无返回值的函数void main(){},return true表示bool main(){}
1处没有错误,malloc(int)函数就是这么用的
2处也没有错误,符合GetMemory()函数的定义格式,str本身是char*,那么&str就是char**



下面程序段的输出结果是
1
2
3
char*p1 = ”123”, *p2 = ”ABC”, str[50] = “xyz”;
strcpy(str + 2, strcat(p1, p2));
printr(“s\n”, str);
  • xyz123ABC
  • z123ABC
  • xy123ABC
  • 出错
p1没有足够空间保存连接后的字符串,出错。

下述程序有什么问题?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include   <string.h>
#include   <stdio.h> 
#include   <stdlib.h> 
voidgetmemory(char*p)  {    
    p=(char*) malloc(100);    
    strcpy(p,"hello world");  
}  
intmain( ) 
{    
    char*str=NULL;    
    getmemory(str);    
    printf("%s\n",str);   
    free(str);    
    return0;    
}
  • 正常输出'hello world"
  • 输出为空
  • 输出"烫烫烫"
  • 程序崩溃
free(ptr)   如果ptr为NULL,则不会做任何操作    如果ptr未定义,则会崩溃。

下述程序有什么问题?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include   <string.h>
#include   <stdio.h> 
#include   <stdlib.h> 
voidgetmemory(char*p)  {    
    p=(char*) malloc(100);    
    strcpy(p,"hello world");  
}  
intmain( ) 
{    
    char*str;    //注意此处的差异
    getmemory(str);    
    printf("%s\n",str);   
    free(str);    
    return0;    
}
  • 正常输出'hello world"
  • 输出为空
  • 输出"烫烫烫"
  • 程序崩溃
free(ptr)   如果ptr为NULL,则不会做任何操作    如果ptr未定义,则会崩溃。


请阅读下面代码片段并且回答问题:
1
2
3
4
5
6
7
8
9
10
11
12
#define SIZE_20M (20*1024*1024)
voidfunc_a()
{
    char*tmp = malloc(SIZE_20M)    //堆上获取  慢
    return;
}
voidfunc_b()
{
    chartemp[SIZE_20M];         //栈上获取  慢
    //...do something using temp
    return;
}
关于这段代码,下列说法正确的是
  • func_a 获得临时内存的方式效率通常更高。
  • func_b 使用了太多的栈,程序可能会在运行时候崩溃。
  • func_b 存在内存泄露
  • func_a 和func_b 分配的内存会自动初始化0




int a[3];
a[0] = 0; a[1] = 1; a[2] = 2;
int *p, *q;
p = a;
q = &a[2];
则a[q - p] = ?
  • 0
  • 1
  • 2
  • 未知

阅读下面代码,程序会打印出来的值是?
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
voidf(char**p){
      *p +=2;
}
main()
{
    char*a[] = {"123","abc","456"},**p;
    p = a;
    f(p);
    printf("%s\r\n",*p);
}
123
abc
456
3
p=a 此时a转换为指向数组首元素的指针,即是char**类型 等价于&a[0]
所以*p为char*类型指向“123”,等价于a[0],实质是指向字符数组“123”的第一个字符,若再次解引用则为第一个字符本身
我们再来看函数内,*p+=2   其中*的优先级>+=。那么就意味着先*p也就是指向123的第一个字符,后+2向右移动两位(char类型的)
这也就是3



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
voidswap(int&a,int&b)
{
     inttemp;
     temp=a;
     a=b;
     b=temp;
     cout<<a<<’ ‘<<b<<’\n’;
}
 
intmain(){
     
    intx=1;
    inty=2;
    swap(x,y);
    cout<<x<<’ ‘<<y<<’\n’;
    return0;
}
上面程序输出的是?2121
调用函数swap(x, y);传给被调用函数void swap(int &a, int &b )的是x、y的引用,所以在被调用函数里面交换相应的也会交换实参,所以都是输出2 1



针对以下代码,
1
2
3
4
constcharstr1[]=”abc”;
constcharstr2[]=”abc”;
constchar*p1 = “abc”;
constchar*p2 = “abc”;
判断下列说法哪个是正确的:______。
  • str1和str2地址不同,P1和P2地址相同。
  • str1和str2地址相同,P1和P2地址相同。
  • str1和str2地址不同,P1和P2地址不同。
  • str1和str2地址相同,P1和P2地址不同。
  • 4个地址都相同
  • 4个地址都不相同。
F。str1,str2地址不同,p1,p2地址不同,但是指向同一个地址(即存放相同的内容),但是要是说指针的值的话,那么str1与str2是不同的,p1,p2是相同的。

有以下程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
voidfun ( int*pl,int*p2,int*s )
{
    s=(int*) calloc(1,sizeof(int));
    *s=*pl + *p2;
    free (s);
 }
intmain ( )
{
 inta [2]={1,2},b [2]={40,50},*q = a;
    fun(a,b,q) ;
    printf ( "%d\n", *q);
}

程序运行后的输出结果是?

  • 42
  • 41
  • 1
  • 0
本题考查把数组名作为函数参数,执行fun函数后,q的值并没有发生变化,仍然是指向a,所以输出结果为1,选项C正确。


观察下面一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ClassA
{
public:
    virtual ~ ClassA(){};
    virtual void FunctionA(){};
};
class ClassB
{
public:
   virtual void FunctionB(){};
};
class ClassC : public ClassA,public ClassB
{
    public:
};
  
ClassC aObject;
ClassA* pA=&aObject;
ClassB* pB=&aObject;
ClassC* pC=&aObject;
关于pA,pB,pC的取值,下面的描述中正确的是:
  • pA,pB,pC的取值相同.
  • pC=pA+pB
  • pA和pB不相同
  • pC不等于pA也不等于pB
在这个情况下,子类的指针和第一个基类的指针应该是一样的,和第二个基类是不一样的。pB和pA的差值是 虚函数表指针的大小,32位机器是4字节,64位机器就是8字节了



下面程序的执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A{
    public:
        long a;
};
class B : public A {
    public:
        long b;
};
void seta(A* data, int idx) {
    data[idx].a = 2;
}
int main(int argc, char *argv[]) {
    B data[4];
    for(int i=0; i<4; ++i){
        data[i].a = 1;
        data[i].b = 1;
        seta(data, i);
    }
    for(int i=0; i<4; ++i){
         std::cout << data[i].a << data[i].b;
    }
    return0;
}
  • 11111111
  • 12121212
  • 11112222
  • 21212121
  • 22221111
这道题应该注意 指针类型加减 时步长的问题。
A 大小为 4
B 大小为 8
那么:
void seta(A* data, int idx) {
    data[idx].a = 2;
}
由于传入的实参为B类型,大小为8,而形参为A类型,大小为4
data[idx] 取 data + idx 处的元素,这时指针 data加1 的长度不是一个B长度,而是一个A长度,或者说是1/2个B长度。这时该函数中 data[0~3] 指向的是原 data[0].a,data[0].b,data[1].a,data[1].b, 
由于隐式类型转换的缘故,data[0].a, data[0].b,data[1].a,data[1].b 处的值全部由于 data[idx].a = 2; 操作变为 2。
这道题如果改为void seta(B* data, int idx),那么形参中data指针加1步长为8,结果就是21212121。但是由于步长为4,所以结果就是 22221111。






在一个指向字符串的指针char *p_str,要把字符串中第4个字符的值改为'a',正确的做法是()
  • p_str[3]='a'
  • *(p_str+3)='a'
  • p_str[4]='a'
  • *(p_str+4)='a'


已知数组D的定义是int D[4][8];,现在需要把这个数组作为实参传递给一个函数进行处理。下列说明汇总可以作为对应的形参变量说明的是()。
  • int D[4][]
  • int *s[8]
  • int(*s)[8]
  • int D[][8]
选CD
数组作为实参时必须指定列数,否则可能产生歧义,所以D正确
C是数组指针,每个都指向对应的数组的每列




用数组r存储静态链表,结点的next域指向后继,工作指针j指向链中结点,使j沿链移动的操作为()
  • j=r[j].next
  • j=j+1
  • j=j->next
  • j=r[j]->next



下列哪个语句没有开辟可容纳5个整数的空间?
  • int arr[5];
  • int arr(5);
  • int *p = new int[5];
  • std :: vector v(5);
B   是声明一个变量arr,初始值为5.



要使指针变量p指向2维数组A的第1个元素,正确的赋值表达式是()。
  • p=A或p=A[0]     //编译不通过
  • p=A[0]或p=A[0][0]
  • p=A[0]或p=&A[0][0]
  • p=A或p=&A[0][0]




关于 int a[10]; 问下面哪些不可以表示 a[1] 的地址?
  • a+sizeof(int)
  • &a[0]+1
  • (int*)&a+1
  • (int*)((char*)&a+sizeof(int))A. a+sizeof(int)

有以下程序

1
2
3
4
5
6
7
8
9
#include < stdio. h > 
main ( )
    int a [ 3 ] [ 4 ] = { 1,3,5,7,9,11,13,15,17,19,21,23 } , (*p) [4] = a , i , j , k = 0 ;
    for ( i =0 ; i < 3 ; i + + )
    for ( j =0 ; j < 2 ; j + + ) 
        k = k + * ( * ( p+i )+j );
    printf ( "%d" , k );
}

程序运行后的输出结果是?

  • 40
  • 60
  • 80
  • 100


运行下面这段C语言程序之后,输出在屏幕上的结果是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
voidfoobar(inta, int*b, int**c)
{
    int*p = &a;
    *p = 101;
    *c = b;
    b = p;
}
 
intmain()
{
    inta = 1;
    intb = 2;
    intc = 3;
    int*p = &c;
    foobar(a, &b, &p);
    printf("a=%d, b=%d, c=%d, *p=%d\n", a, b, c, *p);
    return(0);
}
  • a=1, b=2, c=3, *p=2
  • a=101, b=2, c=3, *p=2
  • a=101, b=101, c=2, *p=3
  • a=1, b=101, c=2, *p=3
  • 函数foobar中的a是按值传递,因此在函数中的修改不会引起主函数中的变化。
  • 函数中b传递的是主函数中b的指针,语句b = p ,其中p指向的是函数foobar内局部变量a的地址,让传递过去的指针换了指向的数据,原来指向的数据(主函数中的b)不会有影响。如果这里是*b = *p那么主函数中的b也要相应变化。
  • 函数中的c传递的是双重指针,*c = b,也就是让主函数中的p指针指向了主函数中的b的地址
  • 在函数foobar中对指针的变化没有影响到主函数,只是让双重指针更换了指向而已

    以下程序的输出结果是 1
    1
    2
    3
    chars[] = "123", *p; 
    p = s; 
    printf("%c%c%c\n", *p++, *p++, *p++);
    答案:321   整体从右向左,个体从右向左


    以下程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    void fun( char ** p)
    {
        int i;
        for(i=0;i<4;i + + )printf("% s",p[i]);
    }
    main( )
    {
        char *s[6]={ "ABCD""EFGH""IJKL""MNOP""QRST""UVWX" };
        fun(s);   
        printf("\n");
    }

    程序运行后的输出结果是?

    • ABCDEFGHIJKL
    • ABCD
    • AEIM
    • ABCDEFGHIJKLMNOP
    本题考查字符串数组,s表示指向字符数组的指针,s指向了第一个字符串,s + +指向了第二个字符串,所以最后输出结果为D选项。

    下面代码会输出什么()
    1
    2
    3
    4
    5
    6
    intmain(intargc, char**argv)
    {
        inta[4] = {1234};
        int*ptr = (int*)(&a + 1);
        printf("%d", *(ptr - 1));
    }

    • 1
    • 2
    • 3
    • 4
    其实测试可以发现,a 和 &a 是同一个地址,printf("%d", a),printf("%d", &a)结果是相同的。
    但是其代表的含义不同,*(a+1)代表从a数组首地址跳跃一个int的长度,*(&a+1)表示从a数组首地址跳跃一个数组的长度,也就是指向a数组最后一个元素的下一个位置,因此*(ptr - 1)表示a数组的最后一个元素。


    代码执行后,a和b的值分别为?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    classTest{
    public:
        inta;
        intb;
        virtualvoid fun() {}
        Test(inttemp1 = 0, inttemp2 = 0)
        {
            a=temp1 ;
            b=temp2 ;
        }
        intgetA()
        {
            returna;
        }
        intgetB()
        {
            returnb;
        }
    };
     
    intmain()
    {
        Test obj(5, 10);
        // Changing a and b
        int* pInt = (int*)&obj;
        *(pInt+0) = 100;  
        *(pInt+1) = 200;  
        cout << "a = " << obj.getA() << endl;
        cout << "b = " << obj.getB() << endl;
        return0;
    }
    • 200 10
    • 5 10
    • 100 200
    • 100 10
    这么需要考虑虚函数表,指向虚函数表的指针在32位系统下占用4个字节,其地址分布在整个类成员变量的地址的首部,接下来就是变量a的地址、b的地址。当将test对象obj赋给指向整型的pInt后,指针pInt指向了地址的首部也就是虚函数表指针,所以*(pInt+0)=100改变的是虚函数表的值,接下来*(pInt+1)=200改变的是变量a的值,变量b没有变换。


    (1.1.2)内存大小型

    以下系统中,int类型占几个字节,指针占几个字节,操作系统可以使用的最大内存空间是多大:

    • 32位下:4,4,2^32 64位下:8,8,2^64
    • 32位下:4,4,不限制 64位下:4,8,不限制
    • 32位下:4,4,2^32 64位下:4,8,2^64
    • 32位下:4,4,2^32 64位下:4,4,2^64
    指针的大小是根据操作系统来的,类比于字节对齐,为了加速




    在32位机器上,下列代码中

     
    sizeof(a)的值是()

    • 20
    • 21
    • 22
    • 24
    • 非以上选项
    我来详细解释一下吧。
    首先,#pragma pack(2)   强制设定为2字节对齐
    i   4字节
    u  一个为13,一个为4,默认为4字节对齐;
         union占对齐后数据的最大字节大小,默认为13+3=4*4=16;
         但是,该处强制为2字节对齐,实际为13+1=2*7=14字节
    color   枚举类型的实例  4字节
    4+14+4=22字节


    在32位系统中:
    char arr[] = {4, 3, 9, 9, 2, 0, 1, 5};
    char *str = arr;
    strlen(str) = 3 ;  
    5
    strlen(str)求字符串长度,这里的字符串由字符数组得到,字符数组中赋值的是字符的ASCII值
    其中ASCII值为0的代表字符中的‘\0’也就是字符串结束的标志,所以得到的长度为5


    以下程序段的输出结果是
    1
    2
    chars[]="\\123456\123456\t";
    printf("%d\n",strlen(s));
    • 12
    • 13
    • 16
    • 以上都不对
    答案:A
    这里考查转义字符,注意 \\ 表示字符 \
    \123表示字符 {
    \t 表示制表符
    这些都是一个字符。

    在32位操作系统gcc编译器环境下,下面程序的运行结果为()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <iostream>
    using namespace std;
    classA {
    public:
        intb;
        charc;
        virtual voidprint() {
            cout << "this is father’s fuction! " << endl;
        }
    };
    classB: A {
    public:
        virtual voidprint() {
            cout << "this is children’s fuction! " << endl;
        }
    };
    intmain(intargc, char* argv[]) {
        cout << sizeof(A) << ""<< sizeof(B) << endl;
        return0;
    }
    • 12 12
    • 8 8
    • 9 9
    • 12 16
    那么类A的大小等于4个字节 + 4个字节(考虑对齐) + 4个字节(指向虚函数的指针)=12字节;
    类B的大小就是等于类A的大小12个字节.
    因为在基类中存在虚函数时,派生类会继承基类的虚函数,因此派生类中不再增加虚函数的存储空间(因为所有的虚函数共享一块内存区域),而仅仅需要考虑派生类中添加进来的非static数据成员的内存空间大小。所以类B大小为12B


    在Windows 32位操作系统中,假设字节对齐为4,对于一个空的类A,sizeof(A)的值为()?
    • 0
    • 1
    • 2
    • 4
    为了确保不同的对象会拥有不同的地址,所以类的大小不会为0字节。空类的大小为1字节


    1
    2
    3
    4
    5
    6
    7
    8
    struct Date
    {
        chara;
        intb;
        int64_t c;
        chard;
    };
    Date data[2][10];
    在32位系统上,如果Data的地址是x,那么data[1][5].c的地址是()
    • X+195
    • X+365
    • X+368
    • X+215
    a               b  c  d
    1+(3)+4+8+1+(7)= 24,()内表示为了满足对齐填充的大小。
    &data[1][5].c = x+10*24+5*25+1+(3)+4=368。 

    typedef struct
    {
    char flag[3];
    short value;
    } sampleStruct;
    union
    {
    char flag[3];
    short value;
    } sampleUnion;

    假设 sizeof(char)=1,sizeof(short)=2,那么sizeof(sampleStruct) = 6 , sizeof(sampleUnion) =4



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    classA
    {
            inta;
            shortb;
            intc;
            chard;
    };
    classB
    {
            doublea;
            shortb;
            intc;
            chard;
    };

    在32位机器上用gcc编译以上代码,求sizeof(A),sizeof(B)分别是多少。
    • 12 16
    • 12 12
    • 16 24
    • 16 20




    以下代码的输出结果是?
    main() {     char str[]="S\065AB";     printf("\n%d", sizeof(str)); }
    • 7
    • 6
    • 5
    • error
    转义字符\ddd表示8进制,  是一个数 ,所以就有 4个字符 + '\0' 即5




    1
    2
    3
    4
    unsigned char*p1;
    unsigned long*p2;
    p1=(unsigned char*)0x801000;
    p2=(unsigned long*)0x810000;
    请问p1+5= 什么?
    p2+5= 什么?
    • 801005 810005
    • 801010 810014
    • 801005 810014
    • 801010 810015
    注意是16进制,一个unsigned long占4个字节,指针是跳类型不是跳字节的,所以跳5就是偏移4*5=20个字节,8进制14,加上段地址0x810000就是0x810014,



    (1.2)方法、顺序、类


    如下程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    <p class="p0">
        #include "stdio.h"
     
    classBase
    {
    public:
        Base()
        {
            Init();
        }
        virtual voidInit()
        {
            printf("Base Init\n");
        }
        voidfunc()
        {
            printf("Base func\n");
        }
    };
    classDerived: publicBase
    {
    public:
        virtual voidInit()
        {
            printf("Derived Init\n");
        }
        voidfunc()
        {
            printf("Derived func\n");
        }
    };
     
    intmain()
    {
        Derived d;
        ((Base *)&d)->func();
         
        return0;
    }
     
     
     
     
    </p>
    该程序的执行结果
    • Base Init Derived func
    • Base Init Base func
    • Derived Init Base func
    • Derived Init Derived func
    在构造函数不要调用虚函数。在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。
    在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。


    下面这段程序的输出是什么?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class A{
        public:
            A(){p();}
            virtual void p(){print("A")}
            virtual ~A(){p();}
    };
    class B:public A{
        public:
            B(){p();}
            void p(){print("B")}
            ~B(){p();}
    };
    int main(int, char**){
            A* a=newB();
            deletea;
    }
    • AABB
    • BBAA
    • ABAB
    • ABBA
    调用基类,在调用子类的构造函数,析构的时候先调用子类的,在调用基类的。
    a类中的p()函数是虚函数。但是此时构造函数没有完全初始化,所以还是调用基类自己的p()函数,同理子类的虚函数



    分析一下这段程序的输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include<iostream>
     using namespace std;
     classB
     {
     public:
         B()
         {
             cout << "default constructor" << " ";
         }
         ~B()
         {
             cout << "destructed"<< " ";
         }
         B(inti): data(i)
         {
             cout << "constructed by parameter" << data << " ";
         
         privateintdata;
     }; 
     B Play( B b)
     {
         returnb;
     
     intmain(intargc, char*argv[])
     {
         B temp = Play(5);
         return0;
     }

    • constructed by parameter5 destructed destructed
    • constructed by parameter5 destructed
    • default constructor" constructed by parameter5 destructed
    • default constructor" constructed by parameter5 destructed destructed
    首先要说明的是,若用户没有定义,C++隐式声明一个复制构造函数和一个赋值运算符(完成按数据成员复制的动作)。二者很像,但是在下边这点上有很大的不同:复制构造函数是只在对象实例化时才会被调用,也就是说,在复制构造函数调用期间,这个对象处于一个未决状态(直到复制构造函数被成功调用),另外复制构造函数不返回任何值,void都没有。而赋值运算符则在一个现存的对象被赋予新的值时被调用,并且它有返回值。 
    如果添加上复制构造函数:B(const B & r){cout << "copy ctor " << "  ";}
    那么输出:constructed by parameter5 copy ctor destructed


    阅读以下代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    classparent  
    {  
        public:  
        virtual voidoutput();  
    };  
    voidparent::output()  
    {  
        printf("parent!");  
    }  
           
    classson : publicparent  
    {  
        public:  
        virtual voidoutput();  
    };  
    voidson::output()  
    {  
        printf("son!");  
    }
    1
    2
    3
    4
    son s; 
    memset(&s , 0, sizeof(s)); 
    parent& p = s; 
    p.output(); 
     执行结果是()
    • parent!
    • son!
    • son!parent!
    • 没有输出结果,程序运行出错
    答案: d
    解释:
    memset 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 
    本代码中, 不虚函数链表地址也清空了, 所以p.output调用失败。 output函数的地址编程0

     
    下面代码的输出是什么?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    classA  
    {  
    public:  
        A()  {     }  
        ~A() {    cout<<"~A"<<endl;   }  
    };  
       
    classB:publicA  
    {  
        public:  
            B(A &a):_a(a)  
            {  
                 
            }  
            ~B()  
            {  
                cout<<"~B"<<endl;  
            }  
        private:  
            A _a;  
        };  
           
    intmain(void)  
     {  
            A a;       //调用一次构造函数
            B b(a);    //1、调用父类的构造函数,也就是a的构造函数  2、初始化自己的变量 A _a  调用一次构造函数  3、调用b自己的构造函数
    }
    • ~B
    • ~B ~A
    • ~B ~A ~A
    • ~B ~A ~A ~A
    A a;       //调用一次构造函数
     B b(a);    //1、调用父类的构造函数,也就是a的构造函数  2、初始化自己的变量 A _a  调用一次构造函数  3、调用b自己的构造函数
    AAAB    析构就是BAAA

    有如下程序段:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <iostream>
    using namespace std;
     
    classA {
        public:
        ~A() {
            cout << "~A()";
        }
    };
    classB{
        public:
        virtual ~B() {
        cout << "~B()";
    }
    };
    classC: publicA, publicB {
        public:
        ~C() {
            cout << "~C()";
        }
    };
    intmain() {
        C * c = newC;
        B * b1 = dynamic_cast<B *>(c);
        A * a2 = dynamic_cast<A *>(b1);
        delete a2;
    }
    则程序输出:
    • ~C()~B()~A()
    • ~C()~A()~B()
    • A)B)都有可能
    • 以上都不对
    答案解析:创建一个类对象c,然后动态类型转换,让一个B *b1指针指向c,再一次动态类型转换,让一个基类A *a2指针指向b1,当delete a2时,调用析构函数,但是基类A的析构函数不是虚函数,所以只调用A的析构函数,结果应该是:~A()


    如下程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <p class="p0">
        #include "stdio.h"
     
    classA
    {
    public:
        virtual voidTest()
        {
            printf("A test\n");
        }
    };
    classB: publicA
    {
    public:
        voidfunc()
        {
            Test();
        }
        virtual voidTest()
        {
            printf("B test\n");
        }
    };
    classC: publicB
    {
    public:
        virtual voidTest()
        {
            printf("C test\n");
        }
    };
    voidmain()
    {
        C c;
        ((B *)(&c))->func();
        ((B)c).func();
    }
     
     
    </p>

    该程序的执行结果
    C test B test
     ((B *)(&c))->func()   ==》   B *temp;   temp = &c;    temp->func();   ==》 这不就是一个典型的多态问题么,用基类指针指向派生类对象,所以肯定调用的是C对象的func函数,输出 C test
    ((B)c).func();   ==》 不涉及指针的操作,自然就没有多态行为的发生。




    有如下程序段:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    classA
    {
        public:
            A()
            {
                printf(“0”);
            }
            A(inta)
            {
                printf(“1”);
            }
            A& operator=(constA& a)
            {
                printf(“2”);
                return*this;
            }
    }
    intmain()
    {
        A al;
        al=10;
    }
    则程序输出是:
    • 02
    • 012
    • 01
    • 以上都不对
    A a1; //调用A默认构造函数
    a1=10; //类型不匹配,调用构造函数A(int)进行隐式转化,之后将引用传给operator=()


    有如下程序段:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <p class="p0">
        #include "stdio.h"
     
    classA
    {
        public:
        A()
        {
            printf("1");
        }
        A(A &a)
        {
            printf("2");
        }
        A &operator=(constA &a)
        {
            printf("3");
            return*this;
        }
    };
    intmain()
    {
        A a;
        A b = a;
    }
     
     
     
    </p>
    则程序输出为:
    • 12
    • 13
    • 无法确定
    • 编译出错
    拷贝构造函数  A a=b   初始化时候赋值
    赋值构造函数  A a;  a=b

      下列程序的输出结果:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      #include <iostream>
      using namespace std;
      classA
      {
      public:
          voidprint()
          {
              cout << "A:print()";
          }
      };
      classB: privateA
      {
      public:
          voidprint()
          {
              cout << "B:print()";
          }
      };
      classC: publicB
      {
      public:
          voidprint()
          {
      A: print();
          }
      };
      intmain()
      {
          C b;
          b.print();
      }

      编译出错
      B的继承为私有继承,对于C已经不能再调用A的所有方法了



      有如下程序段:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <p class="p0">
      classA
      {
          int_a;
      public:
          A(inta): _a(a)
          {
          }
          friend intf1(A &);
          friend intf2(constA &);
          friend intf3(A);
          friend intf4(constA);
      };
       
       
       
      </p>
      以下调用哪个是错误的:
      • f1(0)
      • f2(0)
      • f3(0)
      • f4(0)
      非常量引用的初始值必须为左值
      在标准C++语言中,临时量(术语为右值,因其出现在赋值表达式的右边)可以被传给函数,但只能被接受为const &类型。
      要理解这个先得理解左值和右值的概念一个区分左值与右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。
      本题举例:
      执行f1(0),实参0要传成A对象,那么执行
      A &a1 = 0;   //这是不行的。
      执行f2(0),实参0要传成A对象,那么执行 
      const A &a2 = 0;//这是可行的。


      编译运行如下程序会出现什么结果
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <p class="p0">
          #include <iostream>
      using namespace std;
       
      classA
      {
          A()
          {
              printf("A()");
          }
      public:
          staticA &get()
          {
              staticA a;
              returna;
          }
      };
      intmain()
      {
          A::get();
          return0;
      }
       
      输出A()
      调用静态函数本身不会执行构造函数, 但 get ()实例化了一个对象,所以在get()里面调用了构造函数。


      设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
      C c;
      voidmain()
      {
          A*pa=newA();
          B b;
          staticD d;
          delete pa;
      }
      ABDC     注意构造过程为:CDAB,最所以不相反是由于,手动先调用delete pa
      这道题主要考察的知识点是 :全局变量,静态局部变量,局部变量空间的堆分配和栈分配
      其中全局变量和静态局部变量时从 静态存储区中划分的空间,
      二者的区别在于作用域的不同,全局变量作用域大于静态局部变量(只用于声明它的函数中),
      而之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反。

      局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
      局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
      之所以是 先 A  后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数, 而语句 delete a ; 位于函数结尾 "}" 之前。


      若MyClass为一个类,执行
      1
      MyClass a[4],*p[5];
      语句时会自动调用该类构造函数的次数是 4
      把MyClass a[4],*p[5];分开写;
      MyClass a[4];
      MyClass *p[5];
      则a[4]是类数组,有4个对象,调用构造函数4次
      *p[5]是指针数组,也就是5个元素存放的是指向MyClass类型的对象的指针,没有初始化的指针为空,不指向任何对象,也不调用构造函数。
      类似于 指针的强制转换,会导致虚函数发生作用,继续调用实际子类实例的函数。 
      而 类的强制转换,不会导致虚函数作用,是谁就是谁


      看以下代码:
      A *pa = new A[10];
      delete pa;
      则类A的构造函数和析构函数分别执行了几次()
      • 1 1
      • 10 10
      • 1 10
      • 10 1
        构造10次毫无疑问  
        释放数组应用用delete [],这儿的代码只释放了A[0],所以是析构1次



        以下代码共调用多少次拷贝构造函数:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        Widget f(Widget u)
        {  
           Widget v(u);
           Widget w=v;
           returnw;
        }
        main(){
            Widget x;
            Widget y=f(f(x));
        }
        • 1
        • 3
        • 5
        • 7
        先看Widget f(Widget u)函数,调用该函数时,形参u传递时调用一次拷贝构造函数,内部Widget w = u; 调用一次拷贝构造函数,返回w时调用一次拷贝构造,所以调用一次该函数调用3次拷贝构造函数。
        再看main中Widget y=f(f(x)); 一共调用了两次f函数,所以是6次拷贝构造函数,然后表达式赋值为y时,再调用一次,共7次。


        (1.3)内存的基础知识类


        以下对C语言的”指针“描述不正确的是:

        • 32位系统下任何类型指针的长度都是4个字节
        • 指针的数据类型声明的是指针实际指向内容的数据类型
        • 野指针是指向未分配或者已释放的内存地址
        • 当使用free释放掉一个指针内容后,指针变量的值被置为NULL
        free掉一个指针后,指针的值是不会自动置为NULL的,当然其指向的内存已经被释放,可以重新分配给其他进行使用,此时该指针被称为野指针。
        对野指针进行操作,可能会破坏内存结构,因为并不知道当前指针指向的内容是什么,所以一般在free操作结束后,由程序猿将指针置为NULL。


        • 下面有关malloc和new,说法错误的是? 
        • new 建立的是一个对象, malloc分配的是一块内存.
        • new 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数,malloc仅仅分配内存,free仅仅回收内存
        • new和malloc都是保留字,不需要头文件支持
        • new和malloc都可用于申请动态内存,new是一个操作符,malloc是是一个函数
        malloc在头文件stdlib.h中的,不是关键字



        引用与指针有什么区别?
        • 指针是一个实体,而引用仅是个别名
        • 指针没有 const,引用有 const;
        • 引用不能为空,指针可以为空;
        • “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
        • 从内存分配上看:程序为引用变量分配内存区域,而指针不需要分配内存区域
        • 指针和引用的自增(++)运算,意义一样
        ★ 相同点:1. 都是地址的概念;指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。★ 区别:1. 指针是一个实体,而引用仅是个别名;2. 引用使用时无需解引用(*),指针需要解引用;3. 引用只能在定义时被初始化一次,之后不可变;指针可变;4. 引用没有 const,指针有 const;5. 引用不能为空,指针可以为空;6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;7. 指针和引用的自增(++)运算意义不一样;8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
        设有如下说明:
        1
        2
        3
        typedef struct ST{
            long a; int b; char c[2];
        } NEW;
        则下面叙述中正确的是:
        • 以上的说明形式非法
        • ST是一个结构体类型
        • NEW是一个结构体类型
        • NEW是一个结构体变量
        struct ST 等价于 NEW,为同一个结构类型

        test.c文件中包括如下语句:

        1
        2
        3
        4
        #define INT_PTR int*
        typedef int* int_ptr;
        INT_PTR a,b;
        int_ptr c,d;

        文件中定义的四个变量中,哪个变量类型不是指针类型?

        • a
        • b
        • c
        • d
        • 都是指针
        • 都不是指针
        typedef int* int_ptr相当于重新创建了一个数据类型,它声明的c,d都是指针,而#define INT_PTR int*,宏定义只是按照格式直接替换,所以编译的时候是按照int* a,b来声明的,所以b不是指针类型。

        下面哪种C/C++分配内存的方法会将分配的空间初始化为0

        • malloc()
        • calloc()
        • realloc()
        • new[ ]
          1) malloc 函数: void *malloc(unsigned int size)

               在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。

          2)calloc 函数: void *calloc(unsigned int num, unsigned int size)

               按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。

              calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。 3)realloc 函数: void *realloc(void *ptr, unsigned int size)

              动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。

              申请的内存空间不会进行初始化。4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量都不进行初始化。
          参照代码
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          classClassA { 
          public
              virtual ~ ClassA() {
              }
              virtual voidFunctionA() {
              }
          };
          classClassB { 
          public
              virtual voidFunctionB() {
              }
          };
          classClassC: publicClassA, publicClassB { 
          public
          };
          ClassC aObject;
          ClassA* pA = &aObject;
          ClassB* pB = &aObject;
          ClassC* pC = &aObject;

          下面那一个语句是不安全的
          • delete pA
          • delete pB
          • delete pC
          因为三个指针不是动态建立的,不用删除,只有用new建立的对象才会用到delete删除
          参照代码:
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          class ClassA
          {
              public:
              virtual ~ ClassA()
              {
              }
              virtual void FunctionA()
              {
              }
          };
          class ClassB
          {
              public:
              virtual void FunctionB()
              {
              }
          };
          class ClassC: public ClassA, public ClassB
          {
              public:
          };
          ClassC aObject;
          ClassA *pA = &aObject;
          ClassB *pB = &aObject;
          ClassC *pC = &aObject;
          假设定义了ClassA* pA2,下面正确的代码是:
          pA2=static_cast<ClassA*>(pB);
          void* pVoid=static_cast<void*>(pB); pA2=static_cast<ClassA*>(pVoid);
          pA2=pB;
          pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB));
          答案 B D
          • pA2=static_cast<ClassA*>(pB);  类型转换无效
          • 正确
          • pA2=pB; 类型转换无效
          • pA2=static_cast<ClassA*>(static_cast<ClassC*>(pB)); 转换有效
          static_cast 的用法
          static_cast < type-id > ( expression )
          该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
          ①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
          进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
          进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
          ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
          ③把空指针转换成目标类型的空指针。
          ④把任何类型的表达式转换成void类型。
          注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。
          C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为显式类型转换使用。
          使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?
          • 会有内存泄露
          • 不会有内存泄露,但不建议用
          • 编译就会报错,必须使用delete []p;
          • 编译没问题,运行会直接崩溃
          C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。 
          关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。
          基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。

          下面哪种C/C++分配内存的方法会将分配的空间初始化为0

          • malloc()
          • calloc()
          • realloc()
          • new[ ]
            1) malloc 函数: void *malloc(unsigned int size)

                 在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。

            2)calloc 函数: void *calloc(unsigned int num, unsigned int size)

                 按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。

                calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。 
            3)realloc 函数: void *realloc(void *ptr, unsigned int size)

                动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。

                申请的内存空间不会进行初始化。
            4)new是动态分配内存的运算符,自动计算需要分配的空间,在分配类类型的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置,在
            函数体外定义的变量都初始化为0,在函数体内定义的内置类型变量都不进行初始化。



            参照代码
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            classClassA { 
            public
                virtual ~ ClassA() {
                }
                virtual voidFunctionA() {
                }
            };
            classClassB { 
            public
                virtual voidFunctionB() {
                }
            };
            classClassC: publicClassA, publicClassB { 
            public
            };
            ClassC aObject;
            ClassA* pA = &aObject;
            ClassB* pB = &aObject;
            ClassC* pC = &aObject;


            以下有关C语言的说法中,错误的是________。
            • 内存泄露一般是指程序申请了一块内存,使用完后,没有及时将这块内存释放,从而导致程序占用大量内存。
            • 无法通过malloc(size_t)函数调用申请超过该机器物理内存大小的内存块。
            • 无法通过内存释放函数free(void*)直接将某块已经使用完的物理内存直接还给操作。
            • 可以通过内存分配函数malloc(size_t)直接申请物理内存。
            内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
            free释放的内存不一定直接还给操作系统,可能要到进程结束才释放。
            可以直到malloc不能直接申请物理内存,它申请的是虚拟内存




            下面那一个语句是不安全的
            • delete pA
            • delete pB
            • delete pC
            因为三个指针不是动态建立的,不用删除,只有用new建立的对象才会用到delete删除


            使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?
            • 会有内存泄露
            • 不会有内存泄露,但不建议用
            • 编译就会报错,必须使用delete []p;
            • 编译没问题,运行会直接崩溃
            C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。 
            关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。
            基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。


            下列关于C语言中指针的说法错误的是:___
            • 指针的值是一个地址
            • 非法指针是指该指针的值不是一个已经分配的内存地址
            • 两个指向同类型地址的指针之间做减法是没有意义的
            • 指针的指针占用的内存空间和其他指针占用的内存空间相同
            非法指针是指该指针的值是一个已经分配的内存地址


            0 0