Xiaodong's profile千米马的厩PhotosBlogListsMore Tools Help

Blog


    温习几个程序问题

    关键字:sizeof, extern "C", for循环的效率
     
    sizeof
     
    再仔细查阅了一下圣经--ISO标准,附1;
    首先,这个操作数求出来的是一个类型按byte计算的大小(The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type)
    个人觉得这个操作符完全可以看作是一种编译期行为,就是说运算在编译的时候已经完成了,编译器在编译后会把 sizeof(...)替换成一个常量(数据类型为标准中说的size_t);对那些在编译时无法确定大小的数据做sizeof操作都是错误的,或者说使用了编译时不能确定类型大小的数据都是不合法的;
    下面看看几种计算结果(假定int的大小是32bit):
    sizeof(2);  //2是int型, 结果为4
    sizeof(2*8); //2*8计算后的结果是int型, 结果仍是4
     
    char* s;
    sizeof(s);   //s 指针类型,结果是4
     
    char s[5];
    sizeof(s);  //s 是数组类型,其大小为5, 结果是5
     
    char* s = "Hello";
    sizeof(s)  //s 还是指针,千万别当成了数组, 结果还是4,
     
    char s[] = "Hello";
    sizeof(s);  //s 在这里可是数组, 所以结果是5;
     
    size_t  func(char s[])
    {
          return sizeof(s);   // 这里s又变成指针了(如果是数组,编译器怎么知道它大小?), 结果是4
    }
     
    size_t func(char s[])
    {
         return sizeof(s[5]); // 嗯,很容易搞混,这里s[5]已经是char类型了(就是s指向的第5个char),结果 
                                    // 是1
    }
     
    char s[5];
    sizeof(s[0]); //这个就比上面那个容易辨认些了,结果是1
     
    typedef struct _A
    {   int i;
        struct _A *next;
    }A;
    sizeof(A);   //A是一个structure,一个int 加一个指针,结果为8
    接上面的定义:
    A *a;
    sizeof(a);   //a 是一个指针,结果不说了
     
    typedef struct
    {
         char c;
         int   i;
    }B;
    sizeof(B);  //c是一个byte,i是4个byte, 但结果却不是5,这里有个指针对齐的问题.这也是标准中提到
                  // 的padding, 处理器访问int须是4的倍数地址访问,在这个结构里,c 占了一个字节, 后面3
                  // 个字节都不能做为i 的访问地址,所以编译器会在c的后面加上3个字节的padding, 这样,
                 // 结果应该是8;
     
    typedef struct
    {
        char c;
        int i;
        char c2;
    }C;
    sizeof(C); // 结果是12, c2后面也会被编译器加上3个padding, 确保紧跟这个结构后面的数据访问是
                 // 4字节对齐的
     
    typedef struct
    {
        char c;
        char c2;
        int i;
    }D;
    sizeof(D); //结果是8, 对char的访问地址可以是奇数.
     
    typedef struct
    {
        char c;
        short s16;
        char c2;
        int i;
    }E;
    sizeof(E); //结果是12, s16是short对它的访问可以是2倍数的地址,所以c后面填了一个byte的
                 // padding, c2后面填了3个 byte的padding.
     
    所以呢,在创建结构的时候有个原则,按数据大小来放置成员,能省不少空间.
     
    extern "C"
    嗯,标准里有较详细的描述, 抄袭在附2备查.
     
    for 循环的效率:
    这是曾经面试被问及的一个问题:
    for( i = 0; i<10; i++);
    for( i =10; i>0; i-- );
    哪个效率更高?看arm-
    GCC输出结果,+比-少一条指令,这样看应该是第一个的效率更高一些了。
    第一条输出的结果:
         mov r3, #9
    .L6:
         subs r3, r3, #1
         bpl .L6
    第二条输出的结果:
         mov r3, #10
    .L6:
         sub r3, r3, #1
         cmp r3, #0
         bgt .L6
     
    再看x86-gcc的输出结果也一样:
            movl      $9, %eax
            .p2align  2,,3
    .L6:
            decl       %eax
            jns         .L6
           
            movl      $10, %eax
            .p2align  2,,3
    .L11:
            decl       %eax
            test1     %eax, %eax
            jg         .L11
     
    [注]以上汇编是加了-O2优化选项的,如果未加优化选项,效率是一样的;
     
     
    附1: 下面的内容摘自:ISO/IEC 9899:1999 (E)
    6.5.3.4 The sizeof operator
       Constraints
    1 The sizeof operator shall not be applied to an expression that has function type or an
       incomplete type, to the parenthesized name of such a type, or to an expression that
       designates a bit-field member.
       Semantics
    2 The sizeof operator yields the size (in bytes) of its operand, which may be an
       expression or the parenthesized name of a type. The size is determined from the type of
       the operand. The result is an integer. If the type of the operand is a variable length array
       type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an
       integer constant.
    3 When applied to an operand that has type char, unsigned char, or signed char,
       (or a qualified version thereof) the result is 1. When applied to an operand that has array
       type, the result is the total number of bytes in the array.84) When applied to an operand
       that has structure or union type, the result is the total number of bytes in such an object,
       including internal and trailing padding.
    4 The value of the result is implementation-defined, and its type (an unsigned integer type)
       is size_t, defined in <stddef.h> (and other headers).
    5 EXAMPLE 1 A principal use of the sizeof operator is in communication with routines such as 
       storage allocators and I/O systems. A storage-allocation function might accept a size (in 
       bytes) of an object to
       allocate and return a pointer to void. For example:
           extern void *alloc(size_t);
           double *dp = alloc(sizeof *dp); 
       The implementation of the alloc function should ensure that its return value is aligned 
       suitably for conversion to a pointer to double.
    6 EXAMPLE 2 Another use of the sizeof operator is to compute the number of elements in an array:
          sizeof array / sizeof array[0]
    7 EXAMPLE 3 In this example, the size of a variable-length array is computed and returned from  a function:
         #include <stddef.h>
         size_t fsize3(int n)
        {
             char b[n+3]; // variable length array
             return sizeof b; // execution time sizeof
        }
        int main()
       {
            size_t size;
            size = fsize3(10); // fsize3 returns 13
            return 0;
       }
     
     
    附2: 下面的内容摘自:ISO/IEC 14882:1998(E)
    7.5 Linkage specifications
     
    linkagespecification:
       
    extern stringliteral { declarationseqopt}
        extern stringliteral declaration
    The stringliteral indicates the required language linkage. The meaning of the stringliteral is implementationdefined.A linkagespecification with a string that is unknown to the implementation is illformed. When the stringliteral in a linkagespecification names a programming language, the pelling of the programming language’s name is implementationdefined. [Note: it is recommended that the spelling be taken from the document defining that language, for example Ada (not ADA) and Fortran or FORTRAN (depending on the vintage). The semantics of a language linkage other than C++ or C are implementationdefined.]
    Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++".
    [Example:
       complex sqrt(complex); // C++ linkage by default
       extern "C" {
          double sqrt(double); // C linkage
       }
    —end example]
       
    Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not stablish a scope. A linkagespecification shall occur only in namespace scope (3.3). In a linkagespecification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).
    [Example:
        extern "C" void f1(void(*pf)(int));
                                               // the name f1 and its function type have C language
                                               // linkage; pf is a pointer to a C function
        extern "C" typedef void FUNC();
        FUNC f2;                         // the name f2 has C++ language linkage and the
                                              // function’s type has C language linkage
        extern "C" FUNC f3;         // the name of function f3 and the function’s type
                                              // have C language linkage
        void (*pf2)(FUNC*);           // the name of the variable pf2 has C++ linkage and
                                              // the type of pf2 is pointer to C++ function that
                                              // takes one parameter of type pointer to C function
    —end example] A C language linkage is ignored for the names of class members and the member function type of class member functions.
    [Example:
        extern "C" typedef void FUNC_c();
        class C {
            void mf1(FUNC_c*);  // the name of the function mf1 and the member
                                           // function’s type have C++ language linkage; the
                                           // parameter has type pointer to C function
            FUNC_c mf2;           // the name of the function mf2 and the member
                                           // function’s type have C++ language linkage
            static FUNC_c* q;     // the name of the data member q has C++ language
                                           // linkage and the data member’s type is pointer to C function
            };
       
        extern "C" {
               class X {
                     void mf();                 // the name of the function mf and the member
                                                    // function’s type have C++ language linkage
                     void mf2(void(*)());     // the name of the function mf2 has C++ language
                                                    // linkage; the parameter has type pointer to C function
               };
        }
    —end example]
     
    If two declarations of the same function or object specify different linkagespecifications (that is, the linkagespecifications of these declarations specify different stringliterals), the program is illformed if the declarations appear in the same translation unit, and the one definition rule (3.2) applies if the declarations appear in different translation units. Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.
     
    At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object. [Note: because of the one definition rule (3.2), only one definition for a function or object with C linkage may appear in the program; that is, such a function or object must not be defined in more than one namespace scope. For example,
       
    namespace A {
            extern "C" int f();
            extern "C" int g() { return 1; }
            extern "C" int h();
        }
        namespace B {
            extern "C" int f();                      // A::f and B::f refer to the same function
            extern "C" int g() { return 1; }     // illformed,the function g with C language linkage
                                                          // has two definitions
        }
        int A::f() { return 98; }                   // definition for the function f with C language linkage
        extern "C" int h() { return 97; }       // definition for the function h with C language linkage
                                                         // A::h and ::h refer to the same function
    —end note]
     
    Except for functions with internal linkage, a function first declared in a linkagespecification behaves as a function with external linkage.
    Example:
        extern "C" double f();
        static double f();      // error
    is illformed(7.1.1). ] The form of linkagespecification that contains a bracedenclosed declarationseq does not affect whether the contained eclarations are definitions or not (3.1); the form of linkagespecification directly containing a single declaration is treated as an extern specifier (7.1.1) for the purpose of determining whether the contained declaration is a definition.
    [Example:
          
    extern "C" int i;    // declaration
           extern "C" {
                 int i;             // definition
           }

    —end example] A linkagespecification directly containing a single declaration shall not specify a storage class.
    [Example:
        extern "C" static void f(); // error
    —end example]
     
    [Note: because the language linkage is part of a function type, when a pointer to C function (for example) is dereferenced, the function to which it refers is considered a C function. ]
    9 Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementationdefined and languagedependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.