In Visual Studio, I can compile my code using /MT or /MD [MSDN]. I think I don’t understand all the implications that those options have.
My current understanding is:
- (A) the "multi-threading" part which MSDN talks about has nothing to do with my application. My application can still be single-threaded. It’s the C runtime which is capable of multi-threading. I understand that as "the C runtime uses synchronization around
new
in order to guarantee that multiple threads don’t run into a data race when generating pointers". There may have been a single-thread version in the past. - (B) although these are compiler options, this is technically more like a linking thing.
/MD
is the dynamic "linking" and/MT
is the static "linking". - (C)
/MD
will result in a smaller executable, but will loaducrtbase.dll
, which then loads the C runtime DLLs, e.g.VCRUNTIME140.dll
,VCRUNTIME140_1.dll
andMSVCP140.dll
(or their debug versions). I need a redistributable version of these when I create my installer (vcredist [MSDN]). - (D)
/MT
will result in a larger executable, but contains the C runtime directly, so that no additional DLLs need to be loaded. - (E) if I have a large application (e.g. 5MLoC, 100 own DLLs), choosing
/MD
is beneficial, since each individual DLL is smaller and the above mentioned DLLs are only loaded into the process once. So basically, I should compile at least all DLLs with/MD
. - (F) the debug versions
/Mxd
of the C runtime use a different memory layout and e.g. use 0xCCCCCCCC as fill patterns to detect buffer overflows/underruns. - (G) I can’t mix
/MD
andMT
in a compilation step. - (H)
/MD
defines_DLL
and programs could do all sorts of#ifdef _DLL
magic, but probably they shouldn’t. One such example iserrno
which can be a simple extern int (for /MT) but a function call (for /MD). - (J) with
/MT
, each module gets its own CRT heap, so memory must be freed in the module it was allocated. (Source [MSDN] and [Source [MSDN]](Potential Errors Passing CRT Objects Across DLL Boundaries))
Is above understanding correct? Did I miss an important point?
My aim is to understand (J). I want to make sure I understood the rest of the topic on my way there.
I have read
- the MSDN reference above
- What does MT and MD stand for?
- Mixing code compiled with /MT and /MD (which you can’t; already stated in the MSDN reference)
- Why is runtime library a compiler option rather than a linker option?
- Resolving LNK4098: defaultlib ‘MSVCRT’ conflicts with
- What difference does /MD, /MT make when compiling object files?
- Should I compile with /MD or /MT?
2
Answers
/MT and /MD don’t relate to multi-threaded nature, but how the runtime is provided (as a statically linked library, or as a DLL).
Since Visual Studio 2005, that has been the case.
/MT provides a larger executable, with limited dependencies. /MD provides a smaller executable, but needs a DLL installed.
Using /MT for an installer makes sense. /MD can (depending on Visual Studio version get an automatically upgraded DLL when bugs are found.
I believe your list is correct.
Regarding (A): There are a few functions, like
strtok()
from the C runtime library, that persist some state between calls. The multithreaded versions of the runtime libraries use thread local storage for that state. If your program has two threads using any of these functions, they won’t be coupled in surprising ways.Regarding (E): Yes, if your product has many DLLs, you’re probably better off using
/MD
for a variety of reasons. Note that you almost certainly should do that for your executable and all of the DLLs. Mixing and matching can be lead to very difficult-to-diagnose bugs. You seem to acknowledge that in (G). (Obviously, the OS DLLs and facilities like COM work with programs that are compiled with either/MT
or/MD
. That’s made possible by having strict rules about what happens across DLL boundaries (e.g., using handles).Regarding (F): The debug versions have several features to help uncover bugs. The memory fill patterns and guards are the best known. I’ve found that the debug implementations of some C++ library features are more likely to catch mistakes than the non-debug versions. For example, if you search or sort with a predicate that doesn’t properly implement strict-weak ordering, you’re more likely to crash when compiled against the debug libraries. I believe the debug versions also help detect some instances of iterator invalidation. Note that there’s a performance cost: So some algorithm implementations in the debug libraries don’t comply with the running-time requirements of the standard, but their counterparts in the release libraries should.
Regarding (J): That is correct, at least in general. Small allocations through CRT functions like
malloc()
will typically be carved out of a larger allocation from the OS-provided heap, and each "instance" of the library will have its own data structures for tracking those allocations. Large allocations, however, may bypass the CRT and be satisfied by the process’s default heap. In that case, it’s possible that afree()
from a different DLL than the one that allocated the block might work. Of course, you shouldn’t depend on that. I’m just pointing it out for those who might experiment and get confused when it appears to work.Unless I’m making a single executable, and I want it to be as self-contained as possible, I use
/MD
.