← C++, End to End

C++, End to End

Constants, Literals, and Strings

const and constexpr both promise a value won't change after initialization, but they promise it at different times, and mixing them up leads to real confusion about what the compiler can and can't do with a value.

c++
const int a = 5;        // cannot be reassigned; a's value might still only be known at runtime
constexpr int b = 5;    // cannot be reassigned, AND must be computable at compile time
c++ · live, editable, runnableOpen in Compiler Explorer ↗

Reach for const by default on any variable you don't intend to modify after initialization, which in well-written C++ is most of them: it documents intent, and it lets the compiler catch an accidental reassignment as an error instead of a silent bug. Reach for constexpr specifically when the value genuinely is known at compile time and you want the compiler to be able to use it in contexts that require that (array sizes, template arguments, other constexpr computations).

const and pointers/references: a preview

const's placement relative to * matters, and this trips up nearly everyone the first time. Full treatment of pointers and references, and this exact rule, gets its own chapter later; for now, just the shape of the rule:

c++
const int* p1;   // pointer to a const int: can't modify *p1, CAN reassign p1 to point elsewhere
int* const p2 = &x;  // const pointer to int: CAN modify *p2, can't reassign p2 itself
const int* const p3 = &x;  // both: neither the pointee nor the pointer can change

Read it right to left from the variable name: p1 is "a pointer to a const int," p2 is "a const pointer to int." That reading trick generalizes to much hairier declarations later on.

std::string versus C-style strings

A C-style string is just a pointer to the first char of a sequence terminated by a null byte ('\0'), and it inherits every sharp edge that comes with manual memory and manual bounds (a full treatment of those specific footguns, buffer overflows and off-by-one termination bugs, already lives in this site's C course, since C strings are actually a C concept C++ inherited). C++ gives you a real alternative: std::string, from <string>, which owns its own memory, tracks its own length, resizes itself automatically, and is the type you should reach for by default.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

A few operations worth knowing immediately because they come up constantly: comparing two strings with ==, concatenating with + or +=, checking emptiness with .empty() (faster and clearer than checking .length() == 0), and .substr(start, count) for extracting a piece.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

std::string can still hand you a raw C-style string when an older API demands one, via .c_str(), which returns a pointer valid only as long as the std::string itself isn't modified or destroyed afterward. That's a real lifetime hazard worth remembering, but it's an edge case you reach for, not the default way of working with text.

String literals and raw strings

A string literal in double quotes is, by default, a const char array (a C-style string) under the hood, not a std::string, even though C++ makes it easy to construct a std::string from one. Two conveniences worth knowing:

c++
using namespace std::string_literals;

auto s = "hello"s;   // the s suffix makes this a real std::string literal, not a const char*

And raw string literals (R"(...)") turn off escape-sequence processing entirely, which is a genuine relief for anything containing backslashes or quotes, regex patterns and file paths especially.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

Try it yourself: write a function that takes a std::string and returns it with the first letter uppercased (std::toupper from <cctype> handles one char at a time). Then write a small main that reads a name with std::getline (not std::cin >>, which stops at the first whitespace) and prints it back capitalized.