博文

强制类型转换的错误(2012-03-02 15:09:00)

摘要:
今天遇到一个虚拟继承引起的类型转换错误,网上查到相关资料记录如下:
class VirtualBase { public: virtual class Derived* asDerived() = 0; }; class Derived : virtual public VirtualBase { public: virtual Derived* asDerived(); }; Derived* Derived::asDerived() { return this; } void main() { Derived d; Derived* dp = 0; VirtualBase* vp = (VirtualBase*)&d; dp = (Derived*)vp; // ERROR! Cast from virtual base class pointer   dp = vp->asDerived(); // OK! Cast in function asDerived }
   Virtual base classes give rise to other type conversion problems. It is possible to convert a pointer, to an instance of a class which has a virtual base class, to a pointer to an object of that virtual base class. The opposite conversion is not allowed, i.e. the type conversion is not reversible. For this reason, we do not recommend the conversion of a......

阅读全文(1644) | 评论:0

函数的调用约定(2010-12-10 12:07:00)

摘要: Using Win32 calling conventions 原文在这里:http://www.unixwiz.net/techtips/win32-callconv.html#decor 下面只是为了防止上面网页打不开而做的简单拷贝,文字未经排版,看起来很不舒服。请尽量使用以上链接查看   When writing code for the Win32 platform, most developers don't pay attention to selecting a "calling convention", and in most cases it doesn't really matter much. But as systems get larger and split into more modules (especially when third-party modules are to be included), this becomes something that cannot really be ignored.   In this Tech Tip, we discuss what the MSVC calling conventions are, why to choose one over the other, and "big system" considerations that may not be obvious.
Calling Conventions Traditionally, C function calls are made with the caller pushing some parameters onto the stack, calling the function, and then popping the stack to clean up those pushed arguments. /* example of __cdecl */ push arg1 push arg2 push arg3 call function add sp,12 // effectively "p......

阅读全文(4092) | 评论:2

字符串内部查找函数(2009-01-20 10:36:00)

摘要:char* strchr( const char* str, int chr );
在str中查找第一次出现的chr,没找到则返回0 char* strrchr( const char* str, int chr );
在str中查找最后一次出现的chr,没找到则返回0 char* strstr( const char* str, const char* substr );
在str中查找第一次出现的substr,没找到则返回0 size_t strspn( const char* str, const char* chrset );
在str中查找第一个不包含在chrset中的字符的索引,即查找从str第一个字符开始的只包含chrset中字符的最长字符串字符数目
 strspn( "cabbage", "abc" ) == 5;
 strspn( "cabbage", "dc" ) == 1;
 strspn( "cabbage", "d" ) == 0;
 strspn("cabbage", "a") == 0;
 strspn( "cabbage", "" ) == 0;
 strspn( "cabbage", "\0" ) == 0;
 strspn("cabbage", "abceg" ) == 7;//返回str长度 char* strspnp( const char* str, const char* chrset );
和strspn功能一样,只是返回指针
const char* str = "cabbage"
strspnp( str, "abc" ) == str+5;
strspnp( str, "dc" ) == str+1;
strspnp( str, "d" ) == str+0;
strspnp( str, "abceg" ) == 0; // 和strspn不一样 size_t strcspn( const char* str, const char* chrset );
在str中查找第一次出现在chrset中的字符位置,没找到则返回str结尾的'......

阅读全文(5020) | 评论:0

虚函数原理的一个小例子(2008-11-20 18:12:00)

摘要:class A
{
    virtual void fun(){printf("this is a's private fun\n");}
};

class B
{
public:
    virtual void fun(){printf("this is b's public fun\n");}
};

int _tmain(int argc, _TCHAR* argv[])
{
    A* pa = new A;
    B* pb=(B*)pa;
    pb->fun();
    return 0;
} 对于pb->fun()编译器首先检查pb的类型为B,于是到B的定义里找fun发现是virtual。于是做了如下转换: typedef void (*pFoo)(); pFoo(*(int*)(*((int*)pb)))(); 这样起始调用的是A中的fun。 如果B的fun为虚,而A的fun非虚,编译器同样会做上面的转换,当然运行的时候就报内存访问违规了。原因很简单,A中并没有虚函数,自然不会有虚表。 如果B的fun是非virtual,编译器就编译器就直接调用了B中的fun: B::fun(); 这种调用最简单,因为不涉及虚函数,自然不会做类似上面的转换。......

阅读全文(3046) | 评论:0

如何取得结构变量的偏移地址(2008-11-19 10:42:00)

摘要:struct Foo{    int a;    char b;    short c; }; 如果我想观察编译器为了达到内存对齐的要求都在Foo的每个变量后加了多少padding字节,有什么办法? 我们知道编译器在定位一个成员变量的时候利用了2个信息: 1. 类对象的地址 2.将要访问的成员变量相对于类起始地址的偏移地址 这样编译器就可以通过 类对象的地址+成员变量的偏移地址 的简单计算得到成员变量在内存中的真正地址。 回到我们先头的问题,其实我们就是想知道第2个信息,即:将要访问的成员变量相对于类起始地址的偏移地址。 偏移地址的计算是编译器在编译该类的时候就已经确定的,因此应该有办法在编译期就得到这个值。考虑一般的调用情况: Foo foo; Foo *pfoo = &foo; pfoo->b;  //这一句编译器就将计算a的真正地址=pfoo的值+b的偏移地址。 那么如果pfoo的值为零,我们岂不是正好得到a的偏移地址吗?也就是 ((Foo*)0)->b我们能通过这样的形式获得我们想要的偏移地址吗? OK,运行一下... ... 发现运行期报了一个内存访问违规的异常(VC8),正常情况下,如果你的编译器没有什么问题,你都应该得到这个异常。仔细观察这个异常: Unhandled exception at 0x00411a06 in sss.exe: 0xC0000005: Access violation reading location 0x00000004. 啊?0x00000004不就是我们想要的偏移地址吗? 恍惚一下~~  发现自己犯了一个弱智的错误:我们不是要访问b的值,我们只是想得到他的偏移地址而已。把他的偏移地址当做他的内存地址当然会出错啦~~毕竟你的pfoo什么都不是(空指针)。 于是乎强制转换成地址:(int)((void*)(&(((Foo*)0)->c))) 这次输出正确了,就是4. 其实在stddef.h中有更加完整的定义,就是offsetof宏: #define offsetof(s,m)   (size_t)&reinterpret_ca......

阅读全文(4429) | 评论:0

浮点数的存储格式(2008-06-27 11:59:00)

摘要:                                                   基于IEEE 754的浮点数存储格式            IEEE(Institute of Electrical and Electronics Engineers,电子电气工程师协会)在I985年制定的IEEE 754(IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Std 754-1985 )二进制浮点运算规范,是浮点运算部件事实上的工业标准。 1  浮点数     在计算机系统的发展过程中,曾经提出过多种方法表示实数,但是到目前为止使用最广泛的是浮点表示法。相对于定点数而言,浮点数利用指数使小数点的位置可以根据需要而上下浮动,从而可以灵活地表达更大范围的实数。     浮点数表示法利用科学计数法来表达实数。通常,将浮点数表示为 ± d.dd…d ×βe,其中d.dd… d 称为有效数字(significand),它具有 p 个数字(称p位有效数字精度),β为基数(Base),e为指数(Exponent),±表示实数的正负[1,2]。更精确地,± d0.d1d2…dp-1 × βe, 表示以下数  ±(d0+d1β-1+… +dp-1β-(p-1))βe,(0≤di<β)。     对实数的浮点表示仅作如上的规定是不够的,因为同一实数的浮点表示还不是唯一......

阅读全文(9015) | 评论:0

另类的成员函数调用(2008-06-17 11:38:00)

摘要:对于给定的一个类: class Foo{
public:
   void CALLINGCONVENTION showMe( int _d ){   //__cdecl or __stdcall is needed.
          cout << "Foo::showMe  _d = " << _d << endl;
   }
}; 通常我们会通过下面的方式进行调用该类中的成员方法: Foo foo; foo.showMe(100); 或者通过成员函数指针的形式: void (Foo::*pfn)(int) = &Foo::showMe; (foo.*pfn)(100); 我总是认为这中使用成员函数指针的调用方式(或是语法)感到很奇怪,不过它毕竟是标准的并且能够为C++编译器认可的调用方式。几乎在所有编译器都不允许将成员函数的地址直接赋给其他类型的变量(如DWORD等,即使使用reinterpret_cast也无济于事)例如:而只能将其赋给给与该成员函数类型声明(包括所使用的调用约定,返回值,函数参数)完全相同的变量。因为成员函数的声明中都有一个隐藏的this指针,该指针会通过ECX或栈的方式传递到成员函数中,为了成员函数被安全调用,编译器禁止此类型的转换也在情理当中。但有时我们为了实现特殊的目的需要将成员函数的地址直接赋给一个DWORD变量(或其他任何能够保存一个指针值的变量,通常只要是一个32位的变量即可),我们可以通过下面两种方法来实现:下面的这几种试图将一个成员函数的地址保存到一个DWORD中都将被编译器认为是语法错误:DWORD dwFooAddrPtr= 0; dwFooAddrPtr = (DWORD) &Foo::showMe; /* Error C2440 */ dwFooAddrPtr = reinterpret_cast<DWORD> (&Foo::showMe); /* Error C2440 */因为成员函数的声明中都有一个隐藏的this指针,该指针会通过E......

阅读全文(4516) | 评论:2

“条件递进式编程”技巧两例(2008-06-03 17:02:00)

摘要:转自:http://blog.csdn.net/happydeer/archive/2004/08/06/66743.aspx#785737 什么是“条件递进式编程”?呵呵,这是我自己起的名字。所谓条件递进式编程,就是指需要一系列相关函数组合执行的情况下,后续函数执行的必要性由前面所有函数执行是否成功来决定。比如说,有一系列函数如下DoSomething1、DoSomething2、DoSomething3......DoSomethingN;DoSomething2函数执行的前提是DoSomething1函数必须执行成功,DoSomething3函数执行的前提是DoSomething1和DoSomething2都执行成功,DoSomethingN函数执行的前提是DoSomething1、DoSomething2、DoSomething3......DoSomething(N-1)都执行成功。 你会如何来写这段程序?不假思索,你可能会如下编写: BOOL DoSomething()
{
 if (DoSomething1())
 {
  if (DoSomething2())
  {
   if (DoSomething3())
   {
    // ...
    return TRUE;
   }
  }
 }
 return FALSE;
} 这种层层嵌套的结构,逻辑上并没有错,但代码的可读性就差了一点。如果系列函数的数量很多(即上述N的值很大),那么整个代码结构的宽度将变得很长,代码的可读性将进一步降低,日后修改代码的时候也很容易出错。 下面,我将给出两种我本人认为很不错的程序结构。这两种程序结构不是我自创的,但我能看到它们智慧的光芒。另外,关于以下两种程序结构,我不想给出过多解释,读者用心慢慢体会吧!
程序结构1: BOOL DoSomething()
{
 BO......

阅读全文(2479) | 评论:0

对受保护成员的一个额外访问控制(2008-05-13 11:42:00)

摘要:对于受保护的成员变量,子类对其的访问有一个额外的限制: Clause 11.5: When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class). 例子: class Base{ protected:      Base(int){}      int i; }; class Derive:public Base{ public:      Derive():Base(100){              //OK           Base b(100);        &nbs......

阅读全文(2758) | 评论:0

Why Java Will Always Be Slower than C++(2008-03-09 22:22:00)

摘要:Why Java Will Always Be Slower than C++
by Dejan Jelovic
 
"Java is high performance. By high performance we mean adequate. By adequate we mean slow." - Mr. Bunny
Anybody that has ever used a non-trivial Java program or has programmed in Java knows that Java is slower than native programs written in C++. This is a fact of life, something that we accept when we use Java.
However, many folks would like to convince us that this is just a temporary condition. Java is not slow by design, they say. Instead, it is slow because......

阅读全文(2733) | 评论:0