asm, c, c++ are my all
-- Core In Computer
posts - 139,  comments - 123,  trackbacks - 0
     摘要: 在向大家介绍CxImage类库之前,先向大家推荐一个优秀的站点: http://www.codeproject.com 。这个站点有很多值得收藏的文章和源码。本文就是根据此站点相关信息写成。 关于这个类,可以看CodeProject上的文章:http://www.codeproject.com/bi...  阅读全文
posted @ 2006-09-10 21:07 Jerry Cat 阅读(1274) | 评论 (0)编辑 收藏

[c++语言]goto语句中标记(labels)的使用
goto语句可以将程序控制转移到由“标记”所指定的地方,这是地球人都知道的。

关于标记的用法不太知道或没注意的地方:
1。标记不能单独出现,必须后跟一条语句;如果需要一个单独的标记(想不出有这种需要),则在标记后放一条空语句。
2。标记在它所在的函数内有效,不能重新定义。不同函数中可以使用同名标记。

reference:
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/vclang/html/_pluslang_Using_Labels_with_the_goto_Statement.htm

posted @ 2006-09-10 21:01 Jerry Cat 阅读(1539) | 评论 (0)编辑 收藏
犹太谚语:父亲帮助儿子时,两人都笑了;儿子帮助父亲时,两人都哭了。
犹太谚语:一个傻子丢了一颗石头到池塘里,十个聪明人也检不回来!
犹太谚语:财富就在一码之内。
犹太谚语:人生有三种东西会伤人:苦恼、争吵和空的钱包。
犹太谚语:投资自己陌生的行业的人,都将与黄金失之交臂。
posted @ 2006-09-10 20:41 Jerry Cat 阅读(411) | 评论 (0)编辑 收藏

C++面试题

1.是不是一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?

virtual修饰符会被隐形继承的。

private 也被集成,只事派生类没有访问权限而已

virtual可加可不加

子类的空间里有父类的所有变量(static除外)

同一个函数只存在一个实体(inline除外)

子类覆盖它的函数不加virtual ,也能实现多态。

在子类的空间里,有父类的私有变量。私有变量不能直接访问。


--------------------------------------------------------------------------
2.输入一个字符串,将其逆序后输出。(使用C++,不建议用伪码)

#include <iostream>
using namespace std;


void main()
{
  char a[50];memset(a,0,sizeof(a));
  int i=0,j;
  char t;
  cin.getline(a,50,'\n');
  for(i=0,j=strlen(a)-1;i<strlen(a)/2;i++,j--)
  {
   t=a[i];
      a[i]=a[j];
   a[j]=t;
  }
  cout<<a<<endl; 
}

//第二种

string str;
cin>>str;
str.replace;
cout<<str;


--------------------------------------------------------------------------
3.请简单描述Windows内存管理的方法。

内存管理是操作系统中的重要部分,两三句话恐怕谁也说不清楚吧~~
我先说个大概,希望能够抛砖引玉吧

当程序运行时需要从内存中读出这段程序的代码。代码的位置必须在物理内存中才能被运行,由于现在的操作系统中有非常多的程序运行着,内存中不能够完全放下,所以引出了虚拟内存的概念。把哪些不常用的程序片断就放入虚拟内存,当需要用到它的时候在load入主存(物理内存)中。这个就是内存管理所要做的事。内存管理还有另外一件事需要做:计算程序片段在主存中的物理位置,以便CPU调度。

内存管理有块式管理,页式管理,段式和段页式管理。现在常用段页式管理

块式管理:把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程 序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,平均浪费了50%的内存空间,但时易于管理。

页式管理:把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。

段式管理:把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。

段页式管理:结合了段式管理和页式管理的优点。把主存分为若干页,每一页又分为若干段。好处就很明显,不用我多说了吧。

各种内存管理都有它自己的方法来计算出程序片断在主存中的物理地址,其实都很相似。

这只是一个大概而已,不足以说明内存管理的皮毛。无论哪一本操作系统书上都有详细的讲解


--------------------------------------------------------------------------
4.
#include "stdafx.h"
#define SQR(X) X*X

int main(int argc, char* argv[])
{
 int a = 10;
 int k = 2;
 int m = 1;

 a /= SQR(k+m)/SQR(k+m);
 printf("%d\n",a);

 return 0;
}
这道题目的结果是什么啊?

define 只是定义而已,在编择时只是简单代换X*X而已,并不经过算术法则的

a /= (k+m)*(k+m)/(k+m)*(k+m);
=>a /= (k+m)*1*(k+m);
=>a = a/9;
=>a = 1;

--------------------------------------------------------------------------
5.
const 符号常量;
(1)const char *p
(2)char const *p
(3)char * const p
说明上面三种描述的区别;


如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

(1)const char *p

一个指向char类型的const对象指针,p不是常量,我们可以修改p的值,使其指向不同的char,但是不能改变它指向非char对象,如:
const char *p;
char c1='a';
char c2='b';
p=&c1;//ok
p=&c2;//ok
*p=c1;//error

(2)char const *p
(3)char * const p

这两个好象是一样的,此时*p可以修改,而p不能修改。

(4)const char * const p
这种是地址及指向对象都不能修改。

--------------------------------------------------------------------------
6.下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么?
 int n;
 if (n == 10) // 第一种判断方式
 if (10 == n) // 第二种判断方式

如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了=

--------------------------------------------------------------------------
7.下面的代码有什么问题?
void DoSomeThing(...)
{
 char* p;
 ...
 p = malloc(1024);  // 分配1K的空间
 if (NULL == p)
  return;
 ...
 p = realloc(p, 2048); // 空间不够,重新分配到2K
 if (NULL == p)
  return;
 ...
}

A:
p = malloc(1024);     应该写成: p = (char *) malloc(1024);
        没有释放p的空间,造成内存泄漏。


--------------------------------------------------------------------------
8.下面的代码有什么问题?并请给出正确的写法。
void DoSomeThing(char* p)
{
 char str[16];
 int n;
 assert(NULL != p);
 sscanf(p, "%s%d", str, n);
 if (0 == strcmp(str, "something"))
 {
  ...
 }
}

A:
sscanf(p, "%s%d", str, n);   这句该写成: sscanf(p, "%s%d", str, &n);

--------------------------------------------------------------------------
9.下面代码有什么错误?
Void test1()
{
 char string[10];
 char *str1="0123456789";
 strcpy(string, str1);
}

数组越界

--------------------------------------------------------------------------
10.下面代码有什么问题?
Void test2()
{
  char string[10], str1[10];
  for(i=0; i<10;i++)
  {
     str1[i] ='a';
  }
  strcpy(string, str1);
}

数组越界

--------------------------------------------------------------------------
11.下面代码有什么问题?
Void test3(char* str1)
{
  char string[10];
  if(strlen(str1)<=10)
  {
    strcpy(string, str1);
  }
}

==数组越界
==strcpy拷贝的结束标志是查找字符串中的\0 因此如果字符串中没有遇到\0的话 会一直复制,直到遇到\0,上面的123都因此产生越界的情况
 
建议使用 strncpy 和 memcpy

--------------------------------------------------------------------------
12.下面代码有什么问题?

#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;
}

系统会初始化static int变量为0,但该值会一直保存,所谓的不可重入...

--------------------------------------------------------------------------
13.写出运行结果:
{// test1
    char str[] = "world"; cout << sizeof(str) << ": ";
    char *p    = str;     cout << sizeof(p) << ": ";
    char i     = 10;      cout << sizeof(i) << ": ";
    void *pp   = malloc(10);  cout << sizeof(p) << endl;
}

6:4:1:4

--------------------------------------------------------------------------
14.写出运行结果:
{// test2
    union V {
 struct X {
  unsigned char s1:2;
  unsigned char s2:3;
  unsigned char s3:3;
 } x;

 unsigned char c;
    } v;

    v.c = 100;
    printf("%d", v.x.s3);

}

3

--------------------------------------------------------------------------
15.用C++写个程序,如何判断一个操作系统是16位还是32位的?不能用sizeof()函数

A1:
16位的系统下,
int i = 65536;
cout << i; // 输出0;
int i = 65535;
cout << i; // 输出-1;

32位的系统下,
int i = 65536;
cout << i; // 输出65536;
int i = 65535;
cout << i; // 输出65535;

A2:

int a = ~0;
if( a>65536 )
{
    cout<<"32 bit"<<endl;
}
else
{
    cout<<"16 bit"<<endl;
}


--------------------------------------------------------------------------
16.C和C++有什么不同?

从机制上:c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。但是,
c++编写面向对象的程序比c容易

从适用的方向:c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的;  llinux核心大部分是c写的,因为它是系统软件,效率要求极高。

从名称上也可以看出,c++比c多了+,说明c++是c的超集;那为什么不叫c+而叫c++呢,是因为c++比
c来说扩充的东西太多了,所以就在c后面放上两个+;于是就成了c++

C语言是结构化编程语言,C++是面向对象编程语言。
C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。

--------------------------------------------------------------------------
17.在不用第三方参数的情况下,交换两个参数的值
#include <stdio.h>

void main()
{
        int i=60;
        int j=50;
        i=i+j;
        j=i-j;
        i=i-j;
        printf("i=%d\n",i);
        printf("j=%d\n",j);
}

方法二:
i^=j;
j^=i;
i^=j;

方法三:
// 用加减实现,而且不会溢出
a = a+b-(b=a)

--------------------------------------------------------------------------
18.有关位域的面试题(为什么输出的是一个奇怪的字符)

a.t = 'b';效果相当于 a.t= 'b' & 0xf;

'b' --> 01100010
'b' & 0xf -->>00000010
所以输出Ascii码为2的特殊字符


char t:4;就是4bit的字符变量,同样
unsigned short i:8;就是8bit的无符号短整形变量

--------------------------------------------------------------------------
19.int i=10, j=10, k=3; k*=i+j; k最后的值是?

60

--------------------------------------------------------------------------
20.进程间通信的方式有?

进程间通信的方式有 共享内存, 管道 ,Socket ,消息队列 , DDE等

--------------------------------------------------------------------------
21.
struct A
{
char t:4;
char k:4;
unsigned short i:8;
unsigned long m;
}
sizeof(A)=?(不考虑边界对齐)

7

struct CELL             // Declare CELL bit field
{
   unsigned character  : 8;  // 00000000 ????????
   unsigned foreground : 3;  // 00000??? 00000000
   unsigned intensity  : 1;  // 0000?000 00000000
   unsigned background : 3;  // 0???0000 00000000
   unsigned blink      : 1;  // ?0000000 00000000
} screen[25][80];       // Array of bit fields
二、位结构
    位结构是一种特殊的结构, 在需按位访问一个字节或字的多个位时, 位结构
比按位运算符更加方便。
    位结构定义的一般形式为:
     struct位结构名{
          数据类型 变量名: 整型常数;
          数据类型 变量名: 整型常数;
     } 位结构变量;
    其中: 数据类型必须是int(unsigned或signed)。 整型常数必须是非负的整
数, 范围是0~15, 表示二进制位的个数, 即表示有多少位。
    变量名是选择项, 可以不命名, 这样规定是为了排列需要。
    例如: 下面定义了一个位结构。
     struct{
          unsigned incon: 8;  /*incon占用低字节的0~7共8位*/
          unsigned txcolor: 4;/*txcolor占用高字节的0~3位共4位*/
          unsigned bgcolor: 3;/*bgcolor占用高字节的4~6位共3位*/
          unsigned blink: 1;  /*blink占用高字节的第7位*/
     }ch;
    位结构成员的访问与结构成员的访问相同。
    例如: 访问上例位结构中的bgcolor成员可写成:
      ch.bgcolor
 
    注意:
    1. 位结构中的成员可以定义为unsigned, 也可定义为signed,  但当成员长
度为1时, 会被认为是unsigned类型。因为单个位不可能具有符号。
    2. 位结构中的成员不能使用数组和指针, 但位结构变量可以是数组和指针,
如果是指针, 其成员访问方式同结构指针。
    3. 位结构总长度(位数), 是各个位成员定义的位数之和,  可以超过两个字
节。
    4. 位结构成员可以与其它结构成员一起使用。
    例如:
     struct info{
          char name[8];
          int age;
          struct addr address;
          float pay;
          unsigned state: 1;
          unsigned pay: 1;
          }workers;
    上例的结构定义了关于一个工人的信息。其中有两个位结构成员, 每个位结
构成员只有一位, 因此只占一个字节但保存了两个信息, 该字节中第一位表示工
人的状态, 第二位表示工资是否已发放。由此可见使用位结构可以节省存贮空间。


--------------------------------------------------------------------------
22.下面的函数实现在一个固定的数上加上一个数,有什么错误,改正
int add_n(int n)
{
  static int i=100;
  i+=n;
  return i;
}

答:
因为static使得i的值会保留上次的值。
去掉static就可了

--------------------------------------------------------------------------
23.下面的代码有什么问题?
class A
{
public:
  A() { p=this; }
  ~A() { if(p!=NULL) { delete p; p=NULL; } }

  A* p;
};

答:
会引起无限递归

--------------------------------------------------------------------------
24.
union a {
 int a_int1;
 double a_double;
 int a_int2;
};

typedef struct
{
 a a1;
 char y;
} b;

class c
{
 double c_double;
 b b1;
 a a2;

};

输出cout<<sizeof(c)<<endl;的结果?

答:
VC6环境下得出的结果是32

另:
我(sun)在VC6.0+win2k下做过试验:
short - 2
int-4
float-4
double-8
指针-4

sizeof(union),以结构里面size最大的为union的size

解析C语言中的sizeof

一、sizeof的概念 
  sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。 

二、sizeof的使用方法 
  1、用于数据类型 

  sizeof使用形式:sizeof(type) 

  数据类型必须用括号括住。如sizeof(int)。 

  2、用于变量 

  sizeof使用形式:sizeof(var_name)或sizeof var_name 

  变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。 

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

  如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式。 

三、sizeof的结果 
  sizeof操作符的结果类型是size_t,它在头文件

中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。 

  1、若操作数具有类型char、unsigned char或signed char,其结果等于1。 

  ANSI C正式规定字符类型为1字节。 

  2、int、unsigned int 、short int、unsigned short 、long int 、unsigned long 、 float、double、long double类型的sizeof 在ANSI C中没有具体规定,大小依赖于实现,一般可能分别为2、2、2、2、 4、4、4、8、10。 

  3、当操作数是指针时,sizeof依赖于编译器。例如Microsoft C/C++7.0中,near类指针字节数为2,far、huge类指针字节数为4。一般Unix的指针字节数为4。 

  4、当操作数具有数组类型时,其结果是数组的总字节数。 

  5、联合类型操作数的sizeof是其最大字节成员的字节数。结构类型操作数的sizeof是这种类型对象的总字节数,包括任何垫补在内。 

  让我们看如下结构: 

  struct {char b; double x;} a; 

  在某些机器上sizeof(a)=12,而一般sizeof(char)+ sizeof(double)=9。 

  这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如double类型的结构成员x要放在被4整除的地址。 

  6、如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。 

四、sizeof与其他操作符的关系 
  sizeof的优先级为2级,比/、%等3级运算符优先级高。它可以与其他操作符一起组成表达式。如i*sizeof(int);其中i为int类型变量。 

五、sizeof的主要用途 
  1、sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 

  void *malloc(size_t size), 

  size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。 

  2、sizeof的另一个的主要用途是计算数组中元素的个数。例如: 

  void * memset(void * s,int c,sizeof(s))。 

六、建议 
  由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。


=============================================================
本文主要包括二个部分,第一部分重点介绍在VC中,怎么样采用sizeof来求结构的大小,以及容易出现的问题,并给出解决问题的方法,第二部分总结出VC中sizeof的主要用法。

1、 sizeof应用在结构上的情况

请看下面的结构:

struct MyStruct

{

double dda1;

char dda;

int type

};

对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:

sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13

但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?

其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了"对齐"处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。

类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)

Char
偏移量必须为sizeof(char)即1的倍数

int
偏移量必须为sizeof(int)即4的倍数

float
偏移量必须为sizeof(float)即4的倍数

double
偏移量必须为sizeof(double)即8的倍数

Short
偏移量必须为sizeof(short)即2的倍数


各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

下面用前面的例子来说明VC到底怎么样来存放结构的。

struct MyStruct

{

double dda1;

char dda;

int type

};

为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用 sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof (int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。

下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:

struct MyStruct

{

char dda;

double dda1;  

int type

};

这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明)

struct MyStruct

{

  char dda;//偏移量为0,满足对齐方式,dda占用1个字节;

double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8

             //的倍数,需要补足7个字节才能使偏移量变为8(满足对齐

             //方式),因此VC自动填充7个字节,dda1存放在偏移量为8

             //的地址上,它占用8个字节。

int type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍

           //数,满足int的对齐方式,所以不需要VC自动填充,type存

           //放在偏移量为16的地址上,它占用4个字节。

};//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构

   //的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof

   //(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为

   //sizeof(double)=8的倍数。


所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。


VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。

VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数; 

否则必须为n的倍数。下面举例说明其用法。

#pragma pack(push) //保存对齐状态

#pragma pack(4)//设定为4字节对齐

struct test

{

  char m1;

  double m4;

  int  m3;

};

#pragma pack(pop)//恢复对齐状态

以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为 m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。(请读者自己分析)

2、 sizeof用法总结

在VC中,sizeof有着许多的用法,而且很容易引起一些错误。下面根据sizeof后面的参数对sizeof的用法做个总结。

A.  参数为数据类型或者为一般变量。例如sizeof(int),sizeof(long)等等。这种情况要注意的是不同系统系统或者不同编译器得到的结果可能是不同的。例如int类型在16位系统中占2个字节,在32位系统中占4个字节。

B.  参数为数组或指针。下面举例说明.

int a[50];  //sizeof(a)=4*50=200; 求数组所占的空间大小

int *a=new int[50];// sizeof(a)=4; a为一个指针,sizeof(a)是求指针

                   //的大小,在32位系统中,当然是占4个字节。

C.  参数为结构或类。Sizeof应用在类和结构的处理情况是相同的。但有两点需要注意,第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。

第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一

个实例在内存中都有唯一的地址。

下面举例说明,

Class Test{int a;static double c};//sizeof(Test)=4.

Test *s;//sizeof(s)=4,s为一个指针。

Class test1{ };//sizeof(test1)=1;

D.  参数为其他。下面举例说明。

   int func(char s[5]);

   {

     cout<<sizeof(s);//这里将输出4,本来s为一个数组,但由于做为函

                     //数的参数在传递的时候系统处理为一个指针,所

                     //以sizeof(s)实际上为求指针的大小。

     return 1;

}

sizeof(func("1234"))=4//因为func的返回类型为int,所以相当于

                     //求sizeof(int).


以上为sizeof的基本用法,在实际的使用中要注意分析VC的分配变量的分配策略,这样的话可以避免一些错误。


--------------------------------------------------------------------------
25.i最后等于多少?
int i = 1;
int j = i++;
if((i>j++) && (i++ == j)) i+=j;

答:
i = 5

--------------------------------------------------------------------------
26.
unsigned short array[]={1,2,3,4,5,6,7};
int i = 3;
*(array + i) = ?

答:
4

--------------------------------------------------------------------------
27.
class A
{
  virtual void func1();
  void func2();
}
Class B: class A
{
  void func1(){cout << "fun1 in class B" << endl;}
  virtual void func2(){cout << "fun2 in class B" << endl;}
}
A, A中的func1和B中的func2都是虚函数.
B, A中的func1和B中的func2都不是虚函数.
C, A中的func2是虚函数.,B中的func1不是虚函数.
D, A中的func2不是虚函数,B中的func1是虚函数.

答:
A

--------------------------------------------------------------------------
28.
数据库:抽出部门,平均工资,要求按部门的字符串顺序排序,不能含有"human resource"部门,

employee结构如下:employee_id, employee_name, depart_id,depart_name,wage

答:
select depart_name, avg(wage)
from employee
where depart_name <> 'human resource'
group by depart_name
order by depart_name

--------------------------------------------------------------------------
29.
给定如下SQL数据库:Test(num INT(4)) 请用一条SQL语句返回num的最小值,但不许使用统计功能,如MIN,MAX等

答:
select top 1 num
from Test
order by num desc

--------------------------------------------------------------------------
30.
输出下面程序结果。

#include <iostream.h>

class A
{
public:
 virtual void print(void)
 {
    cout<<"A::print()"<<endl;
 }
};
class B:public A
{
public:
 virtual void print(void)
 {
   cout<<"B::print()"<<endl;
 };
};
class C:public B
{
public:
 virtual void print(void)
 {
  cout<<"C::print()"<<endl;
 }
};
void print(A a)
{
   a.print();
}
void main(void)
{
   A a, *pa,*pb,*pc;
   B b;
   C c;
  
   pa=&a;
   pb=&b;
   pc=&c;
  
   a.print();
   b.print();
   c.print();
  
   pa->print();
   pb->print();
   pc->print();
  
   print(a);
   print(b);
   print(c);
}

A:
A::print()
B::print()
C::print()
A::print()
B::print()
C::print()
A::print()
A::print()
A::print()

--------------------------------------------------------------------------
31.
试编写函数判断计算机的字节存储顺序是开序(little endian)还是降序(bigendian)

答:
bool IsBigendian()
{
 unsigned short usData = 0x1122;
 unsigned char  *pucData = (unsigned char*)&usData;

 return (*pucData == 0x22);
}

--------------------------------------------------------------------------
32.简述Critical Section和Mutex的不同点

答:
对几种同步对象的总结
1.Critical Section
A.速度快
B.不能用于不同进程
C.不能进行资源统计(每次只可以有一个线程对共享资源进行存取)

2.Mutex
A.速度慢
B.可用于不同进程
C.不能进行资源统计

3.Semaphore
A.速度慢
B.可用于不同进程
C.可进行资源统计(可以让一个或超过一个线程对共享资源进行存取)

4.Event
A.速度慢
B.可用于不同进程
C.可进行资源统计


--------------------------------------------------------------------------
33.一个数据库中有两个表:
一张表为Customer,含字段ID,Name;
一张表为Order,含字段ID,CustomerID(连向Customer中ID的外键),Revenue;
写出求每个Customer的Revenue总和的SQL语句。

建表
create table customer
(
ID int primary key,Name char(10)
)

go

create table [order]
(
ID int primary key,CustomerID  int foreign key references customer(id) , Revenue float
)

go

--查询
select Customer.ID, sum( isnull([Order].Revenue,0) )
from customer full join [order]
on( [order].customerid=customer.id )
group by customer.id

--------------------------------------------------------------------------
34.请指出下列程序中的错误并且修改
void GetMemory(char *p){
  p=(char *)malloc(100);
}
void Test(void){
  char *str=NULL;
  GetMemory=(str);
  strcpy(str,"hello world");
  printf(str);
}

A:错误--参数的值改变后,不会传回
GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。
strcpy(str, "hello world");将使程序崩溃。

修改如下:
char *GetMemory(){
  char *p=(char *)malloc(100);
  return p;
}
void Test(void){
  char *str=NULL;
  str=GetMemory(){
  strcpy(str,"hello world");
  printf(str);
}

方法二:void GetMemory2(char **p)变为二级指针.
void GetMemory2(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
}

--------------------------------------------------------------------------
35.程序改错
class mml
{
  private:
    static unsigned int x;
  public:
    mml(){ x++; }
    mml(static unsigned int &) {x++;}
    ~mml{x--;}
  pulic:
    virtual mon() {} = 0;
    static unsigned int mmc(){return x;}
    ......                     
 
};
class nnl:public mml
{
  private:
    static unsigned int y;
  public:
    nnl(){ x++; }
    nnl(static unsigned int &) {x++;}
    ~nnl{x--;}
  public:
    virtual mon() {};
     static unsigned int nnc(){return y;}
    ......                  
};

代码片断:
mml* pp = new nnl;
..........
delete pp;


A:
基类的析构函数应该为虚函数
virtual ~mml{x--;}

--------------------------------------------------------------------------
36.101个硬币100真、1假,真假区别在于重量。请用无砝码天平称两次给出真币重还是假币重的结论。

答:
101个先取出2堆,
33,33
第一次称,如果不相等,说明有一堆重或轻
那么把重的那堆拿下来,再放另外35个中的33
如果相等,说明假的重,如果不相等,新放上去的还是重的话,说明假的轻(不可能新放上去的轻)

第一次称,如果相等的话,这66个肯定都是真的,从这66个中取出35个来,与剩下的没称过的35个比
下面就不用说了

方法二:
第3题也可以拿A(50),B(50)比一下,一样的话拿剩下的一个和真的比一下。
如果不一样,就拿其中的一堆。比如A(50)再分成两堆25比一下,一样的话就在
B(50)中,不一样就在A(50)中,结合第一次的结果就知道了。

--------------------------------------------------------------------------
37.static变量和static 函数各有什么特点?

答:
static变量:在程序运行期内一直有效,如果定义在函数外,则在编译单元内可见,如果在函数内,在在定义的block内可见;
static函数:在编译单元内可见;

--------------------------------------------------------------------------
38.用C 写一个输入的整数,倒着输出整数的函数,要求用递归方法 ;

答:
void fun( int a )
{
 printf( "%d", a%10 );
 a /= 10;
 if( a <=0 )return;

 fun( a );
}

--------------------------------------------------------------------------
39.写出程序结果:
void Func(char str[100])
{
  printf("%d\n", sizeof(str));
}

答:
4
分析:
指针长度

--------------------------------------------------------------------------
40.int id[sizeof(unsigned long)];
    这个对吗?为什么??

答:

这个 sizeof是编译时运算符,编译时就确定了
可以看成和机器有关的常量。


本文主要包括二个部分,第一部分重点介绍在VC中,怎么样采用sizeof来求结构的大小,以及容易出现的问题,并给出解决问题的方法,第二部分总结出VC中sizeof的主要用法。

1、 sizeof应用在结构上的情况

请看下面的结构:

struct MyStruct

{

double dda1;

char dda;

int type

};

对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:

sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13

但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?

其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了"对齐"处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。

类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)

Char
偏移量必须为sizeof(char)即1的倍数

int
偏移量必须为sizeof(int)即4的倍数

float
偏移量必须为sizeof(float)即4的倍数

double
偏移量必须为sizeof(double)即8的倍数

Short
偏移量必须为sizeof(short)即2的倍数


各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍?

posted @ 2006-09-10 20:36 Jerry Cat 阅读(159) | 评论 (0)编辑 收藏
/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


读VC++内幕之体悟 - 11
作者: Jerry Cat
时间: 2006/09/10
链接:http://www.cppblog.com/jerysun0818/archive/2006/09/10/12225.html


通过在函数OnInitDialog中加入AfxOleLockControl(someActivexControl.GetClsid());可以把ActiveX控件锁定在内存中,这样,除非程序退出或者调用了AfxOleUnlockControl,ActiveX控件将总在内存中。
posted @ 2006-09-10 20:20 Jerry Cat 阅读(671) | 评论 (3)编辑 收藏
/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


动态链接库小结
作者: Jerry Cat
时间: 2006/09/02
链接: http://www.cppblog.com/jerysun0818/archive/2006/09/02/11974.html

一.库模块:

 (略)
 

二.实现(可以使用Visual C++提供的向导来创建动态链接库和静态链接库)

1>:Win32 Static Library

2>:Win32 Dynamic-Link-Library

3>:MFC AppWizard(dll)

Regular Dll With MFC statically linked

Regular Dll using shared MFC Dll

MFC Extension Dll (using shared MFC DLL)
 

三.Win32 DLL可以直接使用API,但是不可以直接使用MFC

MFC AppWizard可以直接使用MFC

. Regular Dll using shared MFC Dll MFC Extension Dll 两者比较

(表1 )

Regular Dll using shared MFC Dll

MFC Extension Dll (using shared MFC DLL)

1.       能导出 C 风格函数,全局变量

2.       能导出资源

1.       能导出 C 风格函数,全局变量

2.       能导出资源

3.       能导出 C++ 类,成员函数,重载函数

允许静态或者动态链接 MFC 类库

只能动态链接 MFC 类库

它允许客户程序静态或者动态链接

只允许客户程序动态链接

 

 

 

五.调用

1> 隐式链接

隐式链接时,使用 DLL 的程序需要获取以下信息:

 1. 包含了导出函数(类)声明的头文件

 2. 导入库 (lib_

 3. 实际的 DLL

  使用导出函数的源文件需要 #include 1 )的头文件,调用导出函数与其它函数完全一样

  建立可执行程序时,需要导入 LIB 文件,可通过以下方式实现

1.       Project->Setting->Link Object/Library Modules 编辑框中指定 LIB 名字

2.       #pragma comment(lib,”LIB 名字 ”)

例如:我们想使用 opengl 函数,我们可以

1.#include<gl/gl.h>

 #include<gl/glu.h>

2.#pragma comment(lib, “glu32.lib”)

 #pragma comment(lib, “opengl32.lib”)

2> 显式链接

使用 DLL 的程序在运行时通过函数调用来显式加载或卸载 DLL ,并通过函数调用来调用 DLL 的导出函数。

 1. 使用 LoadLibrary 来加载 DLL ,得到模块句柄

 2. 调用 GetProcessAddress 来获取应用程序要调用的导出函数指针

 3. 使用结束后,用 FreeLibrary 来卸载 DLL

  例如:我们想使用 xxx.dll 中的 GetVersion 函数,我们可以

typedef UINT (CALLBACK* LPFNDLLFUN)(DWORD,UINT);

HINSTANCE hDll = LoadLibrary(“xxx.dll”);

if(hDll != NULL)
{
    LPFNDLLFUN lpfnDllFun1 = (LPFNDLLFUN)GetProcess(hDll, “GetVersion”);

    if(lpfnDllFun1)
    {
        // 你的函数调用
    }

   FreeLibrary(hDll);
}

posted @ 2006-09-02 21:37 Jerry Cat 阅读(1090) | 评论 (0)编辑 收藏
[转] C++语录 - 基本教义派

1。如果C函数的参数为空,其原形必须含关键字void,而c++可有可无!
2。函数之所以可以能递归,是因为每个函数被调用时,都会把参数和其他的局部对象复制到一块专用的内存区域,而且同一个函数的每一个执行进程都有一个单独的复制
3。exter "c"
{                              // 这是个连接说明
 #include "mychdr.h"  // 告诉c++ 库 这是个用C编译的函数
}
4。C++采用名字重组技术,给编译器内部的函数标识符重命名,重组的函数名包含了指定函数的返回植类型和参数类型的符号!
5。自动储存的类型修饰符指定一个局部变量是自动的,即每次执行到定义该变量的语句块时,都将为该变量在内存中产生一个新的副本,并对其进行初始化。
6。静态储存类型初始化值只在语句块第一次执行时其作用,在随后的运行过程中,变量将保持语句块上一次执行的值!
7。static 储存类型修饰符使得函数和变量的标识符对于连接到同一个程序的其他源代码文件而言是不可见的。
8。寄存器变量的地址是无法取得的,因为大多数计算机的硬件寄存器都不占内存地址。
9。C++程序中,默认情况下,函数的所有参数都是以传值方式,即把实参的一个副本复制到被调用函数的形参中。
10。在编译器内部,用一个字符数组来表示字符串常量,对字符串的使用实际上就是在引用它的内部地址,所以把字符串给字符型指针实际上就是把常量的地址赋给指针!
11。当函数的形参是指向非const 型变量的指针时,不能用const型变量的地址做形参!
12。任何地址都可以赋值给void 型指针,C++允许任何类型的指针自动转换成 void型!
13。typedef 的作用是给一个已存在的类型起个别名!
14。指针是储存地址的变量,地址是相对较小的数据单元(在32机系统中占四个字节)它储存另一个数据的地址!
15。# undef 预处理指令使得以定义的宏在以后的代码中无效!
16。不带参数或所有参数都有默认值的构造函数叫做默认构造函数,实例化对象数组必须有默认的构造函数;如果类中之少有一个构造函数,编辑器将不在提供默认的构造函数!
17。静态成员只能在类中申明,必须在类外定义,并且静态成员只能存在唯一一个实例,即使没有任何类的实例,静态成员也是已经存在的,类的所有实例都可以使用他,他属于整个类!
18。静态成员函数与一般成员函数的区别:静态成员函数没有this指针,因此他无法访问非静态成员!一般的成员函数隐含this指针
19。初始化和赋值的区别是:初始化是创建一个新的对象,然后用已有的的对象去初始化,赋值是两个都存在的对象!
20。常量数据成员和引用数据成员必须使用构造函数的参数初始化表来初始化,而不能赋值!
21。对象被申明为常量,那么该对象就不可以调用类中任何非常量型的成员函数。
22。mutable 修饰符申明的数据成员,这样一个常量型的成员函数就可以修改它的值!
23。class member以他们在class 内的申明次序来初始化和他们在member initialization list 中出现的次序无关!
24。string &operation =(const string & rhs);// 意思是将一个常 string  指定给另一个string 
25。string &operator =(const char *rhs);//意思是将一个常 char * 指定给一个string 
26。避免传回内部数据的handls;
27。public继承是一种( isa),所以任何事情只要对base class  而言是真,就一定对其 derived class  为真。
28。纯虚函数的两个接口,他们必须被任何“继承了他们”的子类重新申明,他们在抽象类中通常没有定义!
29。纯虚函数的申明主要是为了让derived class 继承函数的接口;
         虚函数的申明主要是为了让derived class 继承函数的接口及其缺省行为
        成员函数(非虚)的申明主要是为了让derived class 继承函数的实现,表示在 derived  class 中希望不要有不同的行为,即不要重新定义继承而来的非虚成员函数!
30。绝对不要重新定义继承而来的参数值,因为虚拟函数系动态绑定,而缺省参数却是静态绑定的!
31。对象的静态型别是程序申明它时所采用的型别。对象的动态型别是指对象目前所代表的型别,表现出一个对象的行为模式!
40:getlin(s,bufsize)表示读取bufsize-1个字符给s如果遇到换行符就停下来,添加一个空字节,丢弃换行符!
41。编辑器将空白字符默认为分隔符。
42。不允许重载系统中预定义的操作,因此重载操作中至少有个操作数是用户自定义类型!
43。全局const申明默认情况下是内部连接,而在c 中他们是外部连接,如果希望一个const 对象具有外部连接特性,就必须使用关键字“extern”
44.除了寄存器变量以外,程序中的所有对象都存在内存中的。
45。在c++中给void型指针赋值时,不用强制类型转换是一种错误!
posted @ 2006-09-02 19:57 Jerry Cat 阅读(379) | 评论 (0)编辑 收藏
[转]用SIMD指令优化程序之抛砖引玉

小谈
CPU 缓存体系

  现在的 CPU 依旧采用冯诺伊曼体系,喜欢像傻子一样从头执行到尾,中途没有任何的跳转停顿等待。可是现实情况是,大部分程序里面还是少不了 IF ELSE 之类的判断,循环就更加得多了。如何优化循环大家可以自己琢磨,其实不难,可以参考一下《高质量 C\C++ 编程指南》

  现在 CPU 上都有 Level 1 指令缓存(又叫做 L1 Trace )与 Level 1 数据缓存( L1 Data Cache )。 PMMX P2 P3 为二者都准备了 16kb ,我的 P4 Northwood (以下简称 P4NW )有 8kbL1 数据缓存和 12kb 指令缓存。 CPU 读取 L1 Data Cache 中的数据只需要 1 个时钟周期,速度非常快,应该是仅次于寄存器了。数据缓存是由 256 或者 512 32bytes 组成的,也就是 32bytes 对齐的,而 P4NW 64bytes 字节对齐的,并行 4 路,总共 128 行。当你处理的数据没有载入缓存的时候, CPU 将从内存读取缓存行大小的数据,所以缓存行总是对齐到能被 32 整除的物理地址。 CPU L1 数据缓存中的数据进行操作是最快速的。所以推荐内存地址最起码是 32byte 对齐的。目前编译器在这个地方的优化已经非常好了,一般都是 4byte 对齐,当然也都是 32 对齐的。在后面你将会看到, SSE2 要求数据是 16 字节对齐的。

    缓存类似一个 C++ set 容器,但是不能赋值到一个任意的内存地址。每行本身都有 1 7bit 大小的关联值( set value )要和目标内存地址的 5 11 位对应( 0-4 位已经忽略了),也可以理解为,关联值是内存段地址的一部分。 PPro 中,有 128 个关联值对应到 2 行,所以最多可以为任意的内存单元准备 2 个缓存行。 PMMX P2 P3 P4NW 4 个。由于内存是分段的,所以说 CPU 只能为, 5-11 位地址相同的内存准备 2 或者 4 个不同的缓存行。如何为两个内存地址赋予相同的关联值呢?把 2 个地址的低 5bit 去掉,这样就能被 32 整除了。如果这 2 个截断了的地址都是 4096 1000H )的倍数,那么这两个地址就有了相同的关联值。

    让我们用汇编加深一下印象,假设 ESI 中是 32 对齐的地址。

                                          AGAIN:  MOV  EAX,  [ESI]

MOV  EBX,  [ESI+13*4096+4]

MOV  ECX,  [ESI+20*4096+28]

DEC   EDX

JNZ   AGAIN

   Oh Year ,这里 3 个地址都有相同的关联值,而且地址跨度都超过了数据缓存的大小,可这个循环在 PPro 上效率会相当低。当你想读取 ECX 的值的时候,将没有空闲的缓存行了 —— 因为共享一个关联值,而且 2 行已经被使用了。此时 CPU 将腾出最近使用的 2 个缓存行,一个已经被 EAX 使用。然后 CPU 把这个缓存行用 [ESI+20*4096] [ESI+20*4096+31] 的内存数据填充,然后从缓存中读取 ECX 。听起来好象相当的烦琐。更加糟糕的是,当又需要读取 EAX 的时候,还需要重复上述的过程,需要对内存缓存来回操作,效率相当的低,甚至不如不用缓存。可是,如果我们把第三行改成:

MOV  ECX,  [ESI+20*4096+32]

  哦,不好,看起来,我们的地址超过了 32 ,不能被整除了。可是这样有了不同的关联值,也就意味着有了 1 个新行,不再共享可怜的 2 个行。这样一来,对三个寄存器的操作就不需要反复的用 2 个缓存行进行调度了,各有一个了。嘿嘿,这次只需要 3 个时钟周期了,而上一个要 60 个周期。这是在 PPro 上的,在后来的 CPU 中都是 4 路的,也就不存在上面的问题了。搞笑的是, Intel 的文档却错误的说 P2 的缓存是 2 路的。虽然说很少人在用那么古老的 CPU ,可是其中的道理大家应该明白。

  可是判断要访问的部分数据是否有相同的关联值,也就是关于缓存是否能够命中的问题,是相当困难的,汇编还好,用高等级语言编译过的程序鬼知道是否对缓存做过优化呢。所以么,推荐,在程序的核心部分,对性能要求最高的部分,先对齐数据,然后确保使用的单个数据块不要超过缓存大小, 2 个数据块,单个不要超过缓存大小的一半(仔细想想为什么,因为关联值的问题,可以缓存分为两部分处理两块)。可是大部分情况下,我们都是使用远比数据缓存大的多的结构,以及编译器自己返回的指针,然后为了优化你可能希望把所有频繁使用的变量放到一个连续的数据块中以充分利用缓存。我们可以这样做,把静态变量数值拷贝到栈中的局部变量中,等子函数或者循环结束后再拷贝回来。这样一来就相当于把静态变量放入了连续的地址空间中去。

当读取的数据不在 L1 Cache 内时, CPU 将要从 L2 Cache 读取 L1 缓存行大小的数据到 L1 里去,大概需要 200ns 的时间(也就是 100Mhz 系统的 20 个时钟周期),但是直到你能够使用这些数据前,又需要有 50-100ns 的延迟。最糟糕的是,如果数据也不在 L2 Cache 中,那么就只能从最慢速的内存里读取了,内存的龟速哪能和全速的缓存相比。

好了,关于缓存的知识可以就此打住了,下面开始讲如何优化缓存。无非就是 3 种方法,硬件预取( Prefetch )、软件预取、使用缓存指令。关于预取的注意事项主要有这些:

<!--[if !supportLists]--> 1、  <!--[endif]--> 合理安排内存的数据,使用块结构,提高缓存命中率。

<!--[if !supportLists]--> 2、  <!--[endif]--> 使用编译器提供的预取指令。比如ICC中的_mm_prefetch _mm_stream,甚至_mm_load等比较“传统”的指令。

<!--[if !supportLists]--> 3、  <!--[endif]--> 尽可能少的使用全局的变量或者指针。

<!--[if !supportLists]--> 4、  <!--[endif]--> 程序尽可能少的进行判断跳转循环。

<!--[if !supportLists]--> 5、  <!--[endif]--> 使用const标记,不要在代码中混合register声明。

不过要提醒一句,真正提高程序效率的方法不是那种,从头到尾由于外科手术般的解剖,一个一个地方的优化,请抓住程序最核心的部分进行优化,记住 80-20 规则。

 

使用 SIMD

先复习一下对齐指令, __declspec(aliagn(#)) # 替换为字节数。比如想声明一个 16 字结对齐的浮点数组, __declspec(aliagn(16)) float Array[128] 。需要注意的是,最好充分了解你 CPU 的类型,支持哪些指令集。 SIMD 主要使用在需要同时操作大量数据的工作领域,比如 3D 图形处理(游戏),物理建模( CAD ),加密,以及科学计算领域。据我所知,目前 GPGPU 也是使用 SIMD 的代表之一。

MMX

主要特性: 57 条指令, 64bit FP 寄存器 MM0-MM7 ,对齐到 8 80bit FP 寄存器 ST0-ST7 。需要数据 8 字节对齐,也就是使用 Packed 数字。

PS :这里冒出了一个问题,为什么 Intel 要把 MMX 的寄存器和 FPU 的寄存器混合起来使用呢?因为这里牵涉到一个 FPU 状态切换问题,后面会提到,当你在一段代码中又要用到 MMX 指令又要用到传统的 FPU 指令,那么需要保存 FPU 状态,或者退出 MMX 。可是这种操作对于 FPU 来说非常昂贵,而且对于多任务操作系统来说,近乎于不可能完成的任务 —— 同时有许多程序,有些需要 MMX ,有些不需要,而正确地进行调度会变得非常困难。所以 Intel 将保存状态的工作完全交给了 CPU 自己,软件人员无须作太多这方面的工作,这样一来,就向前向后兼容了多任务操作系统,比如 Windows Linux 。后来随着操作系统和 CPU 的不断升级,操作系统开发人员发布了一个补丁包,就可以让操作系统使用新的寄存器。这时人们都发现 Intel 的这种做法是相当短视的,这可以当作一个重大的失误。后来 Intel 通过引入了新的浮点指令集,这时才加入 XMM 寄存器。可造成这段故事的原因却根本不是技术问题,保证兼容性也是一个方面,总之真的说不清楚。你只要记得无法同时使用 MMX FPU 就可以了, CPU 要进行模式切换。

SSE1

主要特性: 128bit FP 寄存器 XMM0-XMM7 。增加了数据预取指令。额外的 64bit 整数支持。支持同时处理 4 个单精度浮点数,也就是 C\C++ 里的 float

适用范围:多媒体信号处理

SSE2

主要特性: 128bit FP 寄存器支持处理同时处理 2 个双精度 double 浮点数,以及 16byte 8word 4dword 2quadword 整数。

适用范围: 3D 处理 语音识别 视频编码解码

SSE3

主要特性:增加支持非对称 asymmetric 和水平 horizontal 计算的 SIMD 指令。为 SIMD 提供了一条特殊的寄存器 load 指令。线程同步指令。

适用范围:科学计算 多线程程序

手头工具

1 、选择一个合适的编译器,推荐用 Intel C++ Compiler (以下简称 ICC ),以及 Visual Studio .NET 2003 及以上 IDE 附带的 C++ 编译器。同时, Microsoft C++ Compiler 也支持 AMD 3DNow GCC C++ Compiler 没有测试。

2 Intel 以及 AMD 的汇编指令集手册。这个是必需的,强烈建议每个C++ Coder人手准备一份。

  所有的都用 C++ 混合变成的方式实现

使用范例:

向量乘法在 3D 处理中非常非常多,多半用于计算单位矢量的夹角。

我们先定义一个顶点结构。

__declspec(align( 16 ))  struct  Vertex{
    
float
 x,y,z,w;
};
    16字节对齐的结构,其实本身也是16字节的东西。如果没有对齐,运行时会报错。

w是其次坐标系的参数,处理向量的时候不需要用到。我的函数是这样的:

float  Dot(Vertex *  v1,Vertex *  v2)
{
    Vertex tmp;
    __asm{
        MOV EAX,[v1];
        MOVAPS XMM0,[EAX];
        MOV EAX,[v2];
        MOVAPS XMM1,[EAX];
        MULPS XMM0,XMM1;
        MOVAPS tmp,XMM0;
    };
    
return  tmp.x  +  tmp.y  +
 tmp.z;
};

    VC中反汇编之:
 1 float Dot(Vertex* v1,Vertex*  v2)
 2 
{
 3 
0041C690  push        ebx  
 4 
0041C691  mov         ebx,esp 
 5 0041C693  sub         esp,8
 
 6 
0041C696  and         esp,0FFFFFFF0h 
 7 0041C699  add         esp,4
 
 8 
0041C69C  push        ebp  
 9 0041C69D  mov         ebp,dword ptr [ebx+4

10 0041C6A0  mov         dword ptr [esp+4
],ebp 
11 
0041C6A4  mov         ebp,esp 
12 
0041C6A6  sub         esp,0E8h 
13 
0041C6AC  push        esi  
14 
0041C6AD  push        edi  
15 0041C6AE  lea         edi,[ebp-
0E8h] 
16 
0041C6B4  mov         ecx,3Ah 
17 
0041C6B9  mov         eax,0CCCCCCCCh 
18 
0041C6BE  rep stos    dword ptr [edi] 
19 
    Vertex tmp;
20 
    __asm{
21 
        MOV EAX,[v1];
22 
0041C6C0  mov         eax,dword ptr [v1] 
23 
        MOVAPS XMM0,[EAX];
24 
0041C6C3  movaps      xmm0,xmmword ptr [eax] 
25 
        MOV EAX,[v2];
26 
0041C6C6  mov         eax,dword ptr [v2] 
27 
        MOVAPS XMM1,[EAX];
28 
0041C6C9  movaps      xmm1,xmmword ptr [eax] 
29 
        MULPS XMM0,XMM1;
30 
0041C6CC  mulps       xmm0,xmm1 
31 
        MOVAPS tmp,XMM0;
32 
0041C6CF  movaps      xmmword ptr [tmp],xmm0 
33 
    };
34     return tmp.x + tmp.y +
 tmp.z;
35 
0041C6D3  fld         dword ptr [tmp] 
36 0041C6D6  fadd        dword ptr [ebp-
1Ch] 
37 0041C6D9  fadd        dword ptr [ebp-
18h] 
38 };
    前面都是保护现场入Stack的代码,没有必要管。我之所以这样,在Stack中声明了一个零时变量返回之,是为了减少代码的行数。有兴趣地可以参考本文后面引用资料中的Intel范例,代码多的多,功能却一样。这样就可以利用SIMD计算点乘了。图示:
    这种顶点格式称为AoS(Array of structure),这种结构的好处是,能够和现有的程序结构,比如D3D中的FVF顶点格式,和GL中的顶点格式。但是,由于许多情况下,并没有使用第四各浮点数,这就让SIMD指令浪费了25%的性能。于是有了SoA格式,让我们重新来过。
    我借用了一下上面一个结构的指令,还是没有用_mm_128格式,让大家看得清楚一些:
__declspec(align( 16 ))  struct  Vertex_soa{
     
float  x[ 4 ],y[ 4 ],z[ 4 ],w[ 4
];
};
    依旧16字节对齐。计算函数如下:
 1 void Dot(Vertex_soa* v1,Vertex* v2,float*  result)
 2 
{
 3 
    Vertex tmp1,tmp2;
 4 
    __asm{
 5 
        MOV ECX,v1;
 6 
        MOV EDX,v2;
 7 

 8          MOVAPS XMM7,[ECX];
 9         MOVAPS XMM6,[ECX+16
];
10         MOVAPS XMM5,[ECX+32
];
11         MOVAPS XMM4,[ECX+48
];
12 
        MOVAPS XMM0,XMM7;
13 
        UNPCKLPS XMM7,XMM6;
14 
        MOVLPS [EDX],XMM7;
15         MOVHPS [EDX+16
],XMM7;
16 
        UNPCKHPS XMM0,XMM6;
17         MOVLPS [EDX+32
],XMM0;
18         MOVHPS [EDX+48
],XMM0;
19 

20          MOVAPS XMM0,XMM5;
21 
        UNPCKLPS XMM5,XMM4;
22 
        UNPCKHPS XMM0,XMM4;
23         MOVLPS [EDX+8
],XMM5;
24         MOVHPS [EDX+24
],XMM5;
25         MOVLPS [EDX+40
],XMM0;
26         MOVHPS [EDX+56
],XMM0;
27 

28          MOVAPS XMM3,[EDX];
29         MOVAPS XMM2,[EDX+16
];
30         MOVAPS XMM1,[EDX+32
];
31         MOVAPS XMM0,[EDX+48
];
32 

33          MULPS XMM3,XMM2;
34 
        MULPS XMM1,XMM0;
35 
        MOVAPS tmp2,XMM1;
36 
        MOVAPS tmp1,XMM3;
37 
    };
38     result[0= tmp1.x + tmp1.y +
 tmp1.z;
39     result[1= tmp2.x + tmp2.y +
 tmp2.z;
40 };
    Oh Year,就是这样了,同时计算了1对乘法。我在代码中借用了一下前面的顶点结构,这样方便一些。至于SOA格式,请看前面的声明。很多代码都是转换Stack中的内存格式,转换成AOS格式,这样才能使用SIMD指令计算。

    通过上面的演示,想必大家已经对SIMD有了个直观地认识,其实在自己的代码中加入这些是非常方便与容易的。虽然说现在的CPU性能已经提高了许多,性能也强了许多,可是在诸多对性能要求高的地方,还是非常烤烟程序员的水平的。
posted @ 2006-08-24 21:00 Jerry Cat 阅读(608) | 评论 (0)编辑 收藏
     摘要: 用文档序列化来保存打开文件[理论联系实际] 文档与序列化 ...  阅读全文
posted @ 2006-08-17 00:38 Jerry Cat 阅读(872) | 评论 (1)编辑 收藏
/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


读VC++内幕之体悟 - 10

作者: Jerry Cat
时间: 2006/08/05
链接:
http://www.cppblog.com/jerysun0818/archive/2006/08/05/10866.html

10.扩展DLL支持C++接口,并要求客户程序被动态连接到相同版本的MFC库,该库RELEASE版本对应文件为mfc42.dll。正规DLL可以导出C风格的函数,却不能导出C++类。
posted @ 2006-08-05 17:56 Jerry Cat 阅读(561) | 评论 (0)编辑 收藏
仅列出标题
共14页: 1 2 3 4 5 6 7 8 9 Last 

<2006年6月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

常用链接

留言簿(7)

随笔档案

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜