Pointer vs array in C, non-trivial difference

后端 未结 4 1369
没有蜡笔的小新
没有蜡笔的小新 2020-11-27 21:30

I thought I really understood this, and re-reading the standard (ISO 9899:1990) just confirms my obviously wrong understanding, so now I ask here.

The following prog

相关标签:
4条回答
  • 2020-11-27 22:11

    An array is a kind of storage. Syntactically, it's used as a pointer, but physically, there's no "pointer" variable in that struct — just the three ints. On the other hand, the int pointer is an actual datatype stored in the struct. Therefore, when you perform the cast, you are probably* making ptr take on the value of the first element in the array, namely 1.

    *I'm not sure this is actually defined behavior, but that's how it will work on most common systems at least.

    0 讨论(0)
  • 2020-11-27 22:14

    Please forgive me if i overlook anything in your analysis. But i think the fundamental bug in all that is this wrong assumption

    type2_p->ptr has type "pointer to int" and the value is the start address of my_test.

    There is nothing that makes it have that value. Rather, it is very probably that it points somewhere to

    0x00000001
    

    Because what you do is to interpret the bytes making up that integer array as a pointer. Then you add something to it and subscript.

    Also, i highly doubt your casting to the other struct is actually valid (as in, guaranteed to work). You may cast and then read a common initial sequence of either struct if both of them are members of an union. But they are not in your example. You also may cast to a pointer to the first member. For example:

    typedef struct {
        int array[3];
    } type1_t;
    
    type1_t f = { { 1, 2, 3 } };
    
    int main(void) {
        int (*arrayp)[3] = (int(*)[3])&f;
        (*arrayp)[0] = 3;
        assert(f.array[0] == 3);
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 22:14

    It's got to be defined behaviour. Think about it in terms of memory.

    For simplicity, assume my_test is at address 0x80000000.

    type1_p == 0x80000000
    &type1_p->my_array[0] == 0x80000000 // my_array[0] == 1
    &type1_p->my_array[1] == 0x80000004 // my_array[1] == 2
    &type1_p->my_array[2] == 0x80000008 // my_array[2] == 3
    

    When you cast it to type2_t,

    type2_p == 0x80000000
    &type2_p->ptr == 0x8000000 // type2_p->ptr == 1
    type2_p->ptr[0] == *(type2_p->ptr) == *1
    

    To do what you want, you would have to either create a secondary structure & assign the address of the array to ptr (e.g. type2_p->ptr = type1_p->my_array) or declare ptr as an array (or a variable length array, e.g. int ptr[]).

    Alternatively, you could access the elements in an ugly manner : (&type2_p->ptr)[0], (&type2_p->ptr)[1]. However, be careful here since (&type2_p->ptr)[0] will actually be an int*, not an int. On 64-bit platforms, for instance, (&type2_p->ptr)[0] will actually be 0x100000002 (4294967298).

    0 讨论(0)
  • 2020-11-27 22:26

    Where is my reasoning wrong/what do I not understand?

    type_1::array (not strictly C syntax) is not an int *; it is an int [3].

    How do I declare type2_t to make ptr point to the first member of the array?

    typedef struct 
    {    
        int ptr[];
    } type2_t;
    

    That declares a flexible array member. From the C Standard (6.7.2.1 paragraph 16):

    However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array.

    I.e., it can alias type1_t::array properly.

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