Is using sizeof on a variable where a type of the same name exists well defined?

前端 未结 4 1432
忘掉有多难
忘掉有多难 2021-02-19 14:56

Is this well defined behaviour or is it undefined / somehow else defined which foo (data type or identifier) sizeof will be operating on ?



        
相关标签:
4条回答
  • 2021-02-19 15:25

    First of all according to the C Standard (6.5.3.4 The sizeof and alignof operators)

    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.

    Thus it is the type of the operand of the sizeof operator that determinates the size of the operand. So you may use interchangeably either an expression or some type definition.

    Take into account that an expression used in the sizeof operator is not evaluated.

    For example you can write

    int x = 10;
    printf( "%zu\n", sizeof( ++x ) );
    

    and after the printf x will have the same value 10 as before the printf.

    As for your code snippet it seems that you made a mistake. I think you mean the following

    typedef int foo;
    
    int main(int argc, char *argv[])
    {
        foo exp;
        printf ("%zu\r\n", sizeof( exp ));
        return 0;
    }
    

    Of course there is no need to create an object of type foo. You could write simply

        printf ("%zu\r\n", sizeof( foo ));
    

    As for your original code

    typedef int foo;
    
    int main(int argc, char *argv[])
    {
        char foo;
        printf ("%u\r\n", sizeof(foo));
        return 0;
    }
    

    then identifier name foo in the block scope of function main hides the type definition foo in the global scope and statement

        printf ("%u\r\n", sizeof(foo));
    

    is equivalent to

        printf ("%u\r\n", sizeof( char ));
    

    In C you can't refer to the global definition of foo in this case.

    0 讨论(0)
  • 2021-02-19 15:30

    Is this well defined behaviour or is it undefined

    This is well defined behaviour.

    In your code

    printf ("%u\r\n", sizeof(foo));
    

    foo is unambiguous. The local or inner foo "shadows" (or hides) the outer foo. So, it is essentially

    printf ("%u\r\n", sizeof(char));
    

    To quote C11, chapter §6.2.1, "Scopes of identifiers", (emphasis mine)

    If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

    Now, to answer the second part,

    If it is well defined, is there a way I could obtain the size of the datatype foo without declaring a variable of that type to only use sizeof on it?

    Well, a scope of the inner foo identifier starts after it's definition (as a char type variable). So, if you use foo (the typedefed type) before the definition of variable foo, you'll be actually seeing the global definition, as till that point , variable foo is not present to "shadow out" the global identifier foo.

    As mentioned in the other answer by Mr. Purag, you can surely do something like

    #include <stdio.h>
    
    typedef int foo;
    
    int main(void)
    {
        printf ("%zu\n", sizeof foo); /* will cosider the global */
        char foo = 0;                 /* Global definition gets shadowed here*/
        printf ("%zu\n", sizeof foo); /* The local definition of 'foo' is only visible*/ 
                                      /* till the end of the block (function)*/
    
        return 0;
    }
    

    That said, as a note, the type of the result of sizeof operator is size_t. You should be using %zu format specifier to print the result.

    0 讨论(0)
  • 2021-02-19 15:37

    The local char foo completely hides typedef int foo in its scope. You cannot observe the name foo once it is hidden. Try creating a second typedef foo foo_t, or renaming something to avoid the "collision."

    0 讨论(0)
  • 2021-02-19 15:44

    C does not have explicit scope resolution, so identifiers (variable names, typedef names, struct names, etc etc) can be reused and overridden when a new scope is opened. When an identifier is reused, the previous context held by that identifier is no longer visible.

    In your particular code, the scope of the typedef is global, so the typedef is visible everywhere in your compile package. However, you open a new scope with the function declaration, and in that new scope you define a variable that uses the same identifier as the typedef. Now, that identifier refers to the variable instead of the type; meaning, until the scope of the variable ends (the end of the function), the typedef is completely hidden.

    Recall that C is compiled linearly, so you could do something like this as a way around the shielding that occurs:

    #include <stdio.h>
    typedef int foo;
    
    int main()
    {
        printf ("%zu\n", sizeof (foo)); /* #1 */
    
        char foo;
        printf ("%zu\n", sizeof foo); /* #2 */
    
        return 0;
    }
    

    At point #1, note that the scope of the variable char foo has not yet opened since the compiler hasn't reached its declaration. (All the compiler will do is allocate the space on the stack for the variable).

    So the usage of foo at that point is still in reference to the globally defined typedef.

    By the time you hit #2, the variable is declared and the lifetime of the variable is formally started, meaning the identifier is now in use for a different entity. It shields the current block scope (started by the function declaration) from the global definition of foo.

    This is well-document behavior; there is a draft of the C standard up online, but the published standard has to be purchased. The draft says in section 6.2.1:

    If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope. source

    Note that this isn't magical or anything...this is all done at compile-time. The compiler has a table of identifiers and the things they reference, and new scopes create new tables for these. It so happens that at point #1 in the code above, the compiler hasn't yet populated the table with the new char foo (this is due to the linear compilation). So when it translates the first printf line, it looks through all the active scopes to find the identifier foo, and sees the typedef, and uses that. At the second printf, it looks through all the active scopes and finds a more recent use of the identifier foo and uses that.

    0 讨论(0)
提交回复
热议问题