I have a question about C-style string symbolic constants and dynamically allocating arrays.
const char** name = new const char* { "Alan" };
delete name;
when I try to delete name after new'ing a piece of memory, the compiler suggest to me to use delete instead of delete[]. I understand name only stores the address of the pointer to the only-read string.
However, if I only delete the pointer to pointer (which is name), will the string itself cause a memory leak?
As the comments above indicate, you don't need to manage the memory that "Alan" exists in.
Let's see what that looks like in practice.
I made a modified version of your code:
#include <iostream>
void test() {
const char** name;
name = new const char* { "Alan\n" };
delete name;
}
int main()
{
test();
}
and then I popped it into godbolt and it shows what's happening under the hood. (excerpts copied below)
In both clang and gcc, the memory that stores "Alan\n" is in static memory so it always exists. This is how it creates no memory leak even though you never touch it again after mentioning it. The value of the pointer to "Alan\n" is just the position in the program's memory, offset .L.str or OFFSET FLAT:.LC0.
clang:
test(): # #test()
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 8
call operator new(unsigned long)
mov rcx, rax
movabs rdx, offset .L.str
mov qword ptr [rax], rdx
mov qword ptr [rbp - 8], rcx
mov rax, qword ptr [rbp - 8]
cmp rax, 0
mov qword ptr [rbp - 16], rax # 8-byte Spill
je .LBB1_2
mov rax, qword ptr [rbp - 16] # 8-byte Reload
mov rdi, rax
call operator delete(void*)
.L.str:
.asciz "Alan\n"
gcc:
.LC0:
.string "Alan\n"
test():
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 8
call operator new(unsigned long)
mov QWORD PTR [rax], OFFSET FLAT:.LC0
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
test rax, rax
je .L3
mov esi, 8
mov rdi, rax
call operator delete(void*, unsigned long)
Related
I was playing with www.godbolt.org to check what code generates better assembly code, and I can't understand why this two different approaches generate different results (in assembly commands).
The first approach is to declare a string, and then later set a value:
#include <string>
int foo() {
std::string a;
a = "abcdef";
return a.size();
}
Which, in my gcc 7.4 (-O3) outputs:
.LC0:
.string "abcdef"
foo():
push rbp
mov r8d, 6
mov ecx, OFFSET FLAT:.LC0
xor edx, edx
push rbx
xor esi, esi
sub rsp, 40
lea rbx, [rsp+16]
mov rdi, rsp
mov BYTE PTR [rsp+16], 0
mov QWORD PTR [rsp], rbx
mov QWORD PTR [rsp+8], 0
call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
mov rdi, QWORD PTR [rsp]
mov rbp, QWORD PTR [rsp+8]
cmp rdi, rbx
je .L1
call operator delete(void*)
.L1:
add rsp, 40
mov eax, ebp
pop rbx
pop rbp
ret
mov rbp, rax
jmp .L3
foo() [clone .cold]:
.L3:
mov rdi, QWORD PTR [rsp]
cmp rdi, rbx
je .L4
call operator delete(void*)
.L4:
mov rdi, rbp
call _Unwind_Resume
So, I imagined that if I initialize the string in the declaration, the output assembly would be shorter:
int bar() {
std::string a {"abcdef"};
return a.size();
}
And indeed it is:
bar():
mov eax, 6
ret
Why this huge difference? What prevents gcc to optimize the first version similar to the second?
godbolt link
This is just a guess:
operator= has a strong exception guarantee; which means:
If an exception is thrown for any reason, this function has no effect (strong exception guarantee).
(since C++11)
(source)
So while the constructor can leave the object in any condition it likes, operator= needs to make sure that the object is the same as before; I suspect that's why the call to operator delete is there (to clean up potentially allocated memory).
Here a piece of C++ code.
In this example, many code blocks look like constructor calls.
Unfortunately, block code #3 is not (You can check it using https://godbolt.org/z/q3rsxn and https://cppinsights.io).
I think, it is an old C++ notation and it could explain the introduction of the new C++11 construction notation using {} (cf #4).
Do you have an explanation for T(i) meaning, so close to a constructor notation, but definitely so different?
struct T {
T() { }
T(int i) { }
};
int main() {
int i = 42;
{ // #1
T t(i); // new T named t using int ctor
}
{ // #2
T t = T(i); // new T named t using int ctor
}
{ // #3
T(i); // new T named i using default ctor
}
{ // #4
T{i}; // new T using int ctor (unnamed result)
}
{ // #5
T(2); // new T using int ctor (unnamed result)
}
}
NB: thus, T(i) (#3) is equivalent to T i = T();
The statement:
T(i);
is equivalent to:
T i;
In other words, it declares a variable named i with type T. This is because parentheses are allowed in declarations in some places (in order to change the binding of declarators) and since this statement can be parsed as a declaration, it is a declaration (even though it might make more sense as an expression).
You can use Compiler Explorer to see what happens in assembler.
You can see that #1,#2 #4 and #5 do same thing but strangly #3 call the other constructor (the base object constructor).
Does anyone have an explanation?
Assembler code :
::T() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
nop
pop rbp
ret
T::T(int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov DWORD PTR [rbp-12], esi
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 42
// #1
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-7]
mov esi, edx
mov rdi, rax
call T::T(int)
// #2
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-8]
mov esi, edx
mov rdi, rax
call T::T(int)
// #3
lea rax, [rbp-9]
mov rdi, rax
call T::T() [complete object constructor]
// #4
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-6]
mov esi, edx
mov rdi, rax
call T::T(int)
// #5
lea rax, [rbp-5]
mov esi, 2
mov rdi, rax
call T::T(int)
mov eax, 0
leave
ret
I have the following code of a thread_local singleton:
struct Singl {
int& ref;
Singl(int& r) : ref(r) {}
~Singl() {}
void print() { std::cout << &ref << std::endl; }
};
static auto& singl(int& r) {
static thread_local Singl i(r);
return i;
}
int main() {
int x = 4;
singl(x).print();
int y = 55;
singl(y).print();
return 0;
}
This program prints twice the reference to x.
The compiler (gcc 8.1 on godbolt) seems to do a lazy initialization of the singleton object:
singl(int&):
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:guard variable for singl(int&)::i#tpoff
movzx eax, BYTE PTR [rax]
test al, al
jne .L5
mov rax, QWORD PTR [rbp-8]
mov rdx, QWORD PTR fs:0
add rdx, OFFSET FLAT:singl(int&)::i#tpoff
mov rsi, rax
mov rdi, rdx
call Singl::Singl(int&)
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:guard variable for singl(int&)::i#tpoff
mov BYTE PTR [rax], 1
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:singl(int&)::i#tpoff
mov edx, OFFSET FLAT:__dso_handle
mov rsi, rax
mov edi, OFFSET FLAT:_ZN5SinglD1Ev
call __cxa_thread_atexit
.L5:
mov rax, QWORD PTR fs:0
add rax, OFFSET FLAT:singl(int&)::i#tpoff
leave
ret
Is this the default behaviour I can expect whenever I make multiple calls to the singl-function passing different arguments? Or is it possible that the singleton object might be initialized a second time on a subsequent call?
This is indeed guaranteed. static/thread_local local variables are initialized exactly once, when control reaches the declaration.
A few points to take note of:
If multiple threads are calling the function concurrently, only one will perform the initialization, and the others will wait. This is what the guard variables in the disassembly is doing.
If the initialization throws an exception, it is considered incomplete, and will be performed again the next time control reaches it.
In other words, they _just work_™.
I'm new to assembly and I'm trying to figure out how C++ handles dynamic dispatch in assembly.
When looking through assembly code, I saw that there were 2 unusual calls:
call _Znwm
call _ZdlPv
These did not have a subroutine that I could trace them to. From examining the code, Znwm seemed to return the address of the object when its constructor was called, but I'm not sure about that. ZdlPv was in a block of code that could never be reached (it was jumped over).
C++:
Fruit * f;
f = new Apple();
x86:
# BB#1:
mov eax, 8
mov edi, eax
call _Znwm
mov rdi, rax
mov rcx, rax
.Ltmp6:
mov qword ptr [rbp - 48], rdi # 8-byte Spill
mov rdi, rax
mov qword ptr [rbp - 56], rcx # 8-byte Spill
call _ZN5AppleC2Ev
Any advice would be appreciated.
Thanks.
_Znwm is operator new.
_ZdlPv is operator delete.
I was always under the impression that a std::unique_ptr had no overhead compared to using a raw pointer. However, compiling the following code
#include <memory>
void raw_pointer() {
int* p = new int[100];
delete[] p;
}
void smart_pointer() {
auto p = std::make_unique<int[]>(100);
}
with g++ -std=c++14 -O3 produces the following assembly:
raw_pointer():
sub rsp, 8
mov edi, 400
call operator new[](unsigned long)
add rsp, 8
mov rdi, rax
jmp operator delete[](void*)
smart_pointer():
sub rsp, 8
mov edi, 400
call operator new[](unsigned long)
lea rdi, [rax+8]
mov rcx, rax
mov QWORD PTR [rax], 0
mov QWORD PTR [rax+392], 0
mov rdx, rax
xor eax, eax
and rdi, -8
sub rcx, rdi
add ecx, 400
shr ecx, 3
rep stosq
mov rdi, rdx
add rsp, 8
jmp operator delete[](void*)
Why is the output for smart_pointer() almost three times as large as raw_pointer()?
Because std::make_unique<int[]>(100) performs value initialization while new int[100] performs default initialization - In the first case, elements are 0-initialized (for int), while in the second case elements are left uninitialized. Try:
int *p = new int[100]();
And you'll get the same output as with the std::unique_ptr.
See this for instance, which states that std::make_unique<int[]>(100) is equivalent to:
std::unique_ptr<T>(new int[100]())
If you want a non-initialized array with std::unique_ptr, you could use1:
std::unique_ptr<int[]>(new int[100]);
1 As mentioned by #Ruslan in the comments, be aware of the difference between std::make_unique() and std::unique_ptr() - See Differences between std::make_unique and std::unique_ptr.