operator new:void*到T*的转换

In the C++14 standard (as well as C++98/11), there is a statement in Annex C Compatibility:

Change: Converting void* to a pointer-to-object type requires casting

1
2
3
4
5
char a[10];
void* b=a;
void foo() {
char* c=b;
}

ISO C will accept this usage of pointer to void being assigned to a pointer to object type. C++ will not.

But why does operator new() return void* and allow assigning to T* without an explicit conversion to T*?

The basic prototype of operator new in global scope is:

1
2
void* operator new(std::size_t count);
void* operator new[](std::size_t count);

Writing this will not produce any warnings:

1
2
3
struct A{};
A* ap=new A;
delete ap;

Overloading an operator new within a class to return void* also works fine:

1
2
3
4
5
6
struct A{
void* operator new(std::size_t x){ return malloc(x); }
};

A* ap=new A; // OK
delete ap;

However, using std::malloc for allocation and then assignment leads to issues:

1
2
// error: cannot initialize a variable of type 'A *' with an rvalue of type 'void *'
A* ap=std::malloc(sizeof(A));

The compiler is also using the C++14 standard, Clang 3.9…

By analyzing the LLVM-IR, I found that when calling operator new, the compiler automatically converts void* to T*:

1
2
3
4
5
6
7
struct A{};

int main()
{
A *z=new A;
delete z;
}

The corresponding LLVM-IR is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
define i32 @main() #4 {
%1 = alloca i32, align 4
%2 = alloca %struct.A*, align 8
store i32 0, i32* %1, align 4
%3 = call i8* @_Znwy(i64 1) #7
%4 = bitcast i8* %3 to %struct.A*
store %struct.A* %4, %struct.A** %2, align 8
%5 = load %struct.A*, %struct.A** %2, align 8
%6 = icmp eq %struct.A* %5, null
br i1 %6, label %9, label %7

; <label>:7: ; preds = %0
%8 = bitcast %struct.A* %5 to i8*
call void @_ZdlPv(i8* %8) #8
br label %9

; <label>:9: ; preds = %7, %0
%10 = load i32, i32* %1, align 4
ret i32 %10
}
; Function Attrs: nobuiltin
declare noalias i8* @_Znwy(i64) #5

; Function Attrs: nobuiltin nounwind
declare void @_ZdlPv(i8*) #6

The most important operation is at %4:

1
2
3
4
5
6
# Call new to obtain the memory address
%3 = call i8* @_Znwy(i64 1) #7
# Convert the address obtained to struct.A*
%4 = bitcast i8* %3 to %struct.A*
# Store the converted address as the declared pointer
store %struct.A* %4, %struct.A** %2, align 8

Tests show that overloading operator new within a class also causes the conversion.

1
2
3
4
5
6
7
8
9
struct A{
void* operator new(std::size_t x){ return malloc(x); }
};

int main()
{
A *z=new A;
delete z;
}

The LLVM-IR for this code is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
define i32 @main() #4 {
%1 = alloca i32, align 4
%2 = alloca %struct.A*, align 8
store i32 0, i32* %1, align 4
%3 = call i8* @_ZN1AnwEy(i64 1)
%4 = bitcast i8* %3 to %struct.A*
store %struct.A* %4, %struct.A** %2, align 8
%5 = load %struct.A*, %struct.A** %2, align 8
%6 = icmp eq %struct.A* %5, null
br i1 %6, label %9, label %7

; <label>:7: ; preds = %0
%8 = bitcast %struct.A* %5 to i8*
call void @_ZdlPv(i8* %8) #6
br label %9

; <label>:9: ; preds = %7, %0
%10 = load i32, i32* %1, align 4
ret i32 %10
}

; Function Attrs: uwtable
define linkonce_odr i8* @_ZN1AnwEy(i64) #0 comdat align 2 {
%2 = alloca i64, align 8
store i64 %0, i64* %2, align 8
%3 = load i64, i64* %2, align 8
%4 = call i8* @malloc(i64 %3)
ret i8* %4
}

; Function Attrs: nobuiltin nounwind
declare void @_ZdlPv(i8*) #5

declare i8* @malloc(i64) #1

As observed, the conversion from void* to struct.A* still occurs at %4.
I haven’t found an explicit requirement in the standard stating that operator new should convert void* to T*, but the standard mentions the following description:

The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).

The article is finished. If you have any questions, please comment and communicate.

Scan the QR code on WeChat and follow me.

Title:operator new:void*到T*的转换
Author:LIPENGZHA
Publish Date:2017/05/22 18:03
World Count:1.3k Words
Link:https://en.imzlp.com/posts/21564/
License: CC BY-NC-SA 4.0
Reprinting of the full article is prohibited.
Your donation will encourage me to keep creating!