18
struct T{
   T(){}
   ~T(){}
};
int main(){
  auto ptr = (T*)malloc(sizeof(T)*10); // #1
  ptr++;
}

Since T is not an implicit-lifetime class, the operation malloc can never implicitly create an object of class type T. In this example, we intend to make ptr points to the initial element of the array object with 10 elements of type T. And based on this assumption, ptr++ would have a valid behavior. As per [intro.object] p11

Further, after implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object. These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object, if that value would result in the program having defined behavior.

Since an array of any type is an implicit-lifetime type, malloc(sizeof(T)*10) can implicitly create an array object with 10 elements and start the lifetime of that array object. Since the initial element and the containing array are not pointer-interconvertible, ptr can never point to the initial element of that array. Instead, the operation can only produce the pointer value of the array object. With this assumption, we should divide #1 into several steps to make the program well-formed?

  // produce the pointer value points to array object of 10 T
  auto arrPtr = (T(*)[10])malloc(sizeof(T)*10);  
                                                 
  // decay the array 
  auto ptr =*arrPtr; // use array-to-pointer conversion 

  ptr++; 

we should first get the array pointer and use that pointer value to acquire the initial element pointer value? Are these processes necessary in contributing to making the program well-formed?

2
  • Comments are not for extended discussion; this conversation has been moved to chat. Commented Jan 17, 2022 at 17:25
  • @xmh0511 - You say "the initial element and the containing array are not pointer interconvertible". I'm guessing the reason is that one of them has implicit lifetime and the other doesn't. However, you can reasonably say that the pointer actually points to the storage for the object, and not for the object itself. Since C-array semantics define a clear memory layout, I believe this kind of conversion is safe. Commented Feb 18, 2022 at 11:01

1 Answer 1

1

I think the first example is perfectly valid but rather pointless. The array returned by malloc has not initialized any of the objects. While I think you can form a pointer to each object and iterate over the array you must not dereference the pointer at any time.

In my opinion the only thing that is missing here is the use of either placement new or construct_at like this:

struct T{
   T(){}
   ~T(){}
};
int main(){
  constexpr int SIZE = 10;
  auto ptr = (T*)malloc(sizeof(T)*SIZE); // #1
  for (auto p = ptr; p < &ptr[SIZE]; ++p) {
    std::construct_at(p);
  }
  ptr++;
}

The objects in the array can only be constructed using pointer arithmetic or array indexing ptr[i], which is equivalent to *(ptr + i), so basically the same as p++. If your initial example is UB then construct_at calls above would be UB too. Only way around that would be to treat the return of malloc as byte array and calling construct_at every sizeof(T) bytes. After construction the array could then be cast to the right type, I hope. But that would be silly and I hope nobody finds a reason why that should be neccessary.

Overall if your example where illegal then how would you ever manage to implement new() itself?

Sign up to request clarification or add additional context in comments.

3 Comments

I think you missed the point of the question. The question is arguing that malloc(sizeof(T)*SIZE); cannot return a pointer to an object of type T, only to an object of type T[10]. But if it does so the cast to T* cannot point to an object of type T either, because an object of type T is not pointer-interconvertible with an object of type T[10]. Therefore ptr[SIZE] is already UB, because ptr doesn't point to an element of an array.
@user17732522 Thanks, I misread the cast on the malloc there. Wrong stanza removed from the answer.
My first comment still stands though. You are not arguing against the interpretation in the question, but simply assert that ptr[SIZE] is allowed. The question is tagged language-lawyer, so it is about the specific wording of the standard. OP gives an example usage that is definitively not UB without treating as a byte array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.