← Back to all posts

10 C++ Notes

Char Arrays and String Manipulation

C++ Notes


Char Manipulation

Function Reference

FunctionWhat It Checks or Does
std::isalnum(c)checkReturns non-zero if the character is a letter or a digit (alphanumeric).
std::isalpha(c)checkReturns non-zero if the character is a letter, uppercase or lowercase.
std::isdigit(c)checkReturns non-zero if the character is a decimal digit, 0 through 9.
std::isblank(c)checkReturns non-zero if the character is a space or horizontal tab.
std::isupper(c)checkReturns non-zero if the character is an uppercase letter.
std::islower(c)checkReturns non-zero if the character is a lowercase letter.
std::toupper(c)convertReturns the uppercase version of the character. The original is unchanged, store the result in a new variable.
std::tolower(c)convertReturns the lowercase version of the character. The original is unchanged, store the result in a new variable.

Code Example: main.cpp

C++
#include <iostream>
#include <cctype>

int main() {
    char text[] = "Hey 123!";

    for (char c : text) {
        if (c == '\0') break; // stop at null terminator

        std::cout << "Character: '" << c << "'\n";

        if (std::isalnum(c))
            std::cout << "  isalnum  : yes\n";

        if (std::isalpha(c)) {
            std::cout << "  isalpha  : yes\n";
            char upper = std::toupper(c); // original c is unchanged
            char lower = std::tolower(c);
            std::cout << "  toupper  : '" << upper << "'\n";
            std::cout << "  tolower  : '" << lower << "'\n";
        }

        if (std::isdigit(c))
            std::cout << "  isdigit  : yes\n";

        if (std::isupper(c))
            std::cout << "  isupper  : yes\n";

        if (std::islower(c))
            std::cout << "  islower  : yes\n";

        if (std::isblank(c))
            std::cout << "  isblank  : yes\n";

        std::cout << std::endl;
    }

    return 0;
}

C-String Manipulation

Length

Comparison

Search: First Occurrence

C++
const char* text = "Try not. Do, or do not. There is no try.";
char target = 'T';
const char* result = text; // start from beginning
int count = 0;

while ((result = std::strchr(result, target)) != nullptr) {
    std::cout << "Found at: \"" << result << "\"\n";
    ++result; // move past current match
    ++count;
}
std::cout << "Total: " << count << std::endl;

Search: Last Occurrence

C++
char path[] = "/home/user/hello.cpp";
char* output = std::strrchr(path, '/'); // points to last '/'

if (output)
    std::cout << output + 1 << std::endl; // +1 skips past '/' --> prints: hello.cpp

Code Example: main.cpp

C++
#include <iostream>
#include <cstring>

int main() {
    char stackStr[]       = "Hello, World!";
    const char* literalStr = "Hello, World!";
    char* heapStr          = new char[6]{'H','e','l','l','o','\0'};

    // 1) length
    std::cout << "strlen(stackStr):    " << std::strlen(stackStr)  << std::endl; // prints: 13 (no '\0')
    std::cout << "sizeof(stackStr):    " << sizeof(stackStr)         << std::endl; // prints: 14 (includes '\0')
    std::cout << "sizeof(literalStr):  " << sizeof(literalStr)       << std::endl; // prints: 8 (pointer size, not string)
    std::cout << std::endl;

    // 2) comparison
    int cmp = std::strcmp("ABC", "CBA");
    std::cout << "strcmp(ABC, CBA):         " << cmp << std::endl; // prints: negative
    int ncmp = std::strncmp("ABCDE", "ABCXY", 3);
    std::cout << "strncmp first 3 chars:    " << ncmp << std::endl; // prints: 0 (first 3 match)
    std::cout << std::endl;

    // 3) search first occurrence + count total
    const char* text   = "Try not. Do, or do not. There is no try.";
    char target        = 'T';
    const char* result = text;
    int count          = 0;
    while ((result = std::strchr(result, target)) != nullptr) {
        std::cout << "Found '" << target << "' at: \"" << result << "\"\n";
        ++result; // move past current match to continue searching
        ++count;
    }
    std::cout << "Total occurrences: " << count << std::endl << std::endl;

    // 4) search last occurrence
    char path[] = "/home/user/hello.cpp";
    char* last  = std::strrchr(path, '/');
    if (last)
        std::cout << "Filename: " << last + 1 << std::endl; // prints: hello.cpp

    delete[] heapStr;
    return 0;
}

C-String Concatenation and Copying

Joining Strings (Concatenation)

C++
char a[50]{};        // all elements '\0'
char b[]{"Hello"};  // {'H','e','l','l','o','\0'}
std::strcat(a, b);
std::strcat(a, " World!");
std::cout << a << '\n'; // prints: Hello World!

Copying Strings

C++
char a[50]{};
char b[]{"Hello"};
std::strcpy(a, b);
std::cout << a << '\n'; // prints: Hello

// strncpy: manual null termination needed
char e[50]{};
std::strncpy(e, "HelloWorld", 5); // copies only "Hello", no '\0' guaranteed
e[5] = '\0';                      // manual null termination

Code Example: main.cpp

C++
#include <iostream>
#include <cstring>

int main() {
    // strcat: append src to dest
    char a[50]{};
    char b[]{"Hello"};
    std::strcat(a, b);             // a = "Hello"
    std::strcat(a, " World");      // a = "Hello World"
    std::cout << "strcat  : " << a << '\n'; // prints: Hello World

    // strncat: append at most n characters
    char c[50]{"C++ "};
    std::strncat(c, "Programming Language", 11); // appends only "Programming"
    std::cout << "strncat : " << c << '\n'; // prints: C++ Programming

    // strcpy: copy src into dest (overwrites completely)
    char d[50]{};
    std::strcpy(d, "Copy this");
    std::cout << "strcpy  : " << d << '\n'; // prints: Copy this

    // strncpy: copy at most n characters, manual '\0' needed
    char e[50]{};
    std::strncpy(e, "HelloWorld", 5); // copies "Hello", no '\0' guaranteed
    e[5] = '\0';                      // manual null termination required
    std::cout << "strncpy : " << e << '\n'; // prints: Hello

    return 0;
}

std::string

Why std::string

C-style char arrays come with a real list of problems: size must be manually kept in check, you must always work within bounds explicitly, you must track the null terminator '\0' manually, and many C-string functions don't perform any bounds checking at all.

Declaration Forms

Requires #include <string>.

C++
std::string name;                  // empty string, no garbage, size = 0
std::string name{"Hassan Ali"};    // initialized from string literal
std::string copy{name};            // copies full contents of name
std::string first{name, 6};        // copies first 6 characters ("Hassan")
std::string repeated(4, 'i');      // "iiii" (note: parentheses, not braces)
std::string part{name, 7, 3};      // from index 7, copy 3 chars ("Ali")

std::string msg{"Hello "};         // must start with std::string, not literal
msg += name;                       // appends name
msg += "\nHow are you";           // appends more text

The repeated-character form uses parentheses, (4, 'i'), not braces. Using braces {4, 'i'} would be interpreted as an initializer list of two chars, ASCII 4 and ASCII 105, which is not what you want at all.


Code Example: main.cpp

C++
#include <iostream>
#include <string>

int main() {
    std::string name;                      // empty string
    std::cout << "name      : \"" << name << "\"\n"; // prints: ""

    std::string full{"Hassan Ali"};
    std::cout << "full      : " << full << '\n';       // prints: Hassan Ali

    std::string copy{full};                // copy constructor
    std::cout << "copy      : " << copy << '\n';       // prints: Hassan Ali

    std::string first{full, 6};            // from index 0, take 6 chars
    std::cout << "first     : " << first << '\n';      // prints: Hassan

    std::string repeated(4, 'i');          // 4 copies of 'i'
    std::cout << "repeated  : " << repeated << '\n';   // prints: iiii

    std::string part{full, 7, 3};          // from index 7, take 3 chars
    std::cout << "part      : " << part << '\n';       // prints: Ali

    // concatenation
    std::string msg{"Hello "};
    msg += full;
    msg += "\nHow are you";
    std::cout << "msg       : " << msg << '\n';        // prints: Hello Hassan Ali\nHow are you

    return 0;
}

Closing

Everything in this post, from <cctype> checks to manual strcat bookkeeping, is the exact set of headaches std::string exists to make disappear. Worth knowing the raw mechanics, worth defaulting to std::string anyway. Next up, functions: the compilation model, argument passing, and overloading.

← Previous 09 - References Next → 11 - Functions, Part 1: Basics and Overloading