C/C++试题(7)

 

 

Q5:请讲一讲析构函数和虚函数的用法和作用?   

 A

析构函数一般在对象撤消前做收尾工作,比如回收内存等工作,虚拟函数的功能是使子类可以用同

析构函数:

   如果一个基类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明。

不管你是不是建立了自己的析构函数,析构函数总是有的(如果需要的话,编译器会免费送你一个)。那么要不要建立自己的析构函数呢?答案非常简单:

        不管你是不是建立了自己的析构函数,析构函数总是有的(如果需要的话,编译器会免费送你一个)。那么要不要建立自己的析构函数呢?答案非常简单:

        1、如果类分配了系统资源而没有相应的自动释放机制,需要进行手工资源释放,那么你需要一个析构函数。注意如果有了自动释放机制,例如auto_ptr所管理的内存,那么就不需要析构函数。

        2、如果该类必须或者可以作为基类,那么你需要一个虚拟析构函数,即便他什么也不做。这时为了确保删除基类指针时析构派生类对象。

        3、除此以外,没理由建立自己的析构函数,即便是一个空析构函数。

   析构函数最大的注意事项是不能抛出异常,否则任何使用该类的代码都无法做到异常安全。

虚函数:

虚函数充分体现了面向对象思想中的继承和多态性这两大特性。虚函数是动态联编的基础。虚函数是成员函数,而且是非static的成员函数。 动态联编只能通过指针或引用标识对象来操作虚函数。如果采用一般类型的标识对象来操作虚函数,则将采用静态联编方式调用虚函数。

虚函数的定义要遵循以下重要规则:

  1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后

联编的。

  2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。

  3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。

  4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。

  5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。

  6.析构函数可以是虚函数,而且通常声名为虚函数。

 

1.找错

Void test1()
{
    char string[10];
    char* str1="0123456789";
   strcpy(string, str1);
}


Void test2()
{
    char string[10], str1[10];
   for(I=0; I<10;I++)
{
        str1[i] ='a';
}
strcpy(string, str1);
}

Void test3(char* str1)
{
    char string[10];
    if(strlen(str1)<=10)
{
    strcpy(string, str1);
}
}

答:test1数组长度不够,test2for有问题,test3strlen恒等于2


2.   
找错
#define MAX_SRM 256

DSN get_SRM_no()
{
        static int SRM_no;
        int I;
        for(I=0;I<MAX_SRM;I++,SRM_no++)
{
SRM_no %= MAX_SRM;
if(MY_SRM.state==IDLE)
{
    break;
}
}
if(I>=MAX_SRM)
return (NULL_SRM);
else
return SRM_no;
}

答:出这题目的人够无聊的,看我半天,这种垃圾题拿去烧了。随便找了下,大家有兴趣自己再找,记得通知我。

If(my_.......)==IDLE 改成 IDLE==if(….),良好的编程风格。


3.   
写出程序运行结果

int sum(int a)
{
        auto int c=0;
        static int b=3;
    c+=1;
    b+=2;
    return(a+b+c);
}
void main()
{
        int I;
    int a=2;
    for(I=0;I<5;I++)
    {
        printf("%d,", sum(a));
    }
}

答:运行结果:810121416。因为静态变量。

4.    写出结果

int func(int a)
{
    int b;
    switch(a)
    {
        case 1: b=30;
        case 2: b=20;
        case 3: b=16;
        default: b=0
}
return b;
}
func(1)=?

答:0

5:写出结果
int a[3];
a[0]=0; a[1]=1; a[2]=2;
int *p, *q;
p=a;
q=&a[2];
a[q-p]=?

答:2

6. 定义 int **a[3][4], 则变量占有的内存空间为:__4___

7. 编写一个函数,要求输入年月日时分秒,输出该年月日时分秒的下一秒。如输入 2004 12 31 235959秒,则输出 2005 1 1 000秒。

答:

#include<stdio.h>
#include<iostream.h>
#include<string.h>
typedef struct {
   int tm_year;
   short tm_second; 
   short tm_minute;
   short tm_hour;  
   short tm_day;  
   short tm_month;  
} time_all;

bool isRun(int year)//判断是否为闰年
{
 if(year%4==0)
 {
  if(year%100==0)
  {
   if(year%400==0) return true;
   else return false;
  }
  else return true;
 }
 else return false;
}

int getDays(int month,int year)//根据月份和年份算出当月的天数
{
 int a[12]={31,28,31,30,31,30,31,30,31,30,30,31};
 if(month!=2) return a[month-1];
 else
 {
  if(isRun(year)) return 29;
  else            return 28;
 }
}

int addSecond(time_all& nowTime)//增加一秒
{
//    time_all tempStruct;
// memcpy(&tempStruct,&nowTime,sizeof(time_all)); 
 if((nowTime.tm_second=++nowTime.tm_second%60)!=0)
         return 0;

 if((nowTime.tm_minute=(nowTime.tm_minute+1)%60)!=0)
  return 0;

 if((nowTime.tm_hour=(nowTime.tm_hour+1)%24)!=0)
  return 0;

 if(nowTime.tm_day!=getDays(nowTime.tm_month,nowTime.tm_year))
 {
  nowTime.tm_day++;
  return 0;
 }
 else nowTime.tm_day=1;

 if(nowTime.tm_month!=12)
 {
  nowTime.tm_month++;
  return 0;
 }
 else nowTime.tm_month=1;
 
 nowTime.tm_year++;
 return 0;
}
   
void main()
{
 time_all timeNow;
 cout<<"输入时间 依次顺序为年、月、日、时、分、妙"<<endl;
 cin>>timeNow.tm_year>>timeNow.tm_month>>timeNow.tm_day>>timeNow.tm_hour>>timeNow.tm_minute>>timeNow.tm_second;
 addSecond(timeNow);
    cout<<timeNow.tm_year<<"年"<<timeNow.tm_month<<"月"<<timeNow.tm_day<<"日"<<timeNow.tm_hour<<"时"<<timeNow.tm_minute<<"分"<<timeNow.tm_second<<"秒";
}

8.子函数参数压站次序。

答:函数参数入栈的方式 

在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式。 

比如说__cdecl,它是标准的c方法的堆栈调用方式,就是在函数调用时的参数压入堆栈是与函数的声明顺序相反的,其它两个可以看MSDN,不过这个对我们编程没有太大的作用 

调用约定   

调用约定(Calling  convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定: 

_cdecl   

按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。 

 如函数void  test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ 

 这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。 

_stdcall   

 按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int  func(int  a,  double  b)的修饰名是_func@12。对于“C++”函数,则有所不同。 

 所有的Win32  API函数都遵循该约定。 

 _fastcall   

 头两个DWORD类型或者占更少字节的参数被放入ECXEDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int  func(int  a,  double  b)的修饰名是@func@12。对于“C++”函数,有所不同。 

 未来的编译器可能使用不同的寄存器来存放参数。 

 thiscall   

 仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。 

 naked  call   

 采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESIEDIEBXEBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked  call不产生这样的代码。 

 naked  call不是类型修饰符,故必须和_declspec共同使用,如下: 

 __declspec(  naked  )  int  func(  formal_parameters  ) 

 { 

 //  Function  body 

 } 

 过时的调用约定   

 原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如: 

 #define  CALLBACK  __stdcall 

 #define  WINAPI  __stdcall 

 #define  WINAPIV  __cdecl 

 #define  APIENTRY  WINAPI 

 #define  APIPRIVATE  __stdcall 

 #define  PASCAL  __stdcall  

c/c++的函数参数压栈顺序. 

为了这句话丢了很多次人.无所谓了,反正咱脸皮厚.

   总结一下:   编译出来的c/c++程序的参数压栈顺序只和编译器相关!

下面列举了一些常见的编译器的调用约定

VC6:

        调用约定        堆栈清除    参数传递

        __cdecl                  调用者      从右到左,通过堆栈传递

        __stdcall                  函数体         从右到左,通过堆栈传递

        __fastcall                 函数体         从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈

        thiscall                     函数体         this指针默认通过ECX传递,其它参数从右到左入栈

__cdeclC/C++的默认调用约定; VC的调用约定中并没有thiscall这个关键字,它是类成员函数默认调用约定;

C/C++中的main(wmain)函数的调用约定必须是__cdecl,不允许更改;

默认调用约定一般能够通过编译器设置进行更改,如果你的代码依赖于调用约定,请明确指出需要使用的调用约定;

C++Builder6:

        调用约定        堆栈清除    参数传递

        __fastcall                  函数体      从左到右,优先使用寄存器(EAX,EDX,ECX),然后使用堆栈 (兼容Delphiregister)

        (register__fastcall等同)

        __pascal                     函数体      从左到右,通过堆栈传递

        __cdecl                    调用者    从右到左,通过堆栈传递(C/C++默认调用约定兼容)

        __stdcall                     函数体      从右到左,通过堆栈传递(VC中的__stdcall兼容)

        __msfastcall                函数体      从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈(兼容VC__fastcall)

上述资料来源于HouSisong的一篇文章:http://www.allaboutprogram.com/index.php?option=content&task=view&id=29&Itemid=31.

由于能力和资源有限,只能找到这些东西,主要的差异体现在fastcall上面,vc是前两个参数放入寄存器,后面的压栈,bcb是前3哥参数使用寄存器,

更有变态的,一个朋友朋友说有的参数超过7个以后前5个从左到右传递,后面的从右到走,上面说的不可不信,不可全信.

如何确定你的编译采用的那种顺序那?

#include <stdio.h>

int f(int i,int j,int k);

int main()

{

static int i=0;

f(i++,i++,i++);

return 0;

}

int f(int i,int j,int k)

{

int l;

int g;

printf("k=%d:[%x]/n",k,&k);

printf("j=%d:[%x]/n",j,&j);

printf("i=%d:[%x]/n",i,&i);

printf("___________/n");

printf("l:%x/n",&l);

printf("g:%x/n",&g);

}

看看k->i的地址的增长顺序和l->g的顺序是否相同,如果相同则是从右到左,否则从左到右.

PS:

   本来通过打印参数的值来判断那个先入栈,结果被一个朋友批评了,

   他说:压栈顺序和参数计算顺序不是一回事,所以还是看地址更有保证.

//看过的朋友当作笑谈吧。

  曾经看到一篇文章上面说:c/c++参数压栈顺序是从右到左,pascal参数压栈是从左到右.

9.子函数参数出栈如何完成?
答:这个不懂,也找不到。知道的朋友介绍一下。

 

11000!有几位数,为什么

答:

   运用取以10为底的对数,有对于任意的非负十进制数n,
    对其取以10为底的对数函数lgn的结果向下取整后加1表示该数的十进制位数,
    举例如下(方括号“〔〕”表示对其中的数进行向下取整):

    当n=1时,lg1=0,〔lg1〕+1=0+1=1,n为1位十进制数;
    当n=2时,lg2=0.30,〔lg2〕+1=0+1=1,n为1位十进制数;
    … …
    当n=10时,lg10=1,〔lg10〕+1=1+1=2,n为2位十进制数;
    当n=11时,lg11=1.041,〔lg11〕+1=1+1=2,n为2位十进制数;
    … …
    当n=100时,lg100=2,〔lg100〕+1=2+1=3,n为3位十进制数;
    … …
    当n=468时,lg468=2.670,〔lg468〕+1=2+1=3,n为3位十进制数;
    … …
    当n=1000时,lg1000=3,〔lg1000〕+1=3+1=4,n为4位十进制数;
    所以,当n=1000!时,
    [lg(1000!)]+1
    =〔lg(1*2*3*…*998*999*1000)〕+1
   =[lg1+lg2+lg3+...+lg998+lg999+lg1000]+1
   =2567+1
   =2568
   即,1000!有2568位。
 

2

F(n)=1 n>8 n< 12
F(n)=2 n<
2
F(n)=3 n=
6
F(n)=4 n=other
使用+ - * /sign(n)函数组合出F(n)函数
sign(n)=0 n=0
sign(n)=-1 n<0
sign(n)=1 n>0

答:

3、编一个程序求质数的和例如F(7)=1+3+5+7+11+13+17=58

解:

#include <iostream>
#include "math"

///判断是不是素数
bool IsPrime(int Num)
{
      int i;
      if (Num < 2)
           return false;
      for (i=2; i<=sqrt(Num); i++)
      {
           if (Num%i == 0)
               break;
      }
}

void main()
{
    int low, up;
    int total=0;
   
    do{
        cout<<"输入下限: ";
        cin>>low;
        cout<<"输入上限: ";
        cin>>up;
    }while(low>=up);
   
    for(int t=low; t<=up; t++)
        if( IsPrime(t) )
            total+=t;
            cout<<total<<"/t";
    cout<<endl;
}

 

1. 三个
(a+b)+c==(b+a)+c
(a+b)+c==(a+c)+b
float:a,b,c 问值

答:有可能是00,有可能是11。因为float没0值。

2. 把一个链表反向填空

答:

3. 设计一个重采样系统,说明如何anti-alias

答:

4. y1(n)=x(2n), y2(n)=x(n/2),问:
如果y1为周期函数,那么x是否为周期函数//是
如果x为周期函数,那么y1是否为周期函数//是
如果y2为周期函数,那么x是否为周期函数//是
如果x为周期函数,那么y2是否为周期函数//是

5. 如果模拟信号的带宽为5KHZ,要用8K的采样率,怎么办。

答:

4. 某个程序在一个嵌入式系统( 200M CPU, 50M SDRAM)中已经最化了,换到另一个系统
( 300M
CPU, 50M SDRAM)中运行,还需要优化吗?

5. x^4+a*x^3+x^2+c*x+d最少需要作几次乘法

6. 什么情况下,sin(x+y)+y ~ ....

7. 下面哪种排序法对12354最快
a quick sort
b.buble sort
c.merge sort

8. 哪种结构,平均来讲,获取一个值最快
a. binary tree
b. hash table
c. stack


问:有13个人 围圈坐  从1开始数数  逢个位数为3退  问最后一个原来的序号 编程实现   (典型的约瑟夫问题 )

//header.h
#ifndef N
#define N
typedef struct link
{
    int data;
    struct link *next;
}lk;
void create(lk *const&,int k);
void out(lk *const&);
#endif

//func.cpp
#include <iostream>
#include "header.h"
using namespace std;

void create(lk *const &head,int k)
{
    lk *tem,*p;
    tem=p=head;
    int i;
    for(i=1;i<k;++i)
    {
       p->data=i;
       p->next=new lk;
       p=p->next;
    }
    p->data=i;
    p->next=head;
}
void out(lk *const &head)
{
    lk *p=head,*tem=NULL;
    int i=1;
    while(p->next!=p)
    {


        if(!(i%3))
        {
             cout<<tem->next->data<<endl;
             system("pause");
             tem->next=tem->next->next;
             delete p;
             p=tem;
             /*tem=tem->next;
             delete tem;*/
        }
        ++i;
        tem=p;
        p=p->next;
    }
    cout<<p->data;
}

//main.cpp
#include <iostream>
#include "header.h"
using namespace std;

int main()
{
    lk *head=new lk,*tem;
    head->next=NULL;
    int k;
    cout<<"你想让你的环有几个结点:";
    cin>>k;
    create(head,k);
    out(head);

system("pause");
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值