본문으로 건너뛰기

Strings, Vectors, and Arrays

Built-in Type: A type defined directly by the C++ language.

Library Type: A type provided by the C++ standard library.

string: A variable-length sequence of characters.

vector: A variable-length sequence of objects of the same type.

Array: A built-in fixed-size sequence of elements.


3.1 Namespace using Declarations

Namespace: A scope used to organize names.

std Namespace: Contains the names defined by the C++ standard library.

Scope Operator ::: Accesses a name inside a scope.

std::cout << "Hello\n";

Here, cout is accessed from the std namespace.

using Declaration

using Declaration: Makes one namespace member directly accessible without repeatedly writing the namespace name.

Syntax:

using namespace_name::name;

Example:

#include <iostream>

using std::cin;
using std::cout;
using std::endl;

int main()
{
int a = 0;
int b = 0;

cin >> a >> b;

cout << a + b << endl;
}

Each using declaration introduces one name.

using std::cin;
using std::cout;
using std::endl;

Headers and using

Headers should ordinarily not contain using declarations because every source file that includes the header would also receive those declarations.

Prefer qualified names in headers:

std::string name;

rather than introducing standard-library names globally.

Required Headers

Each library feature requires its corresponding header.

#include <iostream>
#include <string>
#include <vector>

3.2 Library string Type

std::string: A standard-library type representing a variable-length sequence of characters.

Required header:

#include <string>

1) Defining and Initializing strings

A string can be initialized in several ways.

#include <string>

std::string s1;
std::string s2 = "hello";
std::string s3(s2);
std::string s4(5, 'a');

The resulting values are:

s1 = ""
s2 = "hello"
s3 = "hello"
s4 = "aaaaa"

Copy Initialization

Copy Initialization: Uses = when initializing an object.

std::string s = "hello";

Direct Initialization

Direct Initialization: Initializes the object directly using parentheses.

std::string s("hello");

Multiple constructor arguments require direct initialization.

std::string s(5, 'x');

This creates:

xxxxx

2) Operations on strings

Important string operations include:

OperationMeaning
os << sWrites s
is >> sReads one whitespace-separated word
getline(is, s)Reads an entire line
s.empty()Tests whether s is empty
s.size()Returns the number of characters
s[n]Accesses character n
s1 + s2Concatenates strings
s1 = s2Assigns one string to another
s1 == s2Tests equality
s1 != s2Tests inequality

Reading a string

The input operator reads until whitespace.

#include <iostream>
#include <string>

int main()
{
std::string word;

std::cin >> word;

std::cout << word << '\n';
}

Input:

Hello World

Result:

Hello

Only the first word is read.

Reading Multiple Strings

A stream can be used as a loop condition.

std::string word;

while (std::cin >> word)
{
std::cout << word << '\n';
}

The loop continues while input succeeds.

getline

getline reads an entire line, including spaces.

std::string line;

while (std::getline(std::cin, line))
{
std::cout << line << '\n';
}

Input:

Hello C++ World

The complete line is stored in line.

empty() and size()

std::string s = "hello";

if (!s.empty())
{
std::cout << s.size() << '\n';
}

Output:

5

string::size_type

size() returns string::size_type, an unsigned type suitable for representing string sizes.

std::string s = "hello";

std::string::size_type length = s.size();

auto is commonly used instead.

auto length = s.size();

Avoid unnecessary signed/unsigned mixing when working with size().


Comparing strings

Strings can be compared directly.

std::string s1 = "hello";
std::string s2 = "world";

if (s1 != s2)
{
std::cout << "different\n";
}

Relational operators compare strings in lexicographical order.

std::string a = "apple";
std::string b = "banana";

if (a < b)
{
std::cout << "apple comes first\n";
}

String Assignment

std::string s1 = "hello";
std::string s2 = "world";

s1 = s2;

After the assignment:

s1 = "world"

String Concatenation

Strings can be joined using +.

std::string first = "Hello";
std::string second = "World";

std::string result = first + " " + second;

Result:

Hello World

A string can also be extended with +=.

std::string s = "Hello";

s += " World";

Literals and string

At least one operand of each + operation must be a string.

Valid:

std::string s = "Hello";

std::string result = s + " World";

Invalid:

// "Hello " + "World"

Both operands above are string literals, not std::string objects.


3) Dealing with the Characters in a string

Individual characters can be processed using iteration or subscripting.

Range-Based for

Range-Based for: Iterates through every element of a sequence.

std::string s = "Hello";

for (auto c : s)
{
std::cout << c << '\n';
}

The loop variable c receives a copy of each character.

Counting Characters

The <cctype> header provides character classification functions.

#include <cctype>
#include <string>

std::string s = "Hello, World!";

decltype(s.size()) punct_count = 0;

for (auto c : s)
{
if (std::ispunct(c))
{
++punct_count;
}
}

Important functions include:

FunctionMeaning
isalnum(c)Letter or digit
isalpha(c)Letter
isdigit(c)Digit
islower(c)Lowercase letter
isupper(c)Uppercase letter
isspace(c)Whitespace
ispunct(c)Punctuation
tolower(c)Converts to lowercase
toupper(c)Converts to uppercase

Modifying Characters

A reference loop variable allows modification of the original string.

std::string s = "hello";

for (auto& c : s)
{
c = std::toupper(c);
}

After the loop:

HELLO

Without &, only a copy of each character would be modified.


String Subscript

The [] operator accesses a character at a particular position.

std::string s = "hello";

char first = s[0];

first contains:

h

String indexing begins at zero.

A valid index satisfies:

0 <= index < s.size()

Modifying with a Subscript

std::string s = "hello";

s[0] = 'H';

Result:

Hello

Checking Before Subscripting

std::string s = "hello";

if (!s.empty())
{
s[0] = 'H';
}

Subscripting outside the valid range results in undefined behavior.


3.3 Library vector Type

vector: A variable-length collection of objects of the same type.

Container: An object that contains other objects.

Class Template: A template from which specific class types are generated.

Required header:

#include <vector>

A vector type includes its element type.

std::vector<int> numbers;
std::vector<std::string> words;

vector itself is a template, not a complete type.

std::vector<int>

is a specific type generated from the template.

Vectors can contain other vectors.

std::vector<std::vector<int>> matrix;

A vector cannot contain references because references are not objects.


1) Defining and Initializing vectors

Empty Vector

std::vector<int> values;

values initially contains no elements.

Copying a Vector

std::vector<int> v1{1, 2, 3};

std::vector<int> v2(v1);
std::vector<int> v3 = v1;

Both v2 and v3 contain:

1 2 3

List Initialization

std::vector<int> values{1, 2, 3, 4, 5};

Each value becomes an element.

Specified Number of Elements

std::vector<int> values(10);

This creates ten int elements value-initialized to zero.

Count and Value

std::vector<int> values(10, 5);

This creates ten elements, each with value 5.

Parentheses vs. Braces

These definitions have different meanings.

std::vector<int> v1(10);
std::vector<int> v2{10};

v1 contains ten zeros.

v2 contains one element:

10

Likewise:

std::vector<int> v1(10, 1);
std::vector<int> v2{10, 1};

v1:

1 1 1 1 1 1 1 1 1 1

v2:

10 1

2) Adding Elements to a vector

push_back(): Adds an element to the end of a vector.

std::vector<int> values;

values.push_back(10);
values.push_back(20);
values.push_back(30);

Result:

10 20 30

Building a Vector at Run Time

When values are not known beforehand, an empty vector can be filled dynamically.

std::vector<int> values;

for (int i = 0; i != 10; ++i)
{
values.push_back(i);
}

The vector becomes:

0 1 2 3 4 5 6 7 8 9

Reading Values into a Vector

std::vector<int> values;
int value = 0;

while (std::cin >> value)
{
values.push_back(value);
}

Do Not Change Size During Range for

Do not add elements to a vector while a range-based for is iterating over that same vector.

Avoid:

for (auto value : values)
{
// values.push_back(value);
}

Changing the vector size can invalidate the loop's internal iterators.


3) Other vector Operations

Important operations include:

OperationMeaning
v.empty()Tests whether the vector is empty
v.size()Number of elements
v.push_back(t)Adds t
v[n]Accesses element n
v1 = v2Copies elements
v1 == v2Tests equality
v1 != v2Tests inequality

Range-Based for

std::vector<int> values{1, 2, 3};

for (auto value : values)
{
std::cout << value << '\n';
}

Modifying Elements

Use a reference when modifying the original elements.

std::vector<int> values{1, 2, 3};

for (auto& value : values)
{
value *= 2;
}

Result:

2 4 6

vector::size_type

A vector provides a type suitable for representing its size.

std::vector<int> values{1, 2, 3};

std::vector<int>::size_type size = values.size();

Usually:

auto size = values.size();

is simpler.


Computing a Vector Index

A vector subscript accesses an existing element.

std::vector<int> values{10, 20, 30};

std::cout << values[1];

Output:

20

The valid range is:

0 <= index < values.size()

Grade Counter Example

A computed index can select an element.

std::vector<unsigned> scores(11, 0);

unsigned grade = 0;

while (std::cin >> grade)
{
if (grade <= 100)
{
++scores[grade / 10];
}
}

The eleven vector elements count grades in these groups:

0-9
10-19
20-29
...
90-99
100

For example:

grade = 65
65 / 10 = 6

Therefore:

++scores[6];

increments the appropriate counter.

Subscripting Does Not Add Elements

This is invalid:

std::vector<int> values;

values[0] = 10;

The vector is empty, so element 0 does not exist.

Use push_back():

std::vector<int> values;

values.push_back(10);

3.4 Introducing Iterators

Iterator: An object used to access elements of a container or characters of a string indirectly.

An iterator conceptually behaves somewhat like a pointer.

1) Using Iterators

begin()

Returns an iterator to the first element.

auto begin = values.begin();

end()

Returns an iterator one position past the last element.

auto end = values.end();

end() does not refer to an actual element and must not be dereferenced.

Iterator Loop

std::vector<int> values{10, 20, 30};

for (auto it = values.begin(); it != values.end(); ++it)
{
std::cout << *it << '\n';
}

*it accesses the element referred to by the iterator.

Empty Container

For an empty container:

values.begin() == values.end()

Iterator Operations

OperationMeaning
*iterAccess element
iter->memberAccess member of element
++iterMove to next element
--iterMove to previous element
iter1 == iter2Compare positions
iter1 != iter2Compare positions

Modifying Through an Iterator

std::vector<int> values{1, 2, 3};

for (auto it = values.begin(); it != values.end(); ++it)
{
*it *= 2;
}

Result:

2 4 6

Iterator Types

A non-const container provides a modifiable iterator.

std::vector<int>::iterator it;

A const_iterator can read but not modify elements.

std::vector<int>::const_iterator it;

cbegin() and cend()

These functions always return const iterators.

for (auto it = values.cbegin();
it != values.cend();
++it)
{
std::cout << *it << '\n';
}

Arrow Operator ->

For a container of objects, -> accesses a member of the current element.

std::vector<std::string> words{"hello", "world"};

for (auto it = words.begin(); it != words.end(); ++it)
{
std::cout << it->size() << '\n';
}

This:

it->size()

is equivalent to:

(*it).size()

Iterator Invalidation

Operations that change a vector's size may invalidate existing iterators.

std::vector<int> values{1, 2, 3};

auto it = values.begin();

values.push_back(4);

After push_back(), it may no longer be valid.

Do not continue using an iterator after an operation that may invalidate it.


2) Iterator Arithmetic

vector and string iterators support arithmetic operations.

std::vector<int> values{10, 20, 30, 40};

auto it = values.begin();

auto third = it + 2;

third refers to:

30

Iterator Difference

auto first = values.begin();
auto last = values.end();

auto distance = last - first;

The result is the number of elements between the two iterators.

Midpoint

auto mid =
values.begin() +
values.size() / 2;

Iterator Arithmetic Operations

OperationMeaning
iter + nMove forward n elements
iter - nMove backward n elements
iter += nAdvance iterator
iter -= nMove iterator backward
iter1 - iter2Distance between iterators
<, <=, >, >=Compare positions

Iterator arithmetic must involve positions belonging to the same sequence.


3.5 Arrays

Array: A fixed-size sequence of objects of the same type.

Unlike a vector, the number of array elements cannot change after the array is defined.

1) Defining and Initializing Built-in Arrays

Array syntax:

type name[size];

Example:

int values[10];

This defines an array containing ten int elements.

The dimension must be a constant expression.

constexpr unsigned size = 10;

int values[size];

List Initialization

int values[5] = {1, 2, 3, 4, 5};

The dimension can be omitted when an initializer list is present.

int values[] = {1, 2, 3, 4, 5};

The compiler determines the size.

Partial Initialization

int values[5] = {1, 2};

The remaining elements are value initialized.

Conceptually:

1 2 0 0 0

Arrays Cannot Contain References

Invalid:

// int& refs[10];

Character Arrays

A character array can be initialized from a string literal.

char text[] = "hello";

The array contains:

h e l l o \0

The terminating null character is part of the array.

Therefore:

char text[6] = "hello";

is valid.


No Array Copy or Assignment

Built-in arrays cannot be copied directly.

int a[] = {1, 2, 3};
int b[3];

// Invalid
// b = a;

Likewise, one array cannot be initialized by directly copying another built-in array.


Complicated Array Declarations

Array of Pointers

int* pointers[10];

pointers is an array containing ten pointers to int.

Pointer to Array

int values[10];

int (*p)[10] = &values;

p is a pointer to an array of ten ints.

Reference to Array

int values[10];

int (&ref)[10] = values;

ref is a reference to the entire array.


2) Accessing Array Elements

Array subscripts begin at zero.

int values[] = {10, 20, 30};

std::cout << values[0];

Output:

10

Range-Based for

int values[] = {10, 20, 30};

for (auto value : values)
{
std::cout << value << '\n';
}

Use a reference to modify the original elements.

for (auto& value : values)
{
value *= 2;
}

Bounds

For an array of size N, valid indices are:

0 ... N - 1

Access outside the valid range is undefined behavior.

int values[3] = {1, 2, 3};

// Invalid access
// values[3]

3) Pointers and Arrays

In most expressions, an array is converted to a pointer to its first element.

int values[] = {10, 20, 30};

int* p = values;

This is equivalent to:

int* p = &values[0];

auto and Arrays

int values[10];

auto p = values;

p is deduced as:

int*

decltype and Arrays

decltype preserves the array type.

int values[10];

decltype(values) copy = {};

copy is another array of ten ints.


Pointers Are Iterators

Pointers can move through an array.

int values[] = {10, 20, 30};

int* p = values;

++p;

p now points to:

values[1]

Library begin() and end()

The standard library provides functions for obtaining array boundaries.

#include <iterator>

int values[] = {10, 20, 30};

int* first = std::begin(values);
int* last = std::end(values);

first points to the first element.

last points one position past the final element.

Array traversal:

for (auto p = std::begin(values);
p != std::end(values);
++p)
{
std::cout << *p << '\n';
}

Pointer Arithmetic

Adding to a pointer moves it by elements, not bytes.

int values[] = {10, 20, 30, 40};

int* p = values;

p += 2;

p now points to:

values[2]

Pointer Difference

auto first = std::begin(values);
auto last = std::end(values);

auto count = last - first;

For a four-element array:

count = 4

Pointer Dereference and Arithmetic

int values[] = {10, 20, 30};

int* p = values;

std::cout << *(p + 1);

Output:

20

Therefore:

values[1]

and

*(values + 1)

access the same element.


4) C-Style Character Strings

C-Style String: A null-terminated character array.

char text[] = "hello";

The terminating '\0' marks the end of the string.

<cstring>

The <cstring> header provides C-style string functions.

#include <cstring>

Important functions include:

FunctionMeaning
strlen(p)Length excluding '\0'
strcmp(p1, p2)Compare strings
strcpy(p1, p2)Copy string
strcat(p1, p2)Append string

strlen

char text[] = "hello";

std::cout << std::strlen(text);

Output:

5

Comparing C-Style Strings

Do not compare their contents with ordinary pointer comparison.

Use:

const char a[] = "hello";
const char b[] = "hello";

if (std::strcmp(a, b) == 0)
{
std::cout << "equal\n";
}

Destination Size

Functions such as strcpy and strcat require sufficient destination storage.

char destination[20] = "Hello ";
const char source[] = "World";

std::strcat(destination, source);

The destination must have enough space for all characters and the final null terminator.

Prefer std::string

For normal C++ text handling, prefer:

std::string text = "hello";

over manually managing C-style character arrays.


5) Interfacing to Older Code

Modern C++ sometimes needs to interact with APIs that use arrays or C-style strings.

C-Style String to std::string

const char text[] = "hello";

std::string s = text;

string::c_str()

c_str() provides a pointer to a null-terminated representation of a string.

std::string s = "hello";

const char* p = s.c_str();

The returned pointer should not be assumed to remain valid after operations that modify s.


Array to vector

An array range can initialize a vector.

int values[] = {0, 1, 2, 3, 4, 5};

std::vector<int> v(
std::begin(values),
std::end(values)
);

The vector contains:

0 1 2 3 4 5

A partial range can also be copied.

std::vector<int> v(values + 1, values + 4);

Result:

1 2 3

Library Type Preference

For general C++ programming, prefer:

std::vector over built-in arrays
iterators over raw pointer traversal
std::string over C-style strings

unless low-level array or pointer behavior is specifically required.


3.6 Multidimensional Arrays

Multidimensional Array: An array whose elements are themselves arrays.

Strictly speaking, C++ multidimensional arrays are arrays of arrays.

Example:

int matrix[3][4];

This means:

3 rows
4 integers per row
12 integers total

Initializing Multidimensional Arrays

Nested braces make the row structure explicit.

int matrix[3][4] =
{
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};

Partial Initialization

int matrix[3][4] =
{
{0},
{4},
{8}
};

The remaining elements are value initialized.

The rows become conceptually:

0 0 0 0
4 0 0 0
8 0 0 0

Subscripting a Multidimensional Array

One subscript is used for each dimension.

int value = matrix[1][2];

This accesses row 1, column 2.

Partial Subscript

matrix[1]

refers to the entire second row.

A reference can bind to that row.

int (&row)[4] = matrix[1];

Iterating with Nested for Loops

constexpr std::size_t row_count = 3;
constexpr std::size_t column_count = 4;

int matrix[row_count][column_count];

for (std::size_t row = 0;
row != row_count;
++row)
{
for (std::size_t column = 0;
column != column_count;
++column)
{
matrix[row][column] =
row * column_count + column;
}
}

The resulting values are:

0 1 2 3
4 5 6 7
8 9 10 11

Range-Based for

When iterating over multidimensional arrays, the outer loop variable must be a reference so that the inner array does not decay into a pointer.

for (auto& row : matrix)
{
for (auto value : row)
{
std::cout << value << ' ';
}

std::cout << '\n';
}

To modify the elements:

for (auto& row : matrix)
{
for (auto& value : row)
{
value = 0;
}
}

Pointers and Multidimensional Arrays

For:

int matrix[3][4];

matrix is an array whose elements are arrays of four ints.

In most expressions it converts to:

int (*)[4]

A pointer can therefore be declared as:

int (*p)[4] = matrix;

p points to one row.

Incrementing p moves to the next row.

++p;

Now p points to:

matrix[1]

Array of Pointers vs. Pointer to Array

These declarations are different:

int* p1[4];
int (*p2)[4];

p1:

array of four pointers to int

p2:

pointer to an array of four int

The parentheses are essential.


Iterating with Pointers

for (auto p = std::begin(matrix);
p != std::end(matrix);
++p)
{
for (auto q = std::begin(*p);
q != std::end(*p);
++q)
{
std::cout << *q << ' ';
}

std::cout << '\n';
}

p moves through rows.

q moves through the elements of each row.


Type Aliases

A type alias can simplify array declarations.

using int_array = int[4];

int matrix[3][4];

int_array* p = matrix;

This:

int_array* p;

is equivalent to:

int (*p)[4];

The alias makes the relationship between the pointer and each row easier to read.


Essential Study Checklist

  1. std::string represents variable-length character sequences.
  2. std::vector<T> represents variable-length collections of T.
  3. using declarations introduce individual namespace members.
  4. Headers should generally avoid using declarations.
  5. string >> reads one whitespace-separated word.
  6. getline() reads an entire line.
  7. string::size() returns an unsigned size type.
  8. Range-based for is useful for processing every element.
  9. Use auto& when a range-based loop must modify the original element.
  10. vector::push_back() adds elements.
  11. vector[n] accesses only an element that already exists.
  12. Vector subscripting does not create elements.
  13. begin() refers to the first element.
  14. end() refers to one position past the last element.
  15. An iterator must not dereference end().
  16. Vector size changes may invalidate iterators.
  17. Iterator arithmetic is supported by vector and string iterators.
  18. Built-in arrays have a fixed size.
  19. Array indices begin at zero.
  20. Arrays normally convert to pointers to their first elements.
  21. std::begin() and std::end() can safely obtain array bounds.
  22. Pointer arithmetic moves by elements.
  23. C-style strings end with '\0'.
  24. Prefer std::string over C-style strings for normal text handling.
  25. An array range can initialize a vector.
  26. A multidimensional array is actually an array of arrays.
  27. int (*p)[4] is a pointer to an array of four ints.
  28. int* p[4] is an array of four pointers.
  29. Outer range-for variables over multidimensional arrays should be references.
  30. Type aliases can simplify complicated array types.