山里狼

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

下面程序的结果是多少?

复制代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <conio.h>
using namespace std;
int main()
{
  float a=1.0f;
  cout<<(int)a<<endl;
  cout<<&a<<endl;
  cout<<(int&)a<<endl;
  cout<<boolalpha<<((int)a==(int&)a)<<endl;
  float b=0.0f;
  cout<<(int)b<<endl;
  cout<<&b<<endl;
  cout<<(int&)b<<endl;
  cout<<boolalpha<<((int)b==(int&)b)<<endl;
  return 0;
}
复制代码

在机器上运行一下,可以得到结果cout<<(int&)a<<endl;输出的是1065363216,而不是1.这是因为浮点数在内存里和整数的存储方式不同,(int&)a相当于将该浮点数地址开始的sizeof(int)个字节当成int型的数据输出,因此这取决于float型数据在内存中的存储方式,而不是经过(int&)a显示转换的结果1。因为float a=1.0f在内存中的表示都是3f800000,而浮点数和一般整型不一样,所以当(int&)a强制转换时,会把内存值3f800000当做int型输出,所以结果自然变成了1065363216。

下面的程序结果是多少

复制代码
#include <stdio.h>
int main()
{
  unsigned int a=0xFFFFFFF7;
  unsigned char i=(unsigned char)a;
  char* b=(char*)&a;
  printf("%08x,%08x",i,*b);
}
复制代码

unsigned int赋值给unsigned char变量时会发生字节截断,三位和高于三位的将被程序自动丢弃。那么第二个数,也就是char* b=(char*)a中a本身为一个uint类型的值,把他的地址付给一个执行char类型数据的指针,char类型的长度只有一个字节,打印char类型的指针指向的值会是多少?&a的结果是一个指针,他的类型决定于a的类型,此处&a的类型应该是unsigned int *; char *b=(char*)&a;上面等价于unsigned int *p=&a; char *b=(char*)p。上面的步骤就是将一个unsigned int类型指针强制转换成一个char类型的指针,所以请注意,这里是char类型的指针转换,而不是char类型的转换。这样转换后,假设a的地址是x:p+1=x+1*sizeof(int)=x+1*4=x+4; b+1=x+1*sizeof(char)=x+1*1=x+1;影响的是指针的寻址。

有两个变量a和b,不用if,?,和switch或其他的判断语句,找出两个数中间较大的

方案一:int max=((a+b)+abs(a-b))/2

方案二:int c=a-b ;

              char=*strs[2]={"a Large","b Large"};

    c=unsigned(c)>>(sizeof(int)*8-1);

在C++程序中调用被C编译器编译后的函数,为什么要加extern c?

C++语言支持函数重载,C语言不支持函数重载,函数被C++编译后在库中的名字与C语言不同。假设某个函数的原型为void foo(int x,inty y)。该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern C解决名字匹配问题。

头文件中的ifdef/define/endif是干什么用的?

防止该头文件被重复引用

用一个宏定义FIND求一个结构体struct里某个相对struct的偏移量

复制代码
{

int a;

char b[20];

double ccc;

}

则FIND(student,a);//等于0

  FIND(student,b);//等于4
复制代码

可以使用#define FIND(struc,e) (size_t)&(((struc*)0)->e),其中(struc *0)表示将常量0强制转化为struc*型指针指向的地址;&(((struc*)0)->e)表示取取结构体指针(struc *)0的成员e的地址,因为该结构体的首地址为0,所以其实就是得到了成员e距离结构体首地址的偏移量。size_t是一种数据类型,为了便于不同系统之间移植,最好定义为一种无符号数据类型,一般为unsigned int。

const有什么用途,请至少说明两种,和const比较有什么不同

在C程序中,const的用法主要有定义常量、修饰函数参数、修饰函数返回值。在C++程序中,它还可以修饰函数的定义体,定义类中某个成员函数为恒态函数,即不改变类中的数据成员。

const常量具有数据类型,而宏常量没有数据类型,编译器可以对前者进行类型安全检查,而对后者只进行字符串替换,没有类型安全检查。并且在字符串替换中可能会产生意料不到的错误;有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++程序中只是用const常量而不使用宏常量,即const常量完全取代宏常量

常量的引进是在早期的C++版本中,当时标准C规范正在制定,那时候,常量被看作一个好的思想而被包含在C中,但是C中的const意思是一个不能被改变的普通变量,在C中,它总是占用内存,而且他的名字是全局符。C编译器不能把const堪称一个编译期间的常量,在C中,如果:const bufsize=100;char buf[bufsize];尽管看起来好像做了一个合理的事,但是这将得到一个错误的结果。因为bufsize占用内存的某个地方,所以C编译器不知到它在编译时的值。在C语言中可以选择这样写const bufsize;,这样写在C++中是不对的,而C编译器则把它作为一个声明,这个声明指明在别的地方有内存分配。因为C默认const是外部连接的,C++默认const是内部连接的,这样,如果在C++中想完成与C中同样的事情,必须用extern把内部连接改成外部连接,extern const bufsize;这种方法也可以用在C语言中,在C语言中使用限定符const不是很有用,即使是在常熟表达式里像使用一个已命名的值,使用const也不是很有用,C迫使程序员在预处理器里使用define。

有如下类,Class A class {void f() const {...}},在上面这种哦你情况下,如果要修改类的成员变量,该怎么办?

在C++程序中,类里面的数据成员加上mutable后,修饰为const的成员函数,就可以修改它了。

求解下面程序的结果

复制代码
#include <iostream>
using namespace std;
class A1
{
  public: int a;
  static int b;
  A1();
  ~A1();
};
class A2
{
  public:int a;char c;
  A2();
  ~A2();
};
class A3
{
  public:float a;char c;
  A3();
  ~A3();
};
class A4
{
  public:float a;int b;char c;
  A4();
  ~A4();
};
class A5
{
  public:double d;float a;int b;char c;
  A5();
  ~A5();
};
int main()
{
  cout<<sizeof(A1)<<endl;
  cout<<sizeof(A2)<<endl;
  cout<<sizeof(A3)<<endl;
  cout<<sizeof(A4)<<endl;
  cout<<sizeof(A5)<<endl;
  return 0;
}
复制代码

CPU的优化原则大致是这样的:对于n字节的元素,它的首地址能被n整除,才能获得最好的性能。设计编译器的时候可以遵循这个原则,对于每一个变量,可以从当前位置向后找到第一个满足这个条件的地址作为首地址。另外一点,因为静态变量是放在全局数据区的,sizeof计算栈中中分配的大小,是不不会计算在内的。所以答案依次为4,8,8,12,24

说明sizeof和strlen之间的区别

第一个例子char* ss="0123456789";sizeof(ss)的结果为4,ss是只想字符串常量的指针;sizeof(*ss)结果为1,*ss是第一个字符

第二个例子char ss[]="0123456789";sizeof(ss)的结果为11,ss是数组,计算到“\0”的位置,因此是11;sizeof(*s)的结果为1,*ss是第一个字符

第三个例子,char ss[100]="0123456789";sizeof(ss)的结果为100,ss表示在内存中预分配的大小,strlen(ss)的结果为10,它表示内部实现一个循环计算字符串的长度,知道“\0”为止。

第四个例子,int ss[100]="0123456789";sizeof(ss)的结果为400,ss表示在内存中的大小,strlen(ss)错误,strlen的参数只能是char*,且必须是以“\0”结束的

第五个例子,class X{int i;int j;char k;}; X x;cout<<sizeof(X)<<endl;结果为12,理由是内存补齐

通过对sizeof与strlen的深入理解,得出两者区别如下:

sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型。该类型保证能够容纳实现所建立的最大对象的字节大小

sizeof是运算符,strlen是函数

sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以“\0”结尾的,sizeof还可以用函数做参数,如short f();sizeof(f());

数组做sizeof的参数不退化,传递给strlen就退化为指针

大部分编译程序在编译的是偶就把sizeof计算过了,是类型或变量的长度,这就是sizeof(x)可以用来定义数组唯数的原因

strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占用内存的大小

sizeof后如果是类型必须加括号,如果是变量明可以不加括号。这是因为sizeof是个操作符而不是函数

当使用了一个就诶够类型或变量时,sizeof返回实际的大小,当使用一个静态的空间数组时,sizeof返回全部数组的尺寸。sizeof操作符不能返回被动态分配的数组或外部的数组尺寸

数组作为参数传递给函数时传的是指针而不是数组,传递的数组的首地址,如fun(char [8]);fun(char [])都等价与fun(char *)。在C++里传递数组永远都是传递指向数组首元素的指针,编译器不知到数组的大小,如果想在函数内知道数组的大小,需要这样做:进入函数后用memcpy将数组复制出来,长度由另一个参数传递进去,如fun(unsigned char * p1,int len)
{unsigned char * buf=new unsigned char[len+1];memcpy(buf,p1,len);}

计算结构变量的大小就必须讨论数据对其问题。为了使CPU存取的速度最快,C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫做数据堆砌。这样做或许会浪费一些内存,但是在理论上CPU速度快了。当然,这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对其设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可。或者如果要按照字节存储,而不进行数据对其,可以在Options对话框中修改Advance Compiler选项卡中的Data Alignment为按字节对齐。

sizeof操作符不能用于函数类型、不玩全类型或位字段。不玩全类型指具有未知存储大小数据的数据类型,如未存储大小的数组类型,未知内容的结构或联合类型,void等类型等。

一个空类占多少空间?多重继承的空类呢?

一个空类所占的空间为1个字节,多重继承的空类所占空间还是1字节。

内联函数和宏定义差别是什么?

内联函数和普通函数相比可以加快程序运行的速度,因为不许要中断调用,在编译的时候内联函数可以直接被嵌入到目标代码中。而宏只是一个简单的替换。内联函数要做参数类型检查,这是内联函数跟宏相比的优势。inline是指嵌入代码,就是在调用函数的地方不是跳转,而是直接把代码写到里面去,对于短小的代码来说,inline可以带来一定的效率提升,而且和C时代的宏函数相比,inline更安全可靠。可是这个以增加空间消耗为代价的。至于是否需要inline函数,就需要根据实际情况来取舍了。inline一般只用于如下情况:一个函数不断被重复调用;函数只有简单的几行,且函数内不包含for,while和switch语句。一般来说,我们写小程序每笔要定义成inline,但是如果要完成一个工程项目,当一个简单函数被调用多次时,则应该考虑用inline。

宏在C语言里面及其重要,而在C++里面用得就少多了。关于宏的第一规则是绝不应该去使用它,除非你不得不这么做。几乎每个宏都表明了程序设计语言里,程序里或者程序员的一个缺陷,因为它将在编译器看到程序的正文之前重新摆布这些正文。宏也许是许多程序设计工具的主要麻烦。所以,如果你使用了宏,就应该准备只能从各种工具中得到较少的服务。宏是在代码出不加任何验证的简单替换,而内联函数是将代码直接插入嗲用处,而减少了普通函数调用时的资源消耗。宏不是函数,只是在编译前将程序中有关字符串替换成宏体。

inline函数是函数,但在编译中不但读产生代码,而是将有关代码嵌入到调用处 inline fac(float i){return i*i;}

指针和引用的区别

非空区别,在任何情况下都不能使用只想空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让他只想一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针高

合法性区别:在使用引用之前不许要测试它的合法性,相反指针则应该总是被测试防止其为空。

可修改区别:指针与引用的另一个重要的区别是指针可以被重载复制以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变

应用区别:总的来说,在一下情况应该使用指针,一是考虑存在不指向任何对象的可能,而是需要能够在不同的时刻指向不同的对象。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么应该使用引用。

下面五个函数哪个能够成功进行两个数的交换?

复制代码
#include <iostream>
using namespace std;
void swap1(int p,int q)
{
  int temp;
  temp=p;
  p=q;
  q=temp;
}
void swap2(int *p,int *q)
{
  int *temp;
  *temp=*p;
  *p=*q;
  *q=*temp;
}
void swap3(int *p,int *q)
{
  int *temp;
  temp=p;
  p=q;
  q=temp;
}
void swap4(int *p,int *q)
{
  int temp;
  temp=*p;
  *p=*q;
  q*=temp;
}
void swap5(int &p,int &q)
{
  int temp;
  temp=p;
  p=q;
  q=temp;
}
复制代码

这道题目考察函数参数传递、值传递、指针传递和引用传递。swap1传的是副本的值,在函数体内修改了形参p和q,p和q的值确实互换了,但是他们是局部变量,不会影响到主函数中的a和b,当函数swap1的声明周期结束时,q和q所在的栈也就被删除了

swap2传的是一个地址进去,在函数体内的形参*q和*q是指向实际参数a和b的两个指针,这里需要注意的是int *temp;*temp=*p;是不符合逻辑的一段代码,int *temp新建了一个指针,但是没有分配内存。*temp=*p不是指向而是拷贝,把*p所致像的内存里的值也就是实参a拷贝到*temp所指向内存里面了。但是int *temp不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让他存值。分配的随机地址是个意外,且函数结束后不收回,造成内存泄漏。那么swap2能实现两个数的交换吗?这里要看编译器,Dev-C++可以通过测试,但是在更加严格的编译器VS2008,这段代码会报错。

swap3传的是一个地址进去,在函数体内形参*p和*q是指向实际参数a和b地址的两个指针,这里需要注意的是int *temp;temp=p,int *temp新建了一个指针,但是没有分配内存,temp=p是指向而不是拷贝,temp指向了*所指向的地址,而后面的代码的意思是p指向了*q所指向的地址,q指向了*p所指向的地址,但是wap3并不能实现两个数的交换,这时因为函数体内只是指针的变化,而对地址中的值却没有变化。

swap4可以实现两个数的交换,因为它修改的是指针所指向的地址中的值

swap5与swap4相似,是一个引用传递,修改的结果将直接影响实参

下面的这个程序会有什么后果

复制代码
#include <iostream>
void GetMemoery(char *p, int num)
{
  p=(char*)malloc(sizeof(char)*num);
};
int mian()
{
  char *str=NULL;
  GetMemory(str,100);
  strcpy(str,"Hello");
  return 0;
}
复制代码

毛病出在函数GetMemoery中,void GetMemoery中的*p实际上是主函数中的一个副本,编译器总是要为每个参数副本制作临时副本,在本例中,p申请了新的内容,只是把p所指向的内存地址改变了但是str丝毫未变。因为函数GetMemory没有返回值,因此str并不指向p所申请的那段内存,所以函数GetMemoery并不能输出任何东西,事实上,每执行一次GetMemoery就会申请一块内存,但申请的内存得不到释放,结果就是内存一直被独占,最终造成内存泄漏。

如果一定要用指针参数去申请内存,那么应该采用指向指针的指针,传str的地址给函数GetMemory,如下:

复制代码
void GetMemory(char **p, int num)
{
  *p=(char*)malloc(sizeof(char)*num);
};
int mian()
{
  char *str=NULL;
  GetMemory(&str,100);
  strcpy(str,"hello");
  cout<<*str<<endl;
  cout<<str<<endl;
  cout<<&str<<endl;
  return 0;
}
复制代码

由于指向指针的指针这个概念不是很容易理解,我们可以用函数返回值来传递动态内存,这种方法更加简单:

复制代码
char *GetMemory(char *p, int num)
{
  p=(char*)malloc(sizeof(char)*num);
  return p;
}
int mian()
{
  char *str=null;
  str=GetMemory(str,100);
  strcpy(str,"Hello");
  return 0;
}
复制代码

这个函数有什么问题?该如何修改?

char *strA()
{
  char str[]="Hello world";
  return str;
}

这个str里面存的地址是函数strA栈帧里“hello world”首地址,函数调用玩成,栈帧恢复到调用strA之前的状态,临时空间被重置,堆栈回缩,strA栈帧不再属于应该访问的范围,存与strA栈帧里面的hello word当然也不应该被访问了,这段程序可以正确输出结果,但是这种访问方法违背了函数的栈帧机制。

分配内存时有一句老化,First time you do it,then something change it,也许是一个妨碍其他函数调用的内存块,这些情况都无法预知。如果运行一段函数,不会改变其他函数所调用的内存,这种情况下,你运行多少次都不是问题。但是,只要另一个函数调用的话,你就会发现,这种方式的不合理及危险性。我们面对的是一个有操作系统覆盖的计算机,而一个不再访问的内存块,随时都有被回收或作为它用的可能。如果像获得正确的函数,改成下面这样:

const char* strA()
{
  char *str="hello world";
  return str;
}

这里首先要搞清楚char c[]="hello world"是分配一个局部数组;char *c="hello world";是分陪一个全局数组。局部数组是一个局部变量,它所对应的是内存中的栈,全局数组是全局变量,它所对应的是内存中的全局区域。字符串常量保存在只读的数据段,而不是像全局变量保存在普通数据段,也就是静态存储区,另外如果想要修改,也可以这样

const char *strA()
{
  static char str[]="hello world";
  return str;
}

通过static开辟一段静态存储空间。

请问下面代码的输出是多少?

复制代码
#include <stdio.h>
class A{
  A(){m_a=1;m_b=2;};
  ~A(){};
  void fun(){printf("%d %d",m_a,_b);};
  private:int m_a; int m_b;
};
class B{
  public:B(){m_c=3;};
  ~B();
  void fun(){printf("%d",m_c);}
  private:int m_c;
};
void main(){
  A a;
  B *p=(*)(&a);
  p->fun();
}
复制代码

首先可以肯定的是这面这段代码非常糟糕,无论是可读性还是安全性都很差。这道题目出题的目的是考察你对内存偏移的理解,*p=(*)(&a);这就是一个野蛮的转化,强制把a的地址内容堪称一个类对象,p指向的是a类的内存空间。正常情况下,类只有一个元素int m_c,但是a类的内存空间中存放第一个元素的位置是m_a,p指向的内存对象的内存首地址,当p->func()调用的时候,编译器对m_c对它的认识就是m_c距离对象的偏移量是0,于是打印了对象a首地址的编译量变量的值,所以打印的是m_a的值。

请写出函数指针、函数返回指针、const指针、指向const的指针、指向const的const指针

void (*f)();void *f();const int *;int* const;const int* const;

找出下面程序的错误,并解释为什么

复制代码
#include <stdio.h>
int max(int x, int y){
  return x>y?x:y;
}
int main(){
  int max(x,y);
  int *p=&max;
  int a,b,c,d;
  scanf("%d%d%d",a,b,c);
  d=(*p)((*p)(s,b),c);
  printf("%d",d);
  return 0;
}
复制代码

这道程序存在这函数指针错误的问题,正确的程序如下:

复制代码
#include <stdio.h>
int max(int x, int y){
  return x>y?x:y;
}
int main(){
  int max(intt,int);
  int (*p)(int,int)=&max;
  int a,b,c,d;
  scanf("%d%d%d",&a,&b,&c);
  d=(*p)((*p)(a,b),c);
  printf("%d",d);
  return 0;
}
复制代码

下面的数据声明代表什么?

float(**def)[10];  double*(*gh)[10];  double(*f[10])();  int*((*b)[10]);  Long(*fun)(int)  Int (*(*F)(int,int))(int);

def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float

gh是一个指针,它指向一个一维数组,数组元素都是double*

f是一个数组,f有十个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数

是一个一维数组的指针,数组的元素都是int*

函数指针

F是一个函数指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函数,返回的函数指针指向有一个int参数且返回int的函数

以下程序的输出结果是多少

复制代码
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
  int v[2][10]={{1,2,3,4,5,6,7,8,9,10},{11,12,13,14,15,16,17,18,19,20}};
  int (*a)[10]=v;
  cout<<**a<<endl;
  cout<<**(a+1)<<endl;
  cout<<*(*a+1)<<endl;
  cout<<*(a[0]+1)<<endl;
  cout<<*(a[1])<<endl;
  return 0;
}2
复制代码

本题目定义了一个指针指向一个有10个int元素的数组。a+1表明a指针向后移动1*sizeof(数组大小);a+1后向后移动了40个字节,*a+1仅针对这一行向后移动了4个字节,因此最后结果是1,11,2,2,11.

用变量a给出下面的定义:一个整型数;一个指向整型数的指针;一个指向指针的指针,它指向的指针是指向一饿整型数;一个有10个整型数的数组;一个有10个指针的数组,该指针指向一个整型数;一个指向有10个整型数数组的指针;一个指向函数的指针,该函数有一个整型参数并返回一个整型数;一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

int a;  int *a;  int **a;  int a[10];  int *a[10];  int (*a)[10];  int (*a)(int);  int (*a[10])(int)

写出如下程序片段的输出

int a[]={1,2,3,4,5};
int *ptr=(int*)(&a+1);
printf("%d%d",*(a+1),*(ptr-1));

答案是2和5,第一个好理解,第二个的确是5,首先a表示一个1行5列的数组,在内存中表示为一个5个元素的序列,int *ptr=(int*)(&a+1)的意思是,指向a数组的第六个元素,尽管这个元素不存在,那么显然,ptr-1所指向的数据就是a数组的第五个元素,5了。如果存在这样的数组int a[2][5]={1,2,3,4,5,6,7,8,9,10},那么显然 int *p=(int*)(&a+1)=a[1],实际上的数据分布还是按照1,2,3,4,5,6,7,8,9,10分布的,所谓的[0]和[1]实际上只是指向其中一个元素的指针。

时刻牢记这样的观点:数组名本身就是指针,再价格&就变成了双指针,这里的双指针就是指二维数组,加1,就是数组整体加一行,ptr指向a的第六个元素。

C++中有了malloc/free,为什么还需要new/delete?

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符,他们都可用于申请动态内存和释放内存。

对于非内部数据类型的对象而言,只用malloc和free无法满足动态对象的要求,对象在创建的同时要自动执行构造函数,对像在消亡之前要自动执行析构函数。由于malloc和free是库函数而不是运算符,不再编译器控制权之内,不能够把执行构造函数和析构函数的任务强加于malloc和free。因此C++语言需要一个能完成动态内存分配和初始化工作的园算符new,以及一个能完成清理与释放内存工作的运算符delete。new/delete不是库函数,而是运算符。

关于auto_ptr的用法

auto_ptr是安全指针,最初的动机是使得下面的代码更加安全

 

void f(){
T* pt(new T);
  /*more code*/
  delete pt;
}

 

如果f()从没有执行delete语句,比如因为过早的return或者是在函数体内部抛出了异常,动态分配的对象将没有被delete,一个典型的内存泄漏。使其安全的一个简单方法是用一个灵巧的类指针对象包容这个指针,在其析构时自动删除此指针auto_ptr<T> pt(new T);现在这个代码将不再泄漏T对象,无论是函数正常结束还是因为异常,因为pt的析构函数总在退栈过程中被调用。

 
 
 
posted on 2012-11-03 19:09  山里狼  阅读(528)  评论(0编辑  收藏  举报