Is it OK to access past the size of a structure via member address, with enough space allocated?

前端 未结 3 742
没有蜡笔的小新
没有蜡笔的小新 2020-12-06 04:25

Specifically, is the following code, the line below the marker, OK?

struct S{
    int a;
};

#include 

int main(){
    struct S *p;
    p =          


        
3条回答
  •  广开言路
    2020-12-06 05:01

    The intent at least since the standardization of C in 1989 has been that implementations are allowed to check array bounds for array accesses.

    The member p->a is an object of type int. C11 6.5.6p7 says that

    7 For the purposes of [additive operators] a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

    Thus

    &(p->a)
    

    is a pointer to an int; but it is also as if it were a pointer to the first element of an array of length 1, with int as the object type.

    Now 6.5.6p8 allows one to calculate &(p->a) + 1 which is a pointer to just past the end of the array, so there is no undefined behaviour. However, the dereference of such a pointer is invalid. From Appendix J.2 where it is spelt out, the behaviour is undefined when:

    Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that points just beyond the array object and is used as the operand of a unary * operator that is evaluated (6.5.6).

    In the expression above, there is only one array, the one (as if) with exactly 1 element. If &(p->a) + 1 is dereferenced, the array with length 1 is accessed out of bounds and undefined behaviour occurs, i.e.

    behavior [...], for which [The C11] Standard imposes no requirements

    With the note saying that:

    Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

    That the most common behaviour is ignoring the situation completely, i.e. behaving as if the pointer referenced the memory location just after, doesn't mean that other kind of behaviour wouldn't be acceptable from the standard's point of view - the standard allows every imaginable and unimaginable outcome.


    There has been claims that the C11 standard text has been written vaguely, and the intention of the committee should be that this indeed be allowed, and previously it would have been alright. It is not true. Read the part from the committee response to [Defect Report #017 dated 10 Dec 1992 to C89].

    Question 16

    [...]

    Response

    For an array of arrays, the permitted pointer arithmetic in subclause 6.3.6, page 47, lines 12-40 is to be understood by interpreting the use of the word object as denoting the specific object determined directly by the pointer's type and value, not other objects related to that one by contiguity. Therefore, if an expression exceeds these permissions, the behavior is undefined. For example, the following code has undefined behavior:

     int a[4][5];
    
     a[1][7] = 0; /* undefined */ 
    

    Some conforming implementations may choose to diagnose an array bounds violation, while others may choose to interpret such attempted accesses successfully with the obvious extended semantics.

    (bolded emphasis mine)

    There is no reason why the same wouldn't be transferred to scalar members of structures, especially when 6.5.6p7 says that a pointer to them should be considered to behave the same as a pointer to the first element of an array of length one with the type of the object as its element type.

    If you want to address the consecutive structs, you can always take the pointer to the first member and cast that as the pointer to the struct and advance that instead:

    *(int *)((S *)&(p->a) + 1) = 0;
    

提交回复
热议问题