c/c++

c/c++(笔试题)
2009-04-08 15:05

1.程序填空题目:在中括号里补充语句,使程序完整 (from csdn)

typdedef struct _A{ 
                  ...                ... // some definitions 
                  int x; 
                  ...                ... // some definitions 
                  int y; 
}A

void foo(int *px) 

                  //print the value of y 
                 【                                  】 
}

void main() 

                  A *a = ( A *) malloc (sizeof(A));

                  if ( a == NULL ) 
                     return ; 
                  foo( &(a- >x)); 
}

答案1:

void foo(int *px)  
{  
#define OFFSET(TYPE, MEMBER)                 (size_t)&(((TYPE *)0)- >MEMBER) 
                  printf("%d\n", *(int*)((char*)px + OFFSET(A, y) - OFFSET(A, x))); 
}

答案2:

void foo(int *px)  
{  
                A *temp=( A *) malloc (sizeof(A));  
                int addr=(int)(&(temp- >y)-&(temp- >x)); 
                printf("%d\n", *(px+addr));   
}

2.指出下面程序的输出,如果出错,请指出原因:

#include <stdio.h>
main()
{
char *p = "hello world";
p[0] = 'H';
printf("%s", p);
}

答案:运行时出错,因为p指向字符串常量,位于内存静态存储区,它在程序生命期内恒定不变,不能对其所指内容进行修改。

3.malloc和calloc的区别

malloc()函数有一个参数,即要分配的内存空间的大小:

void *malloc(size_t size);

calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小。

void *calloc(size_t numElements,size_t sizeOfElement);

如果调用成功,函数malloc()和函数calloc()都将返回所分配的内存空间的首地址。

函数malloc()和函数calloc()的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重新分配)可能会出现问题。函数calloc()会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那麽这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那麽这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零。

这里还需要说明一点:需要知道malloc的原理,详细信息可以去网上查。

4.malloc和new的区别

(1)分配成功malloc返回void*类型的指针,new返回制定类型的指针

(2)malloc需要指定分配空间的大小,new自动计算所需要大小

例如:int* p = (int *) malloc (1);
代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。

(3)当malloc失败时,它不会调用分配内存失败处理程序new_handler,而使用new的话会的

5.写出下面代码的输出:

#include <stdio.h>

int main()
{
char str1[] = "Hello";
char *str2 = "Hello";
printf("%d, %d\n", sizeof(str1), sizeof(str2));
}

答案:6, 4

sizeof(str1)是str1这个数组所占的空间。

sizeof(str2)是str2这个指针所占的空间,在32位处理器上是4个字节。

6.(short*)NULL + 10 = ?

解答:(short *)null+10 = 20。(short *)null系统会将null转换成0,并将0转换成short *对象,当然这期间会自动将转换的对象方到一个buff中,然后short *对象+10 相当于加了一个sizeof(short)*10的偏移地址。

7.进程和线程的区别

参考答案:(1)进程是系统进行资源分配和调度的一个独立单位,线程是进程的一个实体,是CPU调度和分派的基本单位。
(2)线程比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 
(3)一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
(4)一个程序至少有一个进程,一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。
(5)每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

8.什么是死锁?怎样避免死锁?

死锁是指多个进程循环等待它方占有的资源而无期限的僵持下去的局面。
产生的根本原因:计算机系统产生死锁的根本原因是资源有限且操作不当。
四个必要条件:互斥;非抢占,占有并等待,循环等待
解决死锁的基本策略:
一是鸵鸟策略。鸵鸟策略,即一种不理睬死锁的策略。像鸵鸟那样把头埋在沙子里,装住毫无问题。
二是预防策略。采用的办法是破坏产生死锁的四个必要条件中的一个或多个,向进程施加适当的限制,使系统绝不会进入死锁状态。
三是避免策略。产生死锁的四个必要条件有可能成立,但在资源动态分配的过程中使用某种办法防止系统进人安全和死锁状态。
四是检测和解除。允许系统产生死锁,然后使用检测算法及时地发现并解除它。

9.需要将一个数X的n到m位置零(m >n),假设位数从0开始,下面括号里填什么: 
#define(X) (.....)

解答:#define F(x) (((((x) >> n) << (sizeof(x) * 8 - m + n - 1) >> (sizeof(x) * 8 - m - 1))) ^ (x)) 
即现将最后的n位移出,再将原来中间n到m位移到最高位,在将将n到m位移到开始时的位置与原来的数做异或。
比如: x=1011010111010011 
           n=2, m=9,sizeof(x)=2 
向右移n=2位:                 0010110101110100 
向左移2*8-9+2-1=8位: 0111010000000000 
向右移2*8-9-1=6位:       0000000111010000 
与x作异或:                   ^ 1011010111010011

10.下面语句将会有什么奇怪现象发生?

#define MIN(a, b) ((a) < (b) ? (a) : (b))

int foo(int val)
{
        return MIN(val++, 100);
}

解答:当val <= 99时返回val++;当val >=100时返回100。MIN(val++, 100)扩展后为((val++) < (100) ? (val++) : (100)),由扩展式看出如果val < 100将返回val++;如果val >= 100,则返回100。

11.结构体(struct)和联合体(union)的区别:

参考答案:(1) struct和union都是由多个不同的数据类型成员组成,     但在任何同一时刻,     union中只存放了一个被选中的成员,     而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度大于等于所有成员长度之和(需要对齐结构体内的成员)。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在,所有成员相对于基地址的偏移量都是0。Union变量的长度等于最长的成员的长度。  

(2) 对于union的不同成员赋值,     将会对其它成员重写,     原来成员的值就不存在了,     而对于struct的不同成员赋值是互不影响的。  
         
举一个例子:  

例:  
#include     <stdio.h>  
    
main()  
{  
       union{                                   /*定义一个联合*/  
                int     i;  
                struct{                         /*在联合中定义一个结构*/  
                          char     first;  
                          char     second;  
                }half;  
       }number;  
       number.i=0x4241;                         /*联合成员赋值*/  
       printf("%c%c\n",     number.half.first,     number.half.second);  
       number.half.first='a';                   /*联合中结构成员赋值*/  
       number.half.second='b';  
       printf("%x\n",     number.i);  
       getch();  
}  

输出结果为:  
AB  
6261

12.写出下面代码的输出结果:

#include <iostream>

using namespace std;

char *list[] = {"hello", "hellohello"};

void main()
{
      cout << sizeof(list) << endl;
}

答案:8。list是一个指针数组,即数组里面的元素是指针,它本身是个数组有两个元素,这两个元素分别是指针,在32位机器上指针大小是4个字节,所以4×2=8。

13.TCP与UDP的区别
基于连接与无连接 
对系统资源的要求(TCP较多,UDP少) 
UDP程序结构较简单 
流模式与数据报模式 TCP保证数据正确性,UDP可能丢包TCP保证数据顺序,UDP不保证

TCP协议是面向连接的,每个数据包的传输过程是:先建立链路、数据传输、然后清除链路。数据包不包含目的地址。受端和发端不但顺序一致,而且内容相同。它的可靠性高,

UDP协议是面向无连接的,每个数据包都有完整的源、目的地址及分组编号,各自在网络中独立传输,传输中不管其顺序,数据到达收端后再进行排序组装,遇有丢失、差错和失序等情况,通过请求重发来解决。它的效率比较高。

14.IP地址172.16.100.5/255.255.255.252对应的网络地址和广播地址

答案:网络地址:172.16.100.4,广播地址:172.16.100.7

将IP地址和子网掩码做&运算可以得到网络地址,广播地址主机号部分所有位全为1,用来表示一个网络上所有主机。

15.写出下面程序的输出:

#include <iostream>

using namespace std;

class Base
{
public:
    Base() {}
    ~Base() {}
};

class Derive : public Base
{
public:
    Derive() {}
    ~Derive() {}
};

void main()
{
    Derive D;
    Base *pb = &D;
    cout << typeid(*pb).name() << endl;
}

答案:class Base

16.写出下面程序的输出:

#include <iostream>

using namespace std;

void ffs(int w, int *x, int &y, int *&z)
{
    w++;
    (*x)++;
    x = new int(10);
    y++;
    (*z)++;
    z = new int(10);
}

void main()
{
    int a = 2, b = 3, c = 4, d = 5;
    int *p = &d;
    ffs(a, &b, c, p);
    cout << a << " " << b << " " << c << " " << d << " " << *p << endl;
}

答案:2 4 5 6 10

z是对指针p的引用,对*z的修改也就是对*p的修改,(*z)++也就是(*p)++,即d加一;z = new int(10);将使z(也就是p)指向一个临时变量,其值为10,此时p将不指向d,故不会影响d的值。

17. 下面的C代码在VC++6.0下的运行结果是什么?请详细说明原因。

#include <stdio.h>
int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int *ptr1 = (int*)(&a + 1);
    int *ptr2 = (int*)((int)a + 1);
    printf("%x,%x", ptr1[-1], *ptr2);
    return 0;
}

答案:5,2000000

解释:&a取得地址以后加一的操作,指针增加了sizeof(a)这么多的绝对地址。这应该是指针加了20,然后一个-1有减去了4,这样相当于指针的绝对地址加了16,指向的就是a[4]的首地址
第二个加的是绝对内存地址,假如原来是0x0001,加完了以后是0x0002,然后再考虑pc的小端存储

18.写出下面程序的输出:

#include <stdio.h>
void main()   
{
    int i = 0, j = 2, k = 0;  

    i = j==3 ? ++j > 2 ? 1 : 2 : -1;   
    printf("i=%d, j=%d\n", i, j);  

    j = 2;   
    k = j == 3 ? -1 : ++j > 2 ? 1 : 2;   
    printf("k=%d, j=%d\n", k, j);   
}

答案:
i=-1, j=2
k=1, j=3

分析:这里应该分清楚三个概念:结合性,优先级,求值顺序。C语言中,优先级大家都知道,是确保a+b*c是被解释为a+(b*c)而不是(a+b)*c。而在一个操作数两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式x-y+ z则y应先与“-”号结合,   执行x-y运算,然后再执行+z的运算。这种自左至右的结合方向就称为“左结合性”。而自右至左的结合方向称为“右结合性”。最典型的右结合性运算符是赋值运算符。如x=y=z,由于“=”的右结合性,应先执行y=z再执行x=(y=z)运算。C语言运算符中有不少为右结合性,比如?:,赋值操作符,*,&等单目运算符。

i = j==3 ? ++j > 2 ? 1 : 2 : -1;可以解释为i = (j==3 ? (++j > 2 ? 1 : 2) : -1);,这里的括号仅表示结合性,并不表示优先级,先判断j==3,为假,所以i赋值为-1,此时j的值保持2。

k = j == 3 ? -1 : ++j > 2 ? 1 : 2;“:?”从右向左结合,可以解释为k = (j == 3 ? -1 : (++j > 2 ? 1 : 2));这里的括号仅表示结合性,并不表示优先级,先判断j==3,为假,所以i赋值为(++j > 2 ? 1 : 2)的结果,++j,j变成3,3>2,所以(++j > 2 ? 1 : 2)的结果为1,j的值为3。

19.写出下面程序的输出:

#include <stdio.h>

void main()
{
    int x[5] = {2, 4, 6, 8, 10};
    int *p, **pp;

    p = x;
    pp = &p;
    printf("%d", *(p++));
    printf("%3d\n", **pp);
}

答案:2 4

分析:*p++和*(p++)意义相同,*和++具有相同优先级,所以从左向右运算这个表达式,即取指针p所指的对象,然后将p递增1。

20.写出下面程序的输出:

#include <stdio.h>
void main()
{
    int a = 1, b = 2;
    printf("%d %d\n", (a++)+b, a+++b);
}

答案:4 3

分析:函数参数从右向左入栈,先计算第二个表达式a+++b,++运算符从右向左结合,所以可以理解为(a++)+b,先计算a+b的值,即3,然后a++,a的值变成2。然后计算第一个表达式,计算a+b的值,即4,然后a++,a的值变成3。

21.请分别写出布尔变量,整形变量,浮点变量,指针变量和零值比较的if语句:

答案:

布尔变量:
if (flag) // 表示flag为真
if (!flag) // 表示flag为假

整形变量:
if (value == 0) 
if (value != 0)

浮点变量:
if ((x>=-EPSINON) && (x<=EPSINON))   // #define EPSINON 0.00001

指针变量:
if (p == NULL) // p与NULL显式比较,强调p是指针变量
if (p != NULL)

原文地址:https://www.cnblogs.com/alamps/p/1686162.html