Passing a nullptr to a std::string constructor is one of the most common pitfalls in C++. The standard specifies that the constructor taking a const char* has a strict precondition: the pointer must not be null. Violating this precondition results in Undefined Behavior (UB), usually manifesting as a crash when the implementation tries to calculate the string length using strlen(nullptr).

If you're dealing with third-party code or legacy data structures where raw pointers can be null, here is how you can catch this bug during compile-time, via static analysis, and through runtime hardening.

Why AddressSanitizer Didn't Catch It at Compile Time

AddressSanitizer (-fsanitize=address) is an instrumentation tool that works at runtime, not compile-time. While ASan will catch the null pointer dereference when you run the compiled binary, it will not flag it during compilation. Furthermore, if you want clear compile-time alerts or immediate runtime diagnostic messages rather than a generic segmentation fault, you need different tools.

1. Compile-Time Detection: GCC's -fanalyzer

If you are using GCC 10 or newer (such as GCC 13, 14, or 16), GCC includes an interprocedural static analyzer. Adding the -fanalyzer flag allows GCC to trace pointer values through constructors and recognize that a null pointer is being passed to a function that requires a non-null string.

g++ -std=c++23 -O2 -fanalyzer main.cpp -o main

GCC's analyzer will trace the flow from M m{"Hello"} (where field2 defaults to nullptr) to B b{m}, emitting a diagnostic warning indicating a null pointer dereference during standard library construction.

2. Clang Static Analyzer and Clang-Tidy

If your build pipeline uses Clang, the Clang Static Analyzer and Clang-Tidy are excellent at catching this issue:

  • Clang-Tidy: Enable the checker bugprone-string-constructor or clang-analyzer-core.NonNullParamChecker.
  • Scan-Build: Run scan-build make (or scan-build ninja) to catch the path-sensitive null pointer passing into standard library calls.

3. Standard Library Hardening (Fast Runtime Diagnostics)

If static analysis misses complex cases across translation units, you should enable standard library hardening flags during development and testing. These flags check standard library preconditions at runtime and abort execution with a clear, readable message instead of an opaque crash.

For GCC (libstdc++):

Compile with -D_GLIBCXX_ASSERTIONS:

g++ -std=c++23 -D_GLIBCXX_ASSERTIONS -fsanitize=address main.cpp -o main

With libstdc++ assertions enabled, the standard library checks if __s != nullptr inside std::basic_string constructors. Instead of an unhandled segmentation fault, you get a clean error:

/usr/include/c++/v1/string_view: ... assertion '__s != nullptr' failed. Aborted.

For Clang (libc++):

Modern LLVM/libc++ offers hardening modes. Pass the following define:

-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST

4. Idiomatic Fixes in Modern C++

While C++23 explicitly deletes the std::string(std::nullptr_t) constructor overload to catch literal std::string(nullptr) calls at compile time, it cannot prevent a null const char* variable from being passed. To handle incoming raw pointers safely, consider the following design patterns:

Option A: Ternary Null Check Fallback

struct B
{
    std::string field1_s;
    std::string field2_s;

    explicit B(const M& m)
        : field1_s{m.field1 ? m.field1 : ""},
          field2_s{m.field2 ? m.field2 : ""}
    {
    }
};

Option B: Use std::optional<std::string>

If an absent string should semantically represent an unset value rather than an empty string, model it explicitly:

#include <optional>
#include <string>

struct B
{
    std::optional<std::string> field1_s;
    std::optional<std::string> field2_s;

    explicit B(const M& m)
        : field1_s{m.field1 ? std::make_optional(m.field1) : std::nullopt},
          field2_s{m.field2 ? std::make_optional(m.field2) : std::nullopt}
    {
    }
};

Option C: Enforce Non-Null with gsl::not_null

To eliminate the issue at the root, prevent null raw pointers in the source structure using the Guidelines Support Library:

#include <gsl/pointers>

struct M
{
    gsl::not_null<const char*> field1 = "";
    gsl::not_null<const char*> field2 = "";
};

Summary

To prevent and detect null pointer string initialization issues:

  • Use -fanalyzer (GCC) or Clang-Tidy to catch the error statically during your build or CI pipelines.
  • Always build debug and test suites with -D_GLIBCXX_ASSERTIONS to turn undefined behavior into clear assertion failures.
  • Sanitize input pointers with ternary checks or express optionality using std::optional.