Menu
How Strings Work in C++

How Strings Work in C++

How Strings Work in C++ (and how to use them)

Video Link: C++ Strings

Playlist Link: C++ Tutorial Playlist by The Cherno

📌 Prerequisites (Mentioned at 0:06)

  • It’s helpful to have watched the previous videos on:
    • Pointers (Episode 8)
    • Arrays (Episode 9)
    • Why? Because strings are closely related to both concepts.

🤔 What is a String? (General Concept) (0:21)

  • A string is essentially a sequence or group of characters.
  • Characters can be:
    • Letters (a-z, A-Z)
    • Numbers (0-9)
    • Symbols (!, @, #, $, %, etc.)
  • Basically, a string represents text.
  • Representing text is fundamental in programming for user interfaces, file handling, data processing, etc.

💡 How Strings Work: The Basics (1:08)

  • To understand strings, you first need to understand how characters work.
  • Characters (char) in C++: (1:53)
    • The char data type is used.
    • Typically, char is 1 byte (8 bits) of memory.
    • This 1-byte size is often used for low-level memory manipulation (like pointer arithmetic in bytes or creating memory buffers).
  • Character Encoding: (2:00)
    • How characters (letters, symbols) are mapped to numbers (and thus, bytes).
    • ASCII is the most basic standard, using 1 byte (technically 7 bits, extended ASCII uses 8). It can represent 256 different characters (0-255).
    • This is enough for English letters (uppercase/lowercase), numbers, and common symbols.
    • Limitation: ASCII cannot represent characters from many other languages (e.g., Japanese, Chinese, Russian, Korean) because there are far more than 256 unique characters globally. (3:22)
    • Other encodings exist (UTF-8, UTF-16, UTF-32 - part of Unicode) to handle this, often using more than 1 byte per character. This video focuses on the basic ASCII/char concept.

⛓️ C-Style Strings: Character Pointers & Arrays (2:59)

  • Traditionally (in C, and still usable in C++), strings are represented as arrays of characters, often manipulated via pointers.
  • String Literals: When you write text in double quotes like "Cherno", this is a string literal.
    • Example: const char* name = "Cherno";
    • name is a pointer (char*) pointing to the first character (‘C’) of a sequence of characters stored somewhere in the program’s memory.
    • This memory location for string literals is often read-only.
  • Why const? (5:54)
    • Because string literals are often in read-only memory, trying to modify them can cause a crash.
    • Declaring the pointer as const char* makes the data pointed to constant (read-only from the perspective of this pointer). This helps prevent accidental modification attempts.
    • Example of what NOT to do:
      char* name = "Cherno"; // Problematic: Assigning literal to non-const pointer
      name[2] = 'a'; // CRASH! Trying to write to read-only memory.
      
  • Null Termination: (8:07)
    • How does the program know where a C-style string ends if it only has a pointer to the start?
    • Answer: A special null terminator character (\0) is automatically added at the end of string literals.
    • This character has an ASCII value of 0.
    • Functions working with C-style strings (like printing) read characters sequentially from the starting address until they encounter this null byte (value 0).
    • Memory View: "Cherno" in memory looks like C h e r n o \0 (represented by their byte values, with the last one being 00). (7:21)
  • Manual Char Arrays: (8:48)
    • You can create strings using explicit char arrays.
    • Example (Incorrect - Missing Null Terminator):
      // Array size exactly matches visible characters
      char name2[6] = {'C', 'h', 'e', 'r', 'n', 'o'};
      std::cout << name2 << std::endl; // Prints "Cherno" + GARBAGE until a 0 byte is found in memory
      
    • Example (Correct - With Null Terminator):
      // Array size needs to be N+1 for N characters + null terminator
      char name2[7] = {'C', 'h', 'e', 'r', 'n', 'o', 0}; // Using integer 0
      // OR
      // char name2[7] = {'C', 'h', 'e', 'r', 'n', 'o', '\0'}; // Using null character literal
      std::cout << name2 << std::endl; // Prints "Cherno" correctly
      
    • Key takeaway: If manually creating char arrays for strings, always allocate one extra byte for the null terminator and ensure it’s set to 0 or \0.

✨ The C++ std::string Way (11:03)

  • C++ provides a much more convenient and safer way to handle strings via the Standard Library.
  • Include Header:
    #include <string>
    
  • Usage:
    #include <string>
    #include <iostream>
    
    int main() {
        std::string name = "Cherno"; // Initialize directly from string literal
        std::cout << name << std::endl;
    }
    
  • How it Works:
    • std::string is a class that manages string data.
    • Internally, it likely uses a dynamically allocated character array (on the heap).
    • It handles memory management (allocation, resizing, deallocation) automatically.
    • It automatically manages the null terminator when needed.
  • Advantages:
    • Easier: Simpler syntax, less manual memory management.
    • Safer: Reduces risks of buffer overflows and memory errors common with C-style strings.
    • Functionality: Provides many useful member functions (e.g., finding length, appending, searching).
  • Concatenation (Appending Strings): (14:08)
    • Problem: std::string name = "Cherno" + " hello!"; // ERROR!
      • Reason: Both "Cherno" and " hello!" are const char* (C-style string literals/pointers). C++ doesn’t define how to add two const char* pointers together in this context.
    • Solution 1 (Using +=):
      std::string name = "Cherno";
      name += " hello!"; // Appends " hello!" to the existing string 'name'
      std::cout << name << std::endl; // Outputs "Cherno hello!"
      
      • This works because std::string overloads the += operator to accept const char*.
    • Solution 2 (Explicit std::string Conversion):
      std::string name = std::string("Cherno") + " hello!"; // Create std::string first
      std::cout << name << std::endl; // Outputs "Cherno hello!"
      
      • This works because std::string("Cherno") creates a std::string object. The + operator is overloaded for std::string + const char*.
  • Finding Substrings: (15:14)
    • Use the .find() member function.
    • Example:
      std::string name = "Cherno hello!";
      bool contains = name.find("no") != std::string::npos;
      // std::string::npos is a special static member value indicating "not found"
      // If name.find("no") returns anything other than npos, "no" was found.
      if (contains) {
          std::cout << "Found 'no'!" << std::endl;
      }
      
  • Getting Size/Length: (13:45)
    • Use the .size() member function.
    • Example:
      std::string name = "Cherno";
      std::cout << name.size() << std::endl; // Outputs 6
      
    • This is much easier than using C-style functions like strlen().

👉 Passing Strings to Functions (16:13)

  • How should you pass std::string objects to functions?
  • Option 1: Pass by Value (Generally BAD for strings)
    void PrintString(std::string str) { // Pass by value
        std::cout << str << std::endl;
    }
    // Calling: PrintString(myString);
    
    • Problem: This creates a copy of the entire string every time the function is called.
    • Copying involves allocating new memory on the heap and copying all the character data. This is inefficient and slow, especially for long strings or frequent function calls.
  • Option 2: Pass by Constant Reference (Usually BEST) 👍
    void PrintString(const std::string& str) { // Pass by constant reference
        // str += "h"; // ERROR: Cannot modify because of 'const'
        std::cout << str << std::endl;
    }
    // Calling: PrintString(myString);
    
    • &: Passes a reference (an alias to the original string, avoids copying).
    • const: Prevents the function from modifying the original string (makes it read-only within the function scope).
    • Benefit: Highly efficient (no copying) and safe (prevents accidental modification).
    • Rule of Thumb: If a function only needs to read a string, pass it by const std::string&.

🙏 Conclusion (17:54)

  • Strings are fundamental for text representation.
  • C-style strings (char*, char arrays) require manual null termination and careful memory management.
  • C++ std::string provides a safer, easier, and more functional way to work with strings.
  • Use std::string whenever possible in C++.
  • Pass strings by const reference (const std::string&) to functions when you don’t need to modify them, for efficiency.
  • The video style was more conversational; feedback is welcome.
  • Support on Patreon (patreon.com/TheCherno) helps create more content and gives access to community features (Slack/Discord).

For more details on specific std::string functions, consult a C++ reference like cppreference.com.