---
title: "What’s in a cstring_view?"
document: P4227R0
date: 2026-05-11
reply-to:
  - "< <cpp@andreas-weis.net>"
audience: LEWG Library Evolution
paper-type: proposal
---

In C, a `’\0’` in a char array marks the end of the string. A C string is not just a string that has a `’\0’` at the end. It’s a string that *ends* *at* *the* *first* `’\0’` *character*.

In particular a `cstring``_``view` with embedded NUL bytes will cause friction in all contexts that rely on either the size, or the position of the end of the string.

Consider the following examples:

```cpp
FILE* open_txt_file(std::cstring_view filename) {
  if (filename.ends_with(".txt")) {
    return fopen(filename.c_str(), "rw");
  } else {
    auto const filename_with_ext = std::string{ filename } + ".txt";
    return fopen(filename_with_ext.c_str(), "rw");
  }
}
```

This function will not work correctly with inputs like `"hello\0.txt"`. The reason is that the check inside the `if` has a different notion of the end of the string than `fopen`.

It is likely that every use of `cstring``_``view`’s `ends``_``with` will be an instance of such a logic bug. If `cstring``_``view` allows embedded NUL bytes, it probably should not have an `ends``_``with` function.

```cpp
    // password is too short
    return false;
  }
  set_password(password.c_str());
  return true;
}
```

This function is supposed to reject passwords that are too short, but will accept inputs like `"123\0I-tricked-you!Hahaha!"`.

One might argue that, in practice, crypto APIs like OpenSSL’s `EVP``_``DigestUpdate()` commonly require passing the size explicitly. But such APIs also do not require a terminating `’\0’` at the end of the string, so `string``_``view` or even `span` are suitable for interacting with such APIs. If a user is dealing with an API that requires the use of `cstring``_``view`, they will be susceptible to this kind of bug.

```cpp
char* create_string_copy(std::cstring_view source_string) {
  char* new_string = malloc(source_string.size());
  strcpy(new_string, source_string.c_str());
  return new_string;
}
```

This function will allocate too much memory for its intended purpose. To mitigate this, an implementation must not rely on `cstring``_``view`’s `size()` function. A function which, according to P3655, is one of the key motivations for introducing this type in the first place.

Conceptually, C APIs don’t consider anything past the first NUL byte to be part of the string. A type whose primary purpose is interacting with such APIs should have the same notion of the contents of a string.

### 3

They do.

But unlike `cstring``_``view`, they were not designed for interop with C functions. Consistency with the existing string types is at odds with allowing smooth interop with C APIs.

### 4

P3655 acknowledges that C APIs that allow embedded NUL bytes are rare.

The author is aware of two types of C APIs that are able to process such strings correctly.

1. APIs accepting an explicit `size` argument. Such APIs do not rely on the terminating NUL byte to detect the end of the string. However, because of this, they also do not *require* the string to be null terminated. `string``_``view` and `span` already sufficient for interacting with such APIs.

2. APIs that require “double-null-terminated strings”. Such strings are commonly used in the Win32 API[1][2], but are also rarely encountered in other places[3]. A double-nullterminated string is really a list of strings, where each individual string is terminated by a

## `REFERENCES`

While there may be use cases for a type that represents a string that is both null-terminated *and* may contain embedded NUL bytes, those uses are much rarer and sufficiently different that, if supporting them through the standard library is desired, they should be handled by a different library type.

For example a `cezstring``_``view` (pronounced *see-zee*, rhymes with sleazy, because it kind of is) could be added alongside `cstring``_``view` for this purpose. Such a type would primarily address the needs of users that are concerned about the runtime overhead required to enforce the absence of embedded NUL bytes for `cstring``_``view`. Such a type should omit the problematic `ends``_``with` function. The distinction between `cstring``_``view` and `cezstring``_``view` makes it clear which properties regarding its size a user can rely on when passing a value on to a C API.

## References

[P3655] *P3655R4* *-* `std::cstring``_``view` `https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3655r4.html`

| [1] | `REG``_``MULTI``_``SZ` *Registry* *value* *type* | `https://learn.microsoft.com/en-us/windows/win32/sysinfo/` | `registry-value-types` |
| --- | --- | --- | --- |
| [2] | `lpstrFilter` *field* *in* `OPENFILENAMEA` *struct* | `https://learn.microsoft.com/en-us/windows/win32/api/commdlg/` | `ns-commdlg-openfilenamea` |

[3] *proc_pid_environ(5)* *—* *Linux* *manual* *page* `https://man7.org/linux/man-pages/man5/proc``_``pid``_``environ.5.html`
