asm, c, c++ are my all
-- Core In Computer
posts - 139,  comments - 123,  trackbacks - 0

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


连绵不断的析取流

作者: Jerry Cat
时间: 2006/04/24
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/25/6214.html


/*
精巧的ostream和<<机制, 不仅具有面向对象的多重继承和重载, 且性能不输C的IO库函数

cout, 表示标准输出(standard output)的ostream类对象.cout使我们可以向设备输出或者写数据.
输出主要由重载的析取操作符(<<)来完成, 输入主要由重载的插入操作符(>>)完成:

>>a表示将数据放入a对象中.
<<a表示将a对象中存储的数据拿出到流中, 最终汇入流对象ostream.
*/

e.g.: cout <<"Destroying..." <<i <<endl;

//连绵不断的析取流, 下面是其中ostream成员函数<<析取符函数重载的一个实现
//函数指针_f代表符合"该函数指针参数类型,返回值类型和调用规范类型的"某些C++标准例程比如endl的偏移地址
inline ostream& ostream::operator<<(ostream& (__cdecl * _f)(ostream&))
{                     
    (*_f)(*this); //此处的*this是ostream对象之引用,即当前流对象之引用ostream&
    return *this; //返回当前流对象之引用
}

posted @ 2006-04-25 03:54 Jerry Cat 阅读(509) | 评论 (0)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/

C++对象模型(3) - An Object Distinction
 
作者: Jerry Cat
时间: 2006/04/23
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/24/6114.html


1.3 An Object Distinction
-------------------------
intercede in a and b:在a和b间进行调解
The most common inadvertent mixing of idioms occurs when a concrete instance of a base class, such as

Library_materials thing1;
is used to program some aspect of polymorphism:

// class Book : public Library_materials { ...};
Book book;

// Oops: thing1 is not a Book!
// Rather, book is "sliceD" — thing1就是个只保留book的上半身的残废东西
// thing1 remains a Library_materials

thing1 = book;

// Oops: invokes
// Library_materials::check_in()
thing1.check_in();
rather than a pointer or reference of the base class:

// OK: thing2 now references book, 因为基类和派生类的布局是"基部重合在一起的", 派生类还是超集哩!
// 基类在"上"(低址处), 派生类多出的部分紧接着"连"在下面; 引用(本质上是指针)和指针对这两种数据类型
// 有类似汇编中word ptr 和 dword ptr的关系, 它俩的首址是相同的. 编译器会自动鉴别基类和子类从而调整
// 类似word ptr 和 dword ptr的这种类的"类型寻址"操作
// 而且Scott Meyer说过它们是一种"is a"的关系:"The derived is a base class"
// 向上(基类方向)转换没问题的, 向下转换一般不可 - 简直"无中生有"嘛! 但MFC中对动态类对象CDerived(用
// DECLARE_DYNCREATE宏 和 IMPLEMENT_DYNCREATE宏在程序运行时而非编译动态生成)倒可用DYNAMIC_DOWNCAST
// 宏来完成将指向CBase的指针Downcast成指向它:
// CDerived * pDerived = DYNAMIC_DOWNCAST(CDerived, pBase); //CBase *pBase;
// 原型为DYNAMIC_DOWNCAST( class, pointer )

Library_materials &thing2 = book;//本质是地址,用起来象对象! 对象别名也,从这角度就是对象了嘛^_^

// OK: invokes Book::check_in()
thing2.check_in();

只有指针和引用才能"救多态"!
Although you can manipulate a base class object of an inheritance hierarchy either directly or indirectly, only the indirect manipulation of the object through a pointer or reference supports the polymorphism necessary for OO programming. The definition and use of thing2 in the previous example is a well-behaved instance of the OO paradigm. The definition and use of thing1 falls outside the OO idiom; it reflects a well-behaved instance of the ADT paradigm. Whether the behavior of thing1 is good or bad depends on what the programmer intended. In this example, its behavior is very likely a surprise.

// represent objects: uncertain type
Library_materials *px = retrieve_some_material();
Library_materials &rx = *px;

// represents datum: no surprise
Library_materials dx = *px;
it can never be said with certainty what the actual type of the object is that px or rx addresses. It can only be said that it is either a Library_materials object or a subtype rooted by Library_materials class. dx, however, is and can only be an object of the Library_materials class. Later in this section, I discuss why this behavior, although perhaps unexpected, is well behaved.

Although the polymorphic manipulation of an object requires that the object be accessed either through a pointer or a reference, the manipulation of a pointer or reference in C++ does not in itself necessarily result in polymorphism! For example, consider

// no polymorphism
int *pi;

// no language supported polymorphism
void *pvi;

// ok: class x serves as a base class
x *px;
多态只存在于
In C++, polymorphism exists only within individual public class hierarchies. px, for example, may address either an object of its own type or a type publicly derived from it (not considering ill-behaved casts). Nonpublic derivation and pointers of type void* can be spoken of as polymorphic, but they are without explicit language support; that is, they must be managed by the programmer through explicit casts. (One might say that they are not first-class polymorphic objects.)

The C++ language supports polymorphism in the following ways:
1. Through a set of implicit conversions, such as the conversion of a derived class pointer to a pointer of its public base type:
shape *ps = new circle();

2. Through the virtual function mechanism:
ps->rotate();

3. Through the dynamic_cast and typeid operators:
if ( circle *pc = dynamic_cast< circle* >( ps )) ...//象MFC中DYNAMIC_DOWNCAST和DECLARE_DYNCREATE,
//IMPLEMENT_DYNCREATE, IsKindOf(RUNTIME_CLASS(class))的组合拳

// example for CObject::IsKindOf
/* BOOL IsKindOf( const CRuntimeClass* pClass ) const; */
CAge a(21); // Must use IMPLEMENT_DYNAMIC or IMPLEMENT_SERIAL
ASSERT( a.IsKindOf( RUNTIME_CLASS( CAge ) ) );
ASSERT( a.IsKindOf( RUNTIME_CLASS( CObject ) ) );

// example for RUNTIME_CLASS
/* RUNTIME_CLASS( class_name ) */
Use this macro to get the run-time class structure from the name of a C++ class.

RUNTIME_CLASS returns a pointer to a CRuntimeClass structure for the class specified by class_name. Only CObject-derived classes declared with DECLARE_DYNAMIC, DECLARE_DYNCREATE, or DECLARE_SERIAL will return pointers to a CRuntimeClass structure.

CRuntimeClass* prt = RUNTIME_CLASS( CAge );
ASSERT( lstrcmp( prt->m_lpszClassName, "CAge" )  == 0 );

=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=
The memory requirements to represent a class object in general are the following:

1.) The accumulated size of its nonstatic data members
2.) Plus any padding (between members or on the aggregate boundary itself) due to alignment constraints (or simple efficiency)
3.) Plus any internally generated overhead to support the virtuals

The memory requirement to represent a pointer, [2] however, is a fixed size regardless of the type it addresses. For example, given the following declaration of a ZooAnimal class:

  [2]Or to represent a reference; internally, a reference is generally implemented as a pointer and the object syntax transformed into the indirection required of a pointer.
class ZooAnimal {
public:
   ZooAnimal();
   virtual ~ZooAnimal();

   // ...

   virtual void rotate();
protected:
   int loc;
   String name;
};

ZooAnimal za( "Zoey" );
ZooAnimal *pza = &za;

a likely layout of the class object za and the pointer pza is pictured in Figure 1.4. (I return to the layout of data members in Chapter 3.)

Figure 1.4. Layout of Object and Pointer of Independent Class


layout1.GIF

The Type of a Pointer:
=-=-=-=-=-=-=-=-=-=-=
But how, then, does a pointer to a ZooAnimal differ from, say, a pointer to an integer or a pointer to a template Array instantiated with a String?

ZooAnimal *px;
int *pi
Array< String > *pta;
In terms of memory requirements, there is generally no difference: all three need to be allocated sufficient memory to hold a machine address (usually a machine word). So the difference between pointers to different types rests neither in the representation of the pointer nor in the values (addresses) the pointers may hold. The difference lies in the type of object being addressed. That is, the type of a pointer instructs the compiler as to how to interpret the memory found at a particular address and also just how much memory that interpretation should span:

An integer pointer addressing memory location 1000 on a 32-bit machine spans the address space 1000—1003.

The ZooAnimal pointer, if we presume a conventional 8-byte String (a 4-byte character pointer and an integer to hold the string length), spans the address space 1000—1015.

Hmm. Just out of curiosity, what address space does a void* pointer that holds memory location 1000 span? That's right, we don't know. That's why a pointer of type void* can only hold an address and not actually operate on the object it addresses.

So a cast in general is a kind of compiler directive. In most cases, it does not alter the actual address a pointer contains. Rather, it alters only the interpretation of the size and composition of the memory being addressed.

Adding Polymorphism
=-=-=-=-=-=-=-=-=-=
Now, let's define a Bear as a kind of ZooAnimal. This is done, of course, through public inheritance:

class Bear : public ZooAnimal {
public:
   Bear();
   ~Bear();
   // ...
   void rotate();
   virtual void dance();
   // ...
protected:
   enum Dances { ... };

   Dances dances_known;
   int cell_block;
};

Bear b( "Yogi" );
Bear *pb = &b;
Bear &rb = *pb;
What can we say about the memory requirements of b, pb, and rb? Both the pointer and reference require a single word of storage (4 bytes on a 32-bit processor). The Bear object itself, however, requires 24 bytes (the size of a ZooAnimal [16 bytes] plus the 8 bytes Bear introduces). A likely memory layout is pictured in Figure 1.5.

Figure 1.5. Layout of Object and Pointer of Derived Class

layout2.GIF

Okay, given that our Bear object is situated at memory location 1000, what are the real differences between a Bear and ZooAnimal pointer?

Bear b;
ZooAnimal *pz = &b;
Bear *pb = &b;
Each addresses the same first byte of the Bear object. The difference is that the address span of pb encompasses the entire Bear object, while the span of pz encompasses only the ZooAnimal subobject of Bear.

pz cannot directly access any members other than those present within the ZooAnimal subobject, except through the virtual mechanism:

// illegal: cell_block not a member
// of ZooAnimal, although we ``know''
// pz currently addresses a Bear object
pz->cell_block;
// okay: an explicit downcast
(( Bear* )pz)->cell_block;

// better: but a run-time operation
if ( Bear* pb2 = dynamic_cast< Bear* >( pz ))
   pb2->cell_block;

// ok: cell_block a member of Bear
pb->cell_block;
When we write

pz->rotate();
the type of pz determines the following at compile time:

The fixed, available interface (that is, pz may invoke only the ZooAnimal public interface)

The access level of that interface (for example, rotate() is a public member of ZooAnimal)

The type of the object that pz addresses at each point of execution determines the instance of rotate() invoked. The encapsulation of the type information is maintained not in pz but in the link between the object's vptr and the virtual table the vptr addresses (see Section 4.2 for a full discussion of virtual functions).
So, then, why is it that, given

Bear b;
ZooAnimal za = b;

// ZooAnimal::rotate() invoked
za.rotate();
the instance of rotate() invoked is the ZooAnimal instance and not that of Bear? Moreover, if memberwise initialization copies the values of one object to another, why is za's vptr not addressing Bear's virtual table?

The answer to the second question is that the compiler intercedes in the initialization and assignment of one class object with another. The compiler must ensure that if an object contains one or more vptrs, those vptr values are not initialized or changed by the source object .
子类是基类, 基类非子类. 儿子是老子(生的), 老子非儿子(生的).
The answer to the first question is that za is not (and can never be) a Bear; it is (and can never be anything but) a ZooAnimal. Polymorphism, the potential to be of more than one type, is not physically possible in directly accessed objects. Paradoxically, direct object manipulation is not supported under OO programming. For example, given the following set of definitions:
{
   ZooAnimal za;
   ZooAnimal *pza;

   Bear b;
   Panda *pp = new Panda;

   pza = &b;
}
one possible memory layout is pictured in Figure 1.6.

Figure 1.6. Memory Layout of Sequence of Definitions

layout3.GIF

Assigning pz the address of either za, b, or that contained by pp is obviously not a problem. A pointer and a reference support polymorphism because they do not involve any type-dependent commitment of resources. Rather, all that is altered is the interpretation of the size and composition of the memory they address.

Any attempt to alter the actual size of the object za, however, violates the contracted resource requirements of its definition. Assign the entire Bear object to za and the object overflows its allocated memory. As a result, the executable is, literally, corrupted, although the corruption may not manifest itself as a core dump.

When a base class object is directly initialized or assigned with a derived class object, the derived object is sliced to fit into the available memory resources of the base type. There is nothing of the derived type remaining. Polymorphism is not present, and an observant compiler can resolve an invocation of a virtual function through the object at compile time, thus by-passing the virtual mechanism. This can be a significant performance win if the virtual function is defined as inline.
多态是面向对象OO的实质
To summarize, polymorphism is a powerful design mechanism that allows for the encapsulation of related types behind an abstract public interface, such as our Library_materials hierarchy. The cost is an additional level of indirection, both in terms of memory acquisition and type resolution. C++ supports polymorphism through class pointers and references. This style of programming is called object-oriented.
ADT抽象数据类型是基于对象OB
C++ also supports a concrete ADT style of programming now called object-based (OB)—nonpolymorphic data types, such as a String class. A String class exhibits a nonpolymorphic form of encapsulation; it provides a public interface and private implementation (both of state and algorithm) but does not support type extension. An OB design can be faster and more compact than an equivalent OO design. Faster because all function invocations are resolved at compile time and object construction need not set up the virtual mechanism, and more compact because each class object need not carry the additional overhead traditionally associated with the support of the virtual mechanism. However, an OB design also is less flexible.

posted @ 2006-04-24 03:45 Jerry Cat 阅读(681) | 评论 (0)编辑 收藏

[转]AT&T x86 asm 语法

创建时间:2001-04-09
文章属性:翻译

DJGPP 使用AT&T格式的汇编语法。和一般的intel格式的语法有点不同。主要不同点如下:

AT&T 语法颠倒了源和目的操作数的位置, 目的操作数在源操作数之后。寄存器操作数要有个%的前缀,  立即数操作数要有个$符号的前缀。存

储器操作数的大小取决于操作码的最后一个字符。 它们是b (8-bit), w (16-bit), 和 l (32-bit).
这里有一些例子。 左边部分是intel指令格式,右边是at&t格式。
    movw %bx, %ax        // mov ax, bx
    xorl %eax, %eax        // xor eax, eax
    movw $1, %ax        // mov ax,1
    movb X, %ah        // mov ah, byte ptr X
    movw X, %ax        // mov ax, word ptr X
    movl X, %eax        // mov eax, X
大部分操作指令,at%t和intel都是差不多的,除了这些:
    movsSD             // movsx
    movzSD             // movz

S和D分辨代表源和目的操作数后缀。
    movswl %ax, %ecx    // movsx ecx, ax
    cbtw                // cbw
    cwtl                // cwde
    cwtd                // cwd
    cltd                // cdq
    lcall $S,$O         // call far S:O
    ljmp $S,$O          // jump far S:O
    lret $V             // ret far V
操作嘛前缀不能与他们作用的指令写在同一行。 例如, rep 和stosd应该是两个相互独立的指令, 存储器的情况也有一点不同。通常intel格

式的如下:

section:[base + index*scale + disp]

被写成:

section:disp(base, index, scale)

这里有些例子:

    movl 4(%ebp), %eax                // mov eax, [ebp+4])
    addl (%eax,%eax,4), %ecx          // add ecx, [eax + eax*4])
    movb $4, %fs:(%eax)               // mov fs:eax, 4)
    movl _array(,%eax,4), %eax        // mov eax, [4*eax + array])
    movw _array(%ebx,%eax,4), %cx    // mov cx, [ebx + 4*eax + array])

Jump 指令通常是个短跳转。 可是, 下面这些指令都是只能在一个字节的范围内跳转: jcxz, jecxz, loop, loopz, loope, loopnz 和loopne

。象在线文档所说的那样,一个jcxz foo可以扩展成以下工作:
    jcxz cx_zero
    jmp cx_nonzero
cx_zero:
    jmp foo
cx_nonzero:
文档也注意到了mul和imul指令。 扩展的乘法指令只用一个操作数,例如, imul $ebx, $ebx将不会把结果放入edx:eax。使用imul %ebx中的

单操作数来获得扩展结果。


--------------------------------------------------------------------------------

Inline Asm
我将首先开始inline asm, 因为似乎关于这方面的疑问非常多。这是最基本的语法了, 就象在线帮助信息中描述的:
__asm__(asm statements : outputs : inputs : registers-modified);

这四个字段的含义是:

asm statements - AT&T 的结构, 每新行都是分开的。
outputs - 修饰符一定要用引号引起来, 用逗号分隔
inputs - 修饰符一定要用引号引起来, 用逗号分隔
registers-modified - 名字用逗号分隔
一个小小的例子:
    __asm__("
        pushl %eax\n
        movl $1, %eax\n
        popl %eax"
    );
假如你不用到特别的输入输出变量或者修改任何寄存器的值,一般来说是不会使用到其他的三个字段的,
让我们来分析一下输入变量。

    int i = 0;

    __asm__("
        pushl %%eax\n
        movl %0, %%eax\n
        addl $1, %%eax\n
        movl %%eax, %0\n
        popl %%eax"
    :
    : "g" (i)
    );    // increment i
不要为上面的代码所困扰! 我将尽力来解释它。我们想让输入变量i加1,我们没有任何输出变量, 也没有改变寄存器值(我们保存了eax值)。

因此,第二个和最后一个字段是空的。 因为指定了输入字段, 我们仍需要保留一个空的输出字段, 但是没有最后一个字段, 因为它没被使用

。在两个空冒号之间留下一个新行或者至少一个空格。

下面让我们来看看输入字段。 附加描述符可以修正指令来让你给定的编译器来正确处理这些变量。他们一般被附上双引号。那么这个"g"是用

来做什么的呢?  只要是合法的汇编指令,"g"就让编译器决定该在哪里加载i的值。一般来说,你的大部分输入变量都可以被赋予 "g", 让编

译器决定如何去加载它们 (gcc甚至可以优化它们!)。 其他描述符使用"r" (加载到任何可用的寄存器去), "a" (ax/eax), "b" (bx/ebx),

"c" (cx/ecx), "d" (dx/edx), "D" (di/edi), "S" (si/esi), 等等。

我们将要提到一个在asm代码里面的如%0的输入变量。如果我们有两个输入, 他们会一个是%0一个是%1, 在输入段里按顺序排列 (如下一个例

子)。假如N个输入变量且没有输出变量, 从%0 到%N-1将和输入字段里的变量相对应, 按顺序排列。

如果任何的输入, 输出, 寄存器修改字段被使用, 汇编代码里的寄存器名必须用两个%来代替一个%。对应于第一个没有使用最后三个字段的例

子。

让我们看看两个输入变量且引入了"volatile"的例子:

    int i=0, j=1;
    __asm__ __volatile__("
        pushl %%eax\n
        movl %0, %%eax\n
        addl %1, %%eax\n
        movl %%eax, %0\n
        popl %%eax"
    :
    : "g" (i), "g" (j)
    );    // increment i by j
Okay, 现在我们已经有了两个输入变量了。没问题了, 我们只需要记住%0对应第一个输入变量(在这个例子中是i), %1对应在i后面的列出的j


Oh yeah, 这个volatile到底是什么意思呢? 它防止你的编译器修改你的汇编代码,就是不进行优化(纪录, 删除, 结合,等等优化手段。), 不

改变代码原样来汇编它们。建议一般情况下使用volatile选项。

让我们来看看输出字段:

    int i=0;
    __asm__ __volatile__("
        pushl %%eax\n
        movl $1, %%eax\n
        movl %%eax, %0\n
        popl %%eax"
    : "=g" (i)
    );    // assign 1 to i
这看起来非常象我们前面提到的输入字段的例子; 确实也没有很大的不同。所有的输出修饰符前面都应该加上=字符,他们同样在汇编代码里

面用%0到%N-1来表示, 在输出字段按顺序排列。你一定会问如果同时有输入和输出字段会怎么排序的呢? 好,下面一个例子就是让大家知道

如何同时处理输入输出字段的。
    int i=0, j=1, k=0;
    __asm__ __volatile__("
        pushl %%eax\n
        movl %1, %%eax\n
        addl %2, %%eax\n
        movl %%eax, %0\n
        popl %%eax"
    : "=g" (k)
    : "g" (i), "g" (j)
    );    // k = i + j
Okay, 唯一个不清楚的地方就是汇编代码中的变量的个数。我马上来解释一下。
当同时使用输入字段和输出字段的时候:

%0 ... %K 是输出变量

%K+1 ... %N 是输入变量

在我们的例子中, %0 对应k, %1 对应i, %2对应j。很简单,是吧?

到现在为止我们都没有使用最后一个字段(registers-modified)。如果我们要在我们的汇编代码里使用任何寄存器, 我们要明确的用push和

pop指令来保存它们, 或者列到最后一个字段里面让gcc来处理它们。

这是前面的一个例子, 没有明确的保留和存贮eax。

    int i=0, j=1, k=0;
    __asm__ __volatile__("
        pushl %%eax\n    /*译者注:好像原文说的有点问题,明明是保存了eax的值,:(*/
        movl %1, %%eax\n
        addl %2, %%eax\n
        movl %%eax, %0\n
        popl %%eax"
    : "=g" (k)
    : "g" (i), "g" (j)
    : "ax", "memory"
    );    // k = i + j
我们让gcc来保存和存贮eax, 如果必要的话。一个16-bit寄存器名代表了32-, 16-或8-bit寄存器。 如果我们要改写内存 (写入一个变量等。

), 建议在register-modified字段里面来指定"memroy"修饰符。这意味着除了第一个例子我们都应该加上这个修饰符, 但是直到现在我才提出

来, 是为了更简单易懂。

在你的内联汇编里面定位标号应该使用b或f来作为终止符, 尤其是向后向前的跳转。(译者注:b代表向后跳转,f代表向前跳转)

For example,

    __asm__ __volatile__("
        0:\n
            ...
            jmp 0b\n
            ...
            jmp 1f\n
            ...
        1:\n
            ...
    );
这里有个用c代码和内联汇编代码混合写的跳转程序的例子(thanks to Srikanth B.R for this tip).

void MyFunction( int x, int y )
{
    __asm__( "Start:" );
    __asm__( ...do some comparison... );
    __asm__( "jl Label_1" );

    CallFunction( &x, &y );    
    __asm__("jmp Start");

Label_1:
    return;
}

--------------------------------------------------------------------------------

External Asm
Blah... Okay fine. Here's a clue: Get some of your C/C++ files, 且用gcc -S file.c来编译。 然后查看file.S文件。基本结构如下:
    .file "myasm.S"

    .data
    somedata: .word 0
    ...

    .text
    .globl __myasmfunc
    __myasmfunc:
    ...
    ret
Macros, macros! 头文件libc/asmdefs.h便于你写asm。 在你的汇编代码最前面包含此头文件然后就可以使用宏了。一个例子: myasm.S:
    #include <libc/asmdefs.h>

    .file "myasm.S"

    .data
    .align 2
    somedata: .word  0
    ...

    .text
    .align 4
    FUNC(__MyExternalAsmFunc)
    ENTER
            movl   ARG1, %eax
            ...
            jmp    mylabel
            ...
        mylabel:
            ...
    LEAVE
这是一个好的纯粹的汇编代码框架

posted @ 2006-04-23 17:53 Jerry Cat 阅读(568) | 评论 (0)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


C与C++编程一头一尾最重要, 无数英雄折腰, 多少豪杰翻船!

作者: Jerry Cat
时间: 2006/04/22
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/22/6086.aspx


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//Fuction: BOOL revstr(char *str);
//Purpose: reverse a string
//Author:  Jerry
//Time:     04/22/2006
//Feature: This version is suitable for both sring and char array

char* revstr(char *str)
{
    int nCnt = 0, len = strlen(str);
    char* tmpstr = (char*) malloc(len+1);
    if     (tmpstr) strcpy(tmpstr, str);
    else { printf("\n malloc error!\n"); return 0; }
    for(--len, tmpstr = tmpstr + len; len > 0; *str++ = *tmpstr--, --len, ++nCnt);
    *str = *tmpstr; //C/C++编程一头一尾最重要, 无数英雄折腰, 多少豪杰翻船!
    free(tmpstr);
    return str - nCnt;
}

上面方法不好, 繁杂且易出错, 经沐枫兄指点, 下面俩是改进版^_^
char* revstr(char *str)
{
    char c, *head = str, *tail = str + strlen(str) -1;
    for(; head < tail; c = *head, *head++ = *tail, *tail-- = c);
    return str;
}

char* revstr(char *str)
{   //省去一变量, 时间换空间法
    char *head = str, *tail = str + strlen(str) -1;
    for(; head < tail; *head=*head ^ *tail, *tail=*head ^ *tail, *head=*head++ ^ *tail--);
    return str;
}
 

posted @ 2006-04-22 22:20 Jerry Cat 阅读(1091) | 评论 (7)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


Linux静态、共享和动态库之编程

作者: Jerry Cat
时间: 2006/04/21
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/22/6065.html

一.库的分类
    有两种说法, 如果熟悉WIN平台下的DLL, 相信不难理解:

    库可以有三种使用的形式:静态、共享和动态.静态库的代码在编译时就已连接到开发人员开发的应用程序中, 而共享库只是在程序开始运行时才载入, 在编译时, 只是简单地指定需要使用的库函数.动态库则是共享库的另一种变化形式.动态库也是在程序运行时载入, 但与共享库不同的是, 使用的库函数不是在程序运行开始, 而是在程序中的语句需要使用该函数时才载入.动态库可以在程序运行期间释放动态库所占用的内存, 腾出空间供其它程序使用.由于共享库和动态库并没有在程序中包括库函数的内容, 只是包含了对库函数的引用, 因此代码的规模比较小.

    Linux下的库文件分为共享库和静态库两大类, 它们两者的差别仅在程序执行时所需的代码是在运行时动态加载的, 还是在编译时静态加载的.区分库类型最好的方法是看它们的文件后缀, 通常共享库以.so(Shared Object的缩写)结尾, 静态链接库通常以.a结尾(Archive的缩写).在终端缺省情况下, 共享库通常为绿色, 而静态库为黑色.

     已经开发的大多数库都采取共享库的方式.ELF格式的可执行文件使得共享库能够比较容易地实现, 当然使用旧的a.out模式也可以实现库的共享.Linux系统中目前可执行文件的标准格式为ELF格式.

  .a的是为了支持较老的a.out格式的可执行文件的
  .so的是支持elf格式的可执行文件的库.

   .a是静态库文件, 可以用ar 命令生成.
  .so是动态库文件, 编译时加上指定的选项即可生成, 具体选项看相应的系统文档了.

二.库的命名规则
    GNU库的使用必须遵守Library GNU Public License(LGPL许可协议).该协议与GNU许可协议略有不同, 开发人员可以免费使用GNU库进行软件开发, 但必须保证向用户提供所用的库函数的源代码.

  系统中可用的库都存放在/usr/lib和/lib目录中.库文件名由前缀lib和库名以及后缀组成.根据库的类型不同, 后缀名也不一样.共享库的后缀名由.so和版本号组成, 静态库的后缀名为.a.采用旧的a.out格式的共享库的后缀名为.sa.
  libname.so.major.minor
  libname.a

  这里的name可以是任何字符串, 用来唯一标识某个库.该字符串可以是一个单字、几个字符、甚至一个字母.数学共享库的库名为libm.so.5, 这里的标识字符为m, 版本号为5.libm.a则是静态数学库.X-Windows库名为libX11.so.6, 这里使用X11作为库的标识, 版本号为6.

三.库操作命令

   Linux库操作可以使用命令完成, 目前常用的命令是ldd和ldconfig.

   1.ldd
 ldd是Library Dependency Display缩写, 它的作用是显示一个可执行程序必须使用的共享库.

 $ ldd /usr/bin/mesg
 libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7eaf000)
 /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb7feb000)

   2.ldconfig
 库安装到系统以后, 为了让动态链接库为系统所认识及共享, 就需要运行ldconfig.ldconfig命令的用途, 主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下, 搜索出可共享的动态链接库(格式如lib*.so*), 进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc/ld.so.cache, 此文件保存已排好序的动态链接库名字列表, ldconfig通常在系统启动时运行, 而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.

     (1)命令格式
 ldconfig [选项] [libs]

     (2)主要选项
 -v或--verbose ldconfig将显示正在扫描的目录、搜索到的动态链接库, 以及它所创建的连接的名字.

 -f CONF 指定动态链接库的配置文件为CONF, 系统默认为/etc/ld.so.conf.

 -C CACHE 指定生成的缓存文件为CACHE, 系统默认的是/etc/ld.so.cache,文件存放已排好序的可共享的动态链接库的列表.

 -p或--print-cache 让ldconfig打印出当前缓存文件所保存的所有共享库的名字.

 -r ROOT 改变应用程序的根目录为ROOT.

 -n ldconfig仅扫描命令行指定的目录, 不扫描默认目录(/lib、/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录.

 运行没有选项的ldconfig命令时, 用于更新高速缓冲文件.这个命令主要用于高速缓冲DNS服务器(Caching DNS Server).高速缓冲DNS服务器的原理是提供查询的历史记录, 并且利用这些记录来提高查询的效率.

 当某个查询是第一次被发送到高速缓冲DNS服务器时, 高速缓冲DNS服务器就将此查询的整个过程记录下来, 在一定的时期内用它来回答所有相同的查询, 从而减少整个DNS系统的负担并且提高查询速度.

四.库的升级

 Linux系统软件更新很快, 新的核心几乎每几个星期就公布一次, 其它软件的更新也是非常频繁.多数情况下, 盲目跟随潮流的升级并不必要, 如果确实需要新版本的特性时再升级.换句话说, 不要为了升级而升级.Linux系统中多数软件都是用共享库来编译的, 其中包含了在不同程序之间共享的公用子例程.

在运行某个程序时, 如果看到如下信息:“Incompatible library version.”则表明需要将该库升级到程序所需要的版本.库是向下兼容的, 也就是说, 用老版本库编译的程序可以在新安装的版本库上运行, 反之则不行.

Linux库函数的升级是一项重要的工作, 往往与其它软件包的升级有一定关联作用, 所以操作前一定要备份文件.下面看一下如何把Glibc 2.2.4.13升级至2.3.2版本, 其过程如下:

  1.下载.gz压缩文件并解压

在GUN C网站下载的四个.gz压缩文件, 解压至一临时目录中:
cd /usr/caolinux
tar xzvf glibc-2.3.2.tar.gz
cd glibc-2.3.2
tar xzvf ../glibc-linuxthreads-2.3.2.tar.gz
tar xzvf ../glibc-crypt-2.3.2.tar.gz
tar xzvf ../glibc-localedata-2.3.2.tar.gz

  2.建立库函数的安装目录
mkdir /usr/higlibc
cd /usr/higlibc

   3.建立编译目录
mkdir cao
cd cao
./configure --enable-add-ons=linuxthreads,crypt,localedata -prefix=/usr/higlibc

  4.编译与安装
make
make check
make install

  5.改变数据库的链接
ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2

然后, 修改/etc/ld.so.conf, 加入一行/usr/higlibc/lib, 执行下面代码:
ldconfig -v

更新/etc/ld.so.cache的内容, 列出每个库的版本号, 扫描目录和所要创建及更新的链接.

 6.更改GCC设置

cd /usr/lib/gcc-lib
cp -r i386-redhat-linux higlibc

 7.更新符号链接
cd /usr/higlibc/include
ln -s /usr/src/linux/include/linux
ln -s /usr/src/linux/include/asm
ln -s /usr/X11R6/include/X11

8.测试并完成

五.高级共享库特性
 1. soname

共享库的一个非常重要的, 也是非常难的概念是 soname——简写共享目标名(short for shared object name).这是一个为共享库(.so)文件而内嵌在控制数据中的名字.如前面提到的, 每一个程序都有一个需要使用的库的清单.这个清单的内容是一系列库的 soname, 如同 ldd 显示的那样, 共享库装载器必须找到这个清单.

soname 的关键功能是它提供了兼容性的标准.当要升级系统中的一个库时, 并且新库的 soname 和老的库的 soname 一样, 用旧库连接生成的程序, 使用新的库依然能正常运行.这个特性使得在 Linux 下, 升级使用共享库的程序和定位错误变得十分容易.

在 Linux 中, 应用程序通过使用 soname, 来指定所希望库的版本.库作者也可以通过保留或者改变 soname 来声明, 哪些版本是相互兼容的, 这使得程序员摆脱了共享库版本冲突问题的困扰.

查看/usr/local/lib 目录, 分析 MiniGUI 的共享库文件之间的关系

2. 共享库装载器

当程序被调用的时候, Linux 共享库装载器(也被称为动态连接器)也自动被调用.它的作用是保证程序所需要的所有适当版本的库都被调入内存.共享库装载器名字是 ld.so 或者是 ld-linux.so, 这取决于 Linux libc 的版本, 它必须使用一点外部交互, 才能完成自己的工作.然而它接受在环境变量和配置文件中的配置信息.

文件 /etc/ld.so.conf 定义了标准系统库的路径.共享库装载器把它作为搜索路径.为了改变这个设置, 必须以 root 身份运行 ldconfig 工具.这将更新 /etc/ls.so.cache 文件, 这个文件其实是装载器内部使用的文件之一.

3. 使用 dlopen

另外一个强大的库函数是 dlopen().该函数将打开一个新库, 并把它装入内存.该函数主要用来加载库中的符号, 这些符号在编译的时候是不知道的.比如 Apache Web 服务器利用这个函数在运行过程中加载模块, 这为它提供了额外的能力.一个配置文件控制了加载模块的过程.这种机制使得在系统中添加或者删除一个模块时, 都不需要重新编译了.

可以在自己的程序中使用 dlopen().dlopen() 在 dlfcn.h 中定义, 并在 dl 库中实现.它需要两个参数:一个文件名和一个标志.文件名可以是我们学习过的库中的 soname.标志指明是否立刻计算库的依赖性.如果设置为 RTLD_NOW 的话, 则立刻计算;如果设置的是 RTLD_LAZY, 则在需要的时候才计算.另外, 可以指定 RTLD_GLOBAL, 它使得那些在以后才加载的库可以获得其中的符号.

当库被装入后, 可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数, 以获得符号在库中的地址.使用这个地址, 就可以获得库中特定函数的指针, 并且调用装载库中的相应函数.

六、LINUX下动态链接库的使用
重要的dlfcn.h头文件
LINUX下使用动态链接库, 源程序需要包含dlfcn.h头文件, 此文件定义了调用动态链接库的函数的原型.下面详细说明一下这些函数.
1. dlerror
原型为: const char *dlerror(void);
当动态链接库操作函数执行失败时, dlerror可以返回出错信息, 返回值为NULL时表示操作函数执行成功.
2. dlopen
原型为: void *dlopen (const char *filename, int flag);
dlopen用于打开指定名字(filename)的动态链接库, 并返回操作句柄.
filename: 如果名字不以/开头, 则非绝对路径名, 将按下列先后顺序查找该文件.
(1) 用户环境变量中的LD_LIBRARY值;
(2) 动态链接缓冲文件/etc/ld.so.cache
(3) 目录/lib, /usr/lib
flag表示在什么时候解决未定义的符号(调用).取值有两个:
1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决.
2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号, 一旦未解决, dlopen将返回错误.
dlopen调用失败时, 将返回NULL值, 否则返回的是操作句柄.
3. dlsym : 取函数执行地址
原型为: void *dlsym(void *handle, char *symbol);
dlsym根据动态链接库操作句柄(handle)与符号(symbol), 返回符号对应的函数的执行代码地址.由此地址, 可以带参数执行相应的函数.
如程序代码: void (*add)(int x,int y); /* 说明一下要调用的动态函数add */
add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */
add(89,369); /* 带两个参数89和369调用add函数 */
4. dlclose : 关闭动态链接库
原型为: int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库, 只有当此动态链接库的使用计数为0时,才会真正被系统卸载.

posted @ 2006-04-22 01:32 Jerry Cat 阅读(3398) | 评论 (1)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


1.2 A Keyword Distinction

作者: Jerry Cat
时间: 2006/04/20
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/22/6064.html

-------------------------
unequivocal: 毫不含糊的
pedestrian:  步行者; 步行的, 呆板的, 通俗的
infamous:    声名狼藉的
strike aside:闪躲开
brandish:    挥舞(n. & v.)
fledgling:   n.羽毛初长的雏鸟, 羽翼未丰无经验的人, 初出茅庐的人
underpinning:基础, 支柱, 支撑
discourse:   谈话, 演说, 演讲, 论文(n. & v.)

C++的struct 与 class有恼人的暧昧关系, 表面上struct里没显式说明存储权限的全是public而class则全是private, 事实却并非如此简单当考虑到与C的兼容时, 尤其是对待C中的tricks时, 更是traps多多, 地雷密布!

A C program's trick is sometimes a C++ program's trap. One example of this is the use of a one-element array at the end of a struct to allow individual struct objects to address variable-sized arrays:

struct mumble {
   /* stuff */
   char pc[ 1 ];
};

// grab a string from file or standard input
// allocate memory both for struct & string

struct mumble *pmumb1 = ( struct mumble* )
   malloc(sizeof(struct mumble)+strlen(string)+1);//在C中内存连续分布的, 但若考虑到
   //这是在C++中, struct基本上就是类, 这类的数据成员与外来(参)变量的"tricky 捆绑式"
   //内存布局将导致派生类的数据成员"插不进去"从而导致类的数据成员内存布局不连续!
   //所以C的trick是非标准的不能滥用!

strcpy( &mumble.pc, string );
This may or may not translate well when placed within a class declaration that

1). specifies multiple access sections containing data,
2). derives from another class or is itself the object of derivation, or
3). defines one or more virtual functions.

The data members within a single access section are guaranteed within C++ to be laid out in the order of their declaration. The layout of data contained in multiple access sections, however, is left undefined. In the following declaration, for example, the C trick may or may not work, depending on whether the protected data members are placed before or after those declared private:

class stumble {
public:
   // operations ...
protected:
   // protected stuff
private:
   /* private stuff */
   char pc[ 1 ];
};
 
Similarly, the layout of data members of the base and derived classes is left undefined, thereby also negating any guarantee that the trick might work. The presence of a virtual function also places the trick's viability in question. The best advice is not to do it. (Chapter 3 discusses these layout issues in greater detail.)

//接上
If a programmer absolutely needs a data portion of an arbitrarily complex C++ class to have the look and feel of an equivalent C declaration, that portion is best factored out into an independent struct declaration. The original idiom for combining this C portion with its C++ part (see [KOENIG93]) was to derive the C++ part from the C struct:

struct C_point { ... };
class Point : public C_point { ... };
thus supporting both the C and C++ usage:

extern void draw_line( Point, Point );
extern "C" void draw_rect ( C_point, C_Point );

draw_line( Point( 0, 0 ), Point( 100, 100 ));
draw_rect( Point( 0, 0 ), Point( 100, 100 ));
This idiom is no longer recommended, however, because of changes to the class inheritance layout in some compilers (for example, the Microsoft C++ compiler) in support of the virtual function mechanism (see Section 3.4 for a discussion). Composition, rather than inheritance, is the only portable method of combining C and C++ portions of a class (the conversion operator provides a handy extraction method):

struct C_point { ... };

class Point {
public:
   operator C_point() { return _c_point; }
   // ...
private:
   C_point _c_point;
   // ...
};

强烈不推荐这种种"淫巧",  不过在C/C++混合编程时你还不得不用它:)
One reasonable use of the C struct in C++, then, is when you want to pass all or part of a complex class object to a C function. This struct declaration serves to encapsulate that data and guarantees a compatible C storage layout. This guarantee, however, is maintained only under composition. Under inheritance, the compiler decides whether additional data members are inserted within the base struct subobject (again, see Section 3.4 for a discussion, as well as Figures 3.2(a) and 3.2(b)).

posted @ 2006-04-22 01:23 Jerry Cat 阅读(553) | 评论 (0)编辑 收藏

胡锦涛主席说"我也每天使用微软公司的操作系统"

胡主席访问微软时说:“比尔·盖茨先生是中国的朋友,我则是微软的朋友。”他告诉盖茨:“我也每天使用微软公司的操作系统。”盖茨则说:“无论何时您需要关于windows的建议,我将很高兴提供帮助。”如果不考虑中美贸易摩擦的大背景,这番对话简直就是一对老朋友之间的对话,而不是国家首脑和一位企业领导者之间的对话。

---------------------------------------------------------

胡锦涛主席访问微软重申保护知识产权决心
2006.04.21  来自:国际金融报   

  表示加强知识产权保护不仅对中国扩大对外开放和改善投资环境十分必要,而且对于加强中国自主创新能力也是不可或缺的。

  美国西雅图消息当地时间4月18日下午,正在美国华盛顿州西雅图访问的中国国家主席胡锦涛,会见了华盛顿州州长葛瑞格尔。

  随后,胡锦涛主席前往设在莱德蒙德的微软公司总部参观,并出席了在比尔·盖茨家举行的欢迎宴会。

  参观微软是胡锦涛此次访美行程中极其重要的一个环节。在参观了微软公司的主要技术后,胡锦涛重申了中国加强知识产权保护的决心。他表示,此举“不仅对中国扩大对外开放和改善投资环境”十分必要,而且“对于加强中国自主创新能力”也是不可或缺的。

  胡锦涛称赞了比尔·盖茨在微软取得的成就,并说:“因为你,比尔·盖茨先生是中国的朋友,我则是微软的朋友。”他表示希望看到微软和中国的合作,并欢迎微软将来增加在中国的投资。

  就在胡锦涛开始访美的当天,联想与微软签订12亿美元的软件购买协议,此前方正、同方及TCL等公司也签订了价值17亿美元的软件购买协议,为其生产的计算机预装微软Windows操作系统,以此抵制盗版软件。

  微软公司的高管们认为,胡锦涛的这次访问象征着中国政府对知识产权的态度出现根本转变。中国已开始强化在知识产权问题上的立场,因为加强对知识产权的保护符合中国自身的经济发展利益。

  微软首席技术官克瑞格·蒙迪表示,“我认为,一年多来,中国领导人已越过了一道坎,意识到知识产权对中国本身的未来要求也至关重要。”尽管问题不会在一夜之间改变,但他认为最近的几则通告是一种“重大飞跃”。

  蒙迪说,中国仍是微软优先考虑的重点,无论是作为一个市场还是未来市场的技术来源,尽管在知识产权方面存在一些困难。

  当天上午10时50分左右,胡锦涛乘坐的专机抵达西雅图佩因机场。在机场发表书面讲话时,胡锦涛指出,中美都是伟大的国家,拥有广泛的共同利益和坚实的合作基础,肩负着促进世界和平与发展的共同责任。一个健康稳定、不断发展的中美关系不仅造福两国人民,而且有利于亚太地区及世界的和平、稳定、繁荣。

  当晚在比尔·盖茨豪宅为胡主席一行举行的欢迎宴会上,美中关系全国委员会主席欧伦斯对胡锦涛此行给予了高度评价。他称,“胡锦涛把访美的第一站放在西雅图,其实是用行动对解决中美贸易争端作出承诺——中国愿意开放经济、同美国加强合作。这就是一个‘负责任的利益相关者’所代表的含义。”

  据悉,胡锦涛将于当地时间19日参观波音公司,然后飞往华盛顿与布什总统在白宫举行会谈,鉴于近期国际市场原油价格飙升,各界人士纷纷预计能源问题将成为此次会谈的重要议题之一。

  胡锦涛对比尔·盖茨家访有深意

  胡锦涛主席于4月18日前往美国进行为期4天的国事访问,其中一项安排是到全球首富、微软公司董事会主席比尔·盖茨的私人住宅探访,并与100多名其他宾客出席晚宴。

  比尔·盖茨作为全球首富,同时也是先进科学技术的领导者之一,其创办的微软公司对全球科技和经济发展已经发挥了并且还将继续发挥引领作用。从胡主席对比尔·盖茨的家访,至少可以看出三点含义:

  一是表达中国政府维护公平贸易、打击盗版的决心。中国作为全世界第二大PC市场,是国际投资者争夺的对象。对于我国现实存在的软件盗版问题,我国政府高度重视,外界也十分关注。加大反盗版行动的力度,既是促进国内经济秩序健康发展的需要,也是对外国投资者的善意回应。

  在胡主席出访前,信产部、国家版权局、商务部、财政部等于4月10日联合下发《关于计算机预装正版操作系统软件有关问题的通知》,要求在我国境内生产的计算机,出厂时应当预装正版操作系统软件。而进口计算机在国内销售,销售前应当预装正版操作系统软件。同时规定计算机生产者和操作系统软件提供者须按照规定报送计算机销售数量、操作系统软件的预装数量。

  胡主席向盖茨表示,希望看到微软和中国的合作,欢迎微软将来增加在中国的投资,并表示中国会在保护知识产权的问题上履行诺言。

  事实上,我国的反盗版行动直接促进了中国厂商与微软公司的贸易。3家中国电脑制造商将宣布未来3年购买总价值4亿美元的微软Windows操作系统的计划。

  二是体现中国领导人对科学技术、对知识分子的尊重。按照中国的传统,家访是一种很高的礼遇。中国政府历来强调尊重知识、尊重人才,而比尔·盖茨既是一位资本家,也是一位成功的创业者,是自主创新的典型代表。他的创业之路早已为中国人所津津乐道,在年轻人心目中,盖茨堪称一位英雄。

  胡主席访问微软时说:“比尔·盖茨先生是中国的朋友,我则是微软的朋友。”他告诉盖茨:“我也每天使用微软公司的操作系统。”盖茨则说:“无论何时您需要关于windows的建议,我将很高兴提供帮助。”如果不考虑中美贸易摩擦的大背景,这番对话简直就是一对老朋友之间的对话,而不是国家首脑和一位企业领导者之间的对话。

  盖茨的家庭宴会上,其实只有三道大菜:烟熏珍珠鸡沙拉、黄洋葱配制的牛排或大比目鱼配大虾(任选其一)、牛油杏仁大蛋糕。说白了就是鸡肉、牛肉或者虾鱼和蛋糕。当然,美国人待客历来只如此,但如果按照中国人的眼光,把盖茨看作是“知识分子企业家”,这个场面和中国传统知识分子的待客模式也还有些相像。

  三是表达中国努力建设创新型国家的雄心。

  今年1月9日,胡主席在全国科技大会上宣布中国将用15年时间建设创新型国家;科技大会召开3个月后,他借访美之机,亲自造访微软总部,考察微软先进技术,显然具有深意。

  毫无疑问,建设创新型国家需要体制和资金上的支持,但同时,在全社会营造“创新文化”更加重要。承认、尊重他人的科技成果,努力学习借鉴他人的先进经验,应当是创新文化的基本要素。

  当中国国家主席与微软领导人聚首之时,传递出的正是对中华文化优良传统的继承和弘扬,对外国文化有益成果的积极吸收和借鉴之意。

posted @ 2006-04-21 16:12 Jerry Cat 阅读(524) | 评论 (0)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


C++对象模型(1)


作者: Jerry Cat
时间: 2006/04/20
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/20/5969.html

Chapter 0 : Preface
=-=-=-=-=-=-=-=-=-=
I must also say that the general pattern of virtual function implementation across all current compilation systems is to use a class-specific virtual table of a fixed size that is constructed prior to program execution.

Chapter 1: Object Lessons
=-=-=-=-=-=-=-=-=-=-=-=-=
1.1 The C++ Object Model
-------------------------
(1). class Point
{
public:
   Point( float xval );
   virtual ~Point();
   float x() const;
   static int PointCount();

protected:
   virtual ostream& print( ostream &os ) const;
   float _x;
   static int _point_count;
};

Stroustrup's original (and still prevailing) C++ Object Model is derived from the simple object model by optimizing for space and access time. Nonstatic data members are allocated directly within each class object. Static data members are stored outside the individual class object. Static and nonstatic function members are also hoisted outside the class object. Virtual functions are supported in two steps:

  1). A table of pointers to virtual functions is generated for each class (this is called the virtual table).

  2). A single pointer to the associated virtual table is inserted within each class object (traditionally, this has been called the vptr). The setting, resetting, and not setting of the vptr is handled automatically through code generated within each class constructor, destructor, and copy assignment operator (this is discussed in Chapter 5). The type_info object associated with each class in support of runtime type identification (RTTI) is also addressed within the virtual table, usually within the table's first slot.

(2). 加入继承后
C++ supports both single inheritance:

class Library_materials { ... };
class Book : public Library_materials { ... };
class Rental_book : public Book { ... };
and multiple inheritance:

// original pre-Standard iostream implementation
class iostream:
   public istream,
   public ostream { ... };
Moreover, the inheritance may be specified as virtual (that is, shared):

class istream : virtual public ios { ... };
class ostream : virtual public ios { ... };

In the case of virtual inheritance, only a single occurrence of the base class is maintained (called a subobject) regardless of how many times the class is derived from within the inheritance chain. iostream, for example, contains only a single instance of the virtual ios base class.

How might a derived class internally model its base class instance? In a simple base class object model, each base class might be assigned a slot within the derived class object. Each slot holds the address of the base class subobject. The primary drawback to this scheme is the space and access-time overhead of the indirection. A benefit is that the size of the class object is unaffected by changes in the size of its associated base classes.

Alternatively, one can imagine a base table model. Here, a base class table is generated for which each slot contains the address of an associated base class, much as the virtual table holds the address of each virtual function. Each class object contains a bptr initialized to address its base class table. The primary drawback to this strategy, of course, is both the space and access-time overhead of the indirection. One benefit is a uniform representation of inheritance within each class object. Each class object would contain a base table pointer at some fixed location regardless of the size or number of its base classes. A second benefit would be the ability to grow, shrink, or otherwise modify the base class table without changing the size of the class objects themselves.

The original inheritance model supported by C++ forgoes all indirection; the data members of the base class subobject are directly stored within the derived class object. This offers the most compact and most efficient access of the base class members. The drawback, of course, is that any change to the base class members, such as adding, removing, or changing a member's type, requires that all code using objects of the base class or any class derived from it be recompiled.

posted @ 2006-04-20 18:13 Jerry Cat 阅读(508) | 评论 (0)编辑 收藏

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/

VC++的链接错误LNK2005


作者: Jerry Cat
时间: 2006/04/19
链接: http://www.cppblog.com/jerysun0818/archive/2006/04/19/5913.html

编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误。弄清楚它形成的原因,就可以轻松解决它了。

造成LNK2005错误主要有以下几种情况:
1.重复定义全局变量。可能存在两种情况:
A、对于一些初学编程的程序员,有时候会以为需要使用全局变量的地方就可以使用定义申明一下。其实这是错误的,全局变量是针对整个工程的。正确的应该是在一个CPP文件中定义如下:int g_Test;那么在使用的CPP文件中就应该使用:extern int g_Test即可,如果还是使用int g_Test,那么就会产生LNK2005错误,一般错误错误信息类似:AAA.obj error LNK2005 int book c?
book@@3HA already defined in BBB.obj。切记的就是不能给变量赋值否则还是会有LNK2005错误。
       这里需要的是“声明”,不是“定义”!根据C++标准的规定,一个变量是声明,必须同时满足两个条件,否则就是定义:
(1)声明必须使用extern关键字;(2)不能给变量赋初值
所以,下面的是声明:
extern int a;
下面的是定义
int a; int a = 0; extern int a =0;
B、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。

2.头文件的包含重复。往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。解决办法是在需要包含的头文件中做类似的处理:#ifndef MY_H_FILE   //如果没有定义这个宏
#define MY_H_FILE   //定义这个宏
…….   //头文件主体内容
…….
#endif
上面是使用宏来做的,也可以使用预编译来做,在头文件中加入:
#pragma once
//头文件主体
3.使用第三方的库造成的。这种情况主要是C运行期函数库和MFC的库冲突造成的。具体的办法就是将那个提示出错的库放到另外一个库的前面。另外选择不同的C函数库,可能会引起这个错误。微软和C有两种C运行期函数库,一种是普通的函数库:LIBC.LIB,不支持多线程。另外一种是支持多线程的:msvcrt.lib。如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的:
A、选择VC菜单Project->Settings->Link->Catagory选择Input,再在Ignore libraries 的Edit栏中填入你需要忽略的库,如:Nafxcwd.lib;Libcmtd.lib。然后在Object/library Modules的Edit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序,呵呵,God bless you!
B、选择VC菜单Project->Settings->Link页,然后在Project Options的Edit栏中输入/verbose:lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。
C、选择VC菜单Project->Settings->C/C++页,Catagory选择Code Generation后再在User Runtime libraray中选择MultiThread DLL等其他库,逐一尝试。

这就是我所遇到过的LNK2005错误的几种情况,肯定还有其他的情况也可能造成这种错误,所以我不希望你在看完这篇文章以后,再遇到LNK2005错误时候,不动脑筋的想对号入座的排除错误。编程的过程就是一个思考的过程,所以还是多多开动你的头脑,那样收获会更多!
=======================================
支持,我在社区里也看到了许多LINK 2005错
补充一点,就是一次在用第三方库时,由于errno被重定义,用多种方法都不能解决,后查找MSDN,发现link有个选项/FORCE可以解决,在IDE下
Project->Settings->Link页,选categroy为custom,将force file output前打勾
但会有警告
warning LNK4088: image being generated due to /FORCE option; image may not run
但的确解决了问题,这是由于VC对重定义比较严格,像BCB或GCC在库中的重定义不会有任何警告或错误
========================================
我发现的另外一个出现LINK2005的现象,好像是由于名称空间而引起的。我在dos下写的程序没有问题,但是放在mfc中就出现了这个链接错误。因为起初图省事,我在一个头文件中写了using namespace std,并且这个头文件我多处使用,另外,我还使用了boost库。后来,问题解决的方法非常奇怪,在一个头文件中引用其他头文件,这些头文件的顺序换一下就通过了,那个出现问题的头文件中我使用了std::map,当我把这种容器使用模板代替后,链接就有没事了。(例如:template<class coll>),后来感到模板技术还有这种效果,赚了!哈哈
========================================
What are the C and C++ libraries my program would link with?

                      Summary Table

 Compile   Old             New IOStream    Libraries

 Option     IOStream    or STL                Linked With

 

 /ML        No                No                   LIBC.LIB

 /MLd      No                No                   LIBCD.LIB

 /MT        No                No                   LIBCMT.LIB

 /MTd      No                No                   LIBCMTD.LIB

 /MD       No                 No                   MSVCRT.LIB

 /MDd     No                 No                   MSVCRTD.LIB

 /ML        No                Yes                   LIBC.LIB,     LIBCP.LIB

 /MLd      No                Yes                   LIBCD.LIB,   LIBCPD.LIB

 /MT        No                Yes                   LIBCMT.LIB,  LIBCPMT.LIB

 /MTd      No                Yes                   LIBCMTD.LIB, LIBCPMTD.LIB

 /MD        No                Yes                   MSVCRT.LIB,  MSVCPRT.LIB

 /MDd      No                Yes                   MSVCRTD.LIB, MSVCPRTD.LIB

 /ML        Yes                No                   LIBC.LIB,      LIBCI.LIB

 /MLd      Yes                No                   LIBCD.LIB,    LIBCID.LIB

 /MT        Yes                No                   LIBCMT.LIB,   LIBCIMT.LIB

 /MTd      Yes                No                   LIBCMTD.LIB,  LIBCIMTD.LIB

 /MD        Yes               No                   MSVCRT.LIB,  MSVCIRT.LIB

 /MDd      Yes               No                   MSVCRTD.LIB, MSVCIRTD.LIB


你的程序使用了/ML编译选项,而程序依赖的.lib可能使用/MDd选项编译,造成链接冲突.

统一编译选项可回避此错误
Project Settings->C/C++ Tab->Category:CodeGeneration
Use run-time library组合框中选择Multithread Dll(或Debug Multithread Dll )

Summary Table for CRT DLLs Used
Import Library Linked With DLLs Used (Visual C++ 5.0|6.0) DLLs Used (Visual C++ 4.2) DLLs Used (Visual C++ .NET 2002| Visual C++ .NET 2003)
MSVCRT.LIB MSVCRT.DLL MSVCRT.DLL MSVCRT.DLL
MSVCRTD.LIB MSVCRTD.DLL MSVCRTD.DLL MSVCRTD.DLL
MSVCPRT.LIB MSVCP(5|6)0.DLL MSVCP7(0|1).DLL
MSVCPRTD.LIB MSVCP(5|6)0D.DLL MSVCP7(0|1)D.DLL
MSVCIRT.LIB MSVCIRT.DLL MSVCIRT.DLL
MSVCIRTD.LIB MSVCIRTD.DLL MSVCIRTD.DLL


注意:所有相关工程都应该选择相同编译选项
========================================
微软的MSDN中查到信息的

可能的原因

不慎同时与单线程库和多线程库链接。确保应用程序项目文件只包括适当的库,并且任何第三方库已适当创建了单线程或多线程版本。
该符号为封装函数(通过用 /Gy 编译创建),包含在多个文件中,但在各编译间已改变。重新编译所有包含 symbol 的文件。
以不同的形式在不同库中的两个成员对象中定义了该符号,并且使用了这两个成员对象。
某个绝对符号被定义两次,而每次定义的值不同。
头文件声明并定义了变量。可能的解决方案有:
在 .h 中声明变量:extern BOOL MyBool;,然后在 .c 或 .cpp 文件中向它分配:BOOL MyBool = FALSE;。
将变量声明为 Static。
将变量声明为 selectany。
当将 uuid.lib 与定义 GUID 的其他 .lib 文件(例如 oledb.lib 和 adsiid.lib)一起使用时。例如:
oledb.lib(oledb_i.obj) : error LNK2005: _IID_ITransactionObject
already defined in uuid.lib(go7.obj)
若要修复,请将 /FORCE:MULTIPLE 添加到链接器命令行选项,并确保 uuid.lib 是引用的第一个库。

有关更多信息,请参阅知识库文章:

Q148652,PRB: LNK2005 Errors When Link C Run-Time Libraries Are Linked Before MFC Libraries。
Q140440,FIX: Global Overloaded Delete Operator Causes LNK2005。
Q184235,PRB: LNK2005 Errors on New and Delete When Defining _ATL_MIN_CRT。
该错误之后为致命错误 LNK1169。
=======================================
有时候因为工程使用了预编译头文件并且是增量编译,所以当你改动以后可能也会出现LNK2005错误,提示“XXXX已经在XXXX.obj文件中定义”的消息,这时候只要Rebuild All一般都能解决问题。这是因为头文件的顺序被改动等等操作造成的。
最后要说明的:事物是在不断变化中的,C++的标准在变化,编译器也在变化,所以并不是所有的LNK2005错误都可以在这里找到答案,但是至少它能给你以提示。学习并思考才是正确的!

posted @ 2006-04-19 23:45 Jerry Cat 阅读(3897) | 评论 (0)编辑 收藏
仅列出标题
共14页: First 6 7 8 9 10 11 12 13 14 

<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(7)

随笔档案

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜