zoukankan      html  css  js  c++  java
  • [C/C++] C/C++错题集

    1.

    解析:

    A:在GCC下输出:0
       在VC6.0下输出:1
    B:在GCC下输出:段错误 (核心已转储)
       在VC6.0下输出:已停止工作,出现了一个问题,导致程序停止正常工作。
    C:正常
    D:在GCC下输出:-1 0
       在VC6.0下输出:0 0
     
    2.运行下面这段代码,会出现的情况是:
    void GetMemory(char *p)
    {
        p = (char *)malloc(100);
    }
    void Test(void)
    {
        char *str = NULL;
        GetMemory(str);
        strcpy(str, "hello world");
        printf(str);
    }

    答案:程序崩溃

    GetMemory(char *p);这里*p是形参,是局部变量,不能将malloc申请的地址指针返回,造成内存泄露,更严重的是执行GetMemory(str);后str依然为NULL,执行strcpy(str, 'hello world');就会出错。只要将GetMemory函数改为GetMemory(char *&p)即可正常运行。
     
    3.分析下面这一段程序的输出:
    #include<iostream>
    using namespace std;
    class B
    {
    public:
        B()
        {
            cout << "default constructor" << " ";
        }
        ~B()
        {
            cout << "destructed" << " ";
        }
        B(int i): data(i)
        {
            cout << "constructed by parameter" << data << " ";
        }
    private:
        int data;
    };
    B Play( B b)
    {
        return b;
    }
    int main(int argc, char *argv[])
    {
        B temp = Play(5);
        return 0;
    }

    答案:constructed by parameter5 destructed destructed
    解析:
    ·调用Play函数需要将5隐式类型转换为Play函数中的形参b,会调用B的B(int i): data(i),打印“constructed by parameter5”。
    ·Play函数返回时需要调用B的复制构造函数给对象temp初始化。
    ·Play函数返回后需要调用b的析构函数,将Play函数的形参释放,打印“destructed”。
    ·main函数返回后需要释放temp,打印“destructed”。

    4.

    代码执行后,a和b的值分别为?

    #include<iostream>
    using namespace std;
    
    class Test{
    public:
        int a;
        int b;
        virtual void fun() {}
        Test(int temp1 = 0, int temp2 = 0)
        {
            a=temp1 ;
            b=temp2 ;
        }
        int getA()
        {
            return a;
        }
        int getB()
        {
            return b;
        }
    };
    
    int main()
    {
        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;
        return 0;
    }

    答案:200 10

    解析:

    这么需要考虑虚函数表,指向虚函数表的指针在32位系统下占用4个字节,其地址分布在整个类成员变量的地址的首部,接下来就是变量a的地址、b的地址。当将Test对象obj赋给指向整型的pInt后,指针pInt指向了地址的首部也就是虚函数表指针,所以*(pInt+0)=100改变的是虚函数表的值,接下来*(pInt+1)=200改变的是变量a的值,变量b没有变换。

    5.

    CONTAINER::iterator iter , tempIt;
    for (iter = cont.begin() ; iter != cont.end() ; )      
    {
        tempIt = iter;
        ++iter;
        cont.erase(tempIt);
          
    }
     
    假设cont是一个CONTAINER的示例,里面包含数个元素,那么当CONTAINER为: 1、vector 2、list 3、map 4、deque 会导致上面的代码片段崩溃的CONTAINER类型是?
    

    答案:1,4

    解析:

    首先看看各个容器的erase(pos)实现吧:
    (1)vector:erase(pos),直接把pos+1到finish的数据拷贝到以pos为起点的区间上,也就是vector的长度会逐渐变短,同时iter会逐渐往后移动,直到iter == cont.end(),由于容器中end()返回的迭代器是最后一个元素的下一个(这个地方没有任何值),现在考虑这个状态前一个状态,此时要删除的点是iter, tempIt = iter, ++iter会指向此时的end(),但是执行erase(tempIt)之后,end()向前移动了!!!问题来了,此时iter空了!!!不崩溃才怪。
    (2)list:erase(pos),干的事情很简单,删除自己,前后的节点连接起来就完了,所以iter自增的过程不会指空,不会崩溃喽。
    (3)map:erase(pos),干的事情太复杂,但是我们需要知道的信息其实很少。该容器底层实现是RBTree,删除操作分了很多种情形来讨论的,目的是为了维持红黑树性质。但是我们需要知道的就是每个节点类似于list节点,都是单独分配的空间,所以删除一个节点并不会对其他迭代器产生影响,对应到题目中,不会崩溃喽。
    (4)deque:erase(pos),与vector的erase(pos)有些类似,基于结构的不同导致中间有些步骤不太一致。先说说deque的结构(这个结构本身比较复杂,拣重要说吧,具体看STL源码),它是一个双向开口的连续线性空间,实质是分段连续的,由中控器map维持其整体连续的假象。其实题中只要知道它是双向开口的就够了(可以在头部或尾部增加、删除)。在题中有erase(pos),deque是这样处理的:如果pos之前的元素个数比较少,那么把start到pos-1的数据移到起始地址为start+1的区间内;否则把pos后面的数据移到起始地址为pos的区间内。在题中iter一直往后移动,总会出现后面数据比前面少的时候,这时候问题就和1一样了,必须崩溃!
     
    6.开发C代码时,经常见到如下类型的结构体定义:
    typedef struct list_t
    {
        struct list_t *next;
        struct list_t *prev;
        char data[0];
    } list_t;
    

    最后一行char data[0];的作用是?

    答案:方便管理内存缓冲区,减少内存碎片化。

    解析:柔性数组,具体可参考:http://www.cnblogs.com/lca1826/p/6636549.html

    7.在32位系统下,有以下代码:

    void Foo (char str[100]){
        ...
    }
    

    请计算 sizeof( str ) = ?

    答案:4

    解析:数组作为形参时,数组的数组名会退化成一个指向该类型数组的指针,只要是指针,在32位系统中所占的字节数就是4,在64位系统中所占的字节数是8。

    8.

    char a=101;
    int sum=200;
    a+=27;sum+=a;
    printf("%d
    ",sum);
    

    判断程序输出结果?

    答案:72

    解析:char类型的范围是-128~+127,当a+=27之后a的值超出可表示范围会变为-128。

    9.

    short i=65537;
    int j=i+1;
    printf(“i=%d,j=%d
    ”, i, j);

    判断程序输出结果?

    答案:i=1,j=2

    解析:short类型的数据占用2个字节16位,可表示的最大整数值为65535,即1111 1111 1111 1111,而65537转换成二进制为1 0000 0000 0000 0001,最高位的1溢出,因此i的值为0000 0000 0000 0001,即为1,而j=i+1,故j=2。

     

    10.

    答案:stra tra ra

    解析:char *str[3] = {"stra","strb","strc"};是一个指针数组; char *p = str[0];实际上p指向第一个字符串,所以字符串加1,则指向字符串的下一个字母,而并非是下一个字符串。

    11.

    解析:访问vector中的数据 
    使用两种方法来访问vector。 
    1、vector::at() 
    2、vector::operator[] 
    operator[]主要是为了与C语言进行兼容。它可以像C语言数组一样操作。但at()是我们的首选,因为at()进行了边界检查,如果访问超过了vector的范围,将抛出一个异常。由于operator[]容易造成一些错误,所有我们很少用它,下面进行验证一下: 
    分析下面的代码:

    vector<int> v;
    v.reserve(10);
    
    for(int i=0; i<7; i++)
        v.push_back(i);
    
    try
    {
        int iVal1 = v[7]; // not bounds checked - will not throw
        int iVal2 = v.at(7); // bounds checked - will throw if out of range
    }
    catch(const exception& e)
    {
        cout << e.what();
    }
    

    12.若int占2个字节,char占1个字节,float占4个字节,sizeof(xc)大小是:

    struct stu
    {
        union
        {
            char bj[5];
            int bh[2];
        } class;
        char xm[8];
        float cj;
    } xc;

    答案:20

    解析:

    内存对齐规定:结构体的总大小为结构体最宽基本类型成员大小的整数倍。可见最宽的是float是4.,数组的形式只是多个数据放在一起而已。
    联合体的大小取内部最大的字节数,本题中最大的是char型数组的五字节,所以联合体里面总共是5个字节,要为4的倍数所以为8个字节,
    所以为8 + 8 + 4 = 20。
     
    13. 

    解析:union类型的变量在定义时是可以被初始化的,定义如下union类型:

    union Test
    {
        int a;
        float b;
    };
    Test test = {1};
    

    test变量的定义可以初始化,初始值的类型必须是union中第一个成员的类型。

    14.假设下面的函数foo会被多线程调用,那么让i、j、k三个变量哪些因为线程间共享访问需要加锁保护()。

    int i = 0;
    void foo()
    {
        static int j = 0;
        int k = 0;
        i++; j++; k++;
    }

    答案:i和j

    解析:多线程调用时要进行保护时,主要是针对全局变量和静态变量的,函数内的局部变量不会受到影响。这里i是全局变量,j是局部静态变量,所以要进行保护。

    15.在Linux 系统中,在运行一个程序时,程序中未初始化的全局变量会被加载到以下哪个内存段中?

    答案:BSS

    解析:

      BSS(Block Started by Symbol):通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。

      数据段(data segment):通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

      代码段(code segment/text segment):通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

      堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。

      栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    16.

    解析:选项B不对的原因,是运算符优先级的问题。

    (volatile int *)0xaae0275c[0]=1 的意思是,先取出0xaae0275c地址处的值,然后把该值强制转换成int*类型,显然,该值是一个数值,不是变量,是右值,向该值再赋值1,就类似于 2=1; 这样的赋值,显然不对。
    想改正确很容易,加上括号就OK了。
    正确方式如下:
    ((volatile int *)0xaae0275c)[0]=1;
    加上一对括号即可。
    
  • 相关阅读:
    浅析阿里云API网关的产品架构和常见应用场景
    30分钟全方位了解阿里云Elasticsearch(附公开课完整视频)
    阿里云葛岱斌:让天下没有难做的安全运维
    TPCx-BB官宣最新世界纪录,阿里巴巴计算力持续突破
    从零开始入门 K8s | Kubernetes 网络概念及策略控制
    云栖深度干货 | 打造“云边一体化”,时序时空数据库TSDB技术原理深度解密
    AnalyticDB for PostgreSQL 6.0 新特性介绍
    从零开始入门 K8s | 应用存储和持久化数据卷:核心知识
    K8s 从懵圈到熟练 – 集群网络详解
    理解SqlMapConfig.xml文件
  • 原文地址:https://www.cnblogs.com/lca1826/p/6636568.html
Copyright © 2011-2022 走看看