[关闭]
@Moritz 2019-01-25T04:36:07.000000Z 字数 27533 阅读 442

Chapter 3. Strings, Vectors and Arrays

C++primer C++ 课程学习 所有文稿


3.1 Namespace using Declarations

scope operation:: : std::cin says that we want to use the name cin from namespace std.

A using declarations lets us use a name from a namespace **without qualifying the name with a prefix前缀 like namespace_name::.


3.2 Library string Type

A string is a variable-length sequence of characters. Because it is part of the library, string is defined in the std namespace.

  1. #include <string>
  2. using std::string;
  3. /*
  4. using namespace std;
  5. */

More string operations in 9.5

Defining and Initializing

  1. string s2(s1); // s2 is a copy of s1
  2. string s2 = s1; // s2 is a copy of s1
  3. string s3("hiya"); // s3 is a copy of the string literal, not including the null
  4. string s3 = "hiya"; // equivalent to s3("hiya")
  5. string s4(10, 'c'); // s4 is cccccccccc

When we initialize a variable using =, we are asking the compiler to copy initialize拷贝初始化 the object by copying the initializer 初始化值 on the right-hand side into the object being created.

  1. string s2 = s1;
  2. string s3 = "hiya";

Otherwise, when we omit the =, we use direct initialization 直接初始化.

  1. string s3("hiya");
  2. string s4(10, 'c');

Operations

  1. cin>>string

    • discard any leading whitespace (e.g., spaces, newlines, tabs) 空白字符
    • read characters until the next whitespace character is encountered.
  2. while(cin>>string)

    • stop when hit end-of-file or encounter an invalid input
  3. getline(cin,string)

    • include whitespace

    • stop when sees a newline

    • not including the newline

    • return its istream argument 返回流参数

      1. // read input a line at a time until end-of-file
      2. while (getline(cin, line))
      3. cout << line << endl;
  4. empty and size operations

  1. while (getline(cin, line))
  2. if (!line.empty()&&line.size()>80)
  3. cout << line << endl;
  1. The string::size_typeType

    The size member returns a string::size_type value. It is an unsigned type although we don’t know the precise type of it.

  2. comparing

    The relational operators <, <=, >, >= can be used to compare two strings: compare the first different character or the length of them, in the case of shorter string is equal to the corresponding character of the longer string.

  3. assignment for strings 赋值

    1. string s1(10,'c'),s2;
    2. s2=s1; //cccccccccc
  4. addition

    • + or compound assignment operator += 复合赋值运算符

    • When we mix strings s1,s2... and string or character literals "Hello",'a'...字符串字面值和字符字面值, at least one operand to eachoperator must be of string type

      1. string s6 = s1 + ", " + "world"; // ok: each + has a string operand
      2. /*It is equivalent to these 2 sentences*/
      3. string temp = s1 + ", "; // ok: + has a string operand
      4. s6 = temp + "world"; // ok: + has a string operand
      5. string s7 = "hello" + ", " + s2; // error: can't add string literals
      6. string s7 = ("hello" + ", ") + s2; // error: can't add string literals

Characters in a string

Knowing and/or changing thecharacteristics of a character is handled by a set of library
functions, defined in the cctype header .

isalnum(c) 字母或数字
isalpha(c) 字母
isdigit(c) 数字
iscntrl(c) 控制字符
isgraph(c) 不是空格但可打印时为真
islower(c) 小写字母
isupper(c) 大写字母
ispunct(c) 标点符号
isspace(c) 空白(即空格、横向/纵向制表符、回车符、换行符、进制符中的一种)
isprint(c) 可打印字符(空格或具有可视形式)
isxdigit(c) 十六进制数字
tolower(c) 若c是大写,则输出小写;否则原样输出c
toupper(c) 若c是小写,则输出大写;否则原样输出c

Use Range-Based for to Processing Every Character:

  1. string str("some string");
  2. // print the characters in str one character to a line
  3. for (auto c : str) // for every char in str
  4. cout << c << endl; // print the current character followed by a newline
  1. string s("Hello World!!!");
  2. // punct_cnt has the same type that s.size returns (string::size_type)
  3. decltype(s.size()) punct_cnt = 0;
  4. // count the number of punctuation characters in s
  5. for (auto c : s) // for every char in s
  6. if (ispunct(c)) // if the character is punctuation
  7. ++punct_cnt; // increment the punctuation counter
  8. cout << punct_cnt<< " punctuation characters in " << s << endl;
  9. /*output:
  10. 3 punctuation characters in Hello World!!!
  11. */
  1. string s("Hello World!!!");
  2. // convert s to uppercase
  3. for (auto &c : s) // for every char in s (note: c is a reference)
  4. c = toupper(c); // c is a reference 引用, so the assignment changes the char in s
  5. cout << s << endl;
  6. /*The output of this code is
  7. HELLO WORLD!!!
  8. */

There are two ways to access individual characters in a string: We can use a subscript 下标 or an iterator 迭代器 . The subscript operator [ ] takes a string::size_type value, called subscript or index 索引. Before accessing the character, we check that string is not empty.


3.3 Library vector Type

A vector is often referred to as a container 容器 because it “contains” other objects.

  1. #include <vector>
  2. using std::vector;

A vector is a class template 类模板. C++ has both class and function templates 函数模板. Templates 模板 are not themselves functions or classes. Instead, they can be thought of as instructions 说明 to the compiler for generating classes or functions. The process that the compiler uses to create classes or functions from templates is called instantiation 实例化. When we use a template, we specify what kind of class or function we want the compiler to instantiate.

For a class template, we specify which class to instantiate by supplying additional information, the nature of which depends on the template. How we specify the information is always the same: We supply it inside a pair of angle brackets 尖括号 following the template’s name.
In the case of vector, the additional information we supply is the type of the objects the vector will hold:

  1. #include <vector>
  2. using std::vector;
  3. vector<int> ivec; // ivec holds objects of type int
  4. vector<Sales_item> Sales_vec; // holds Sales_items
  5. vector<vector<string>> file; // vector whose elements are vectors,C++11
  6. vector<vector<string> > file; //earlier than C++11, add a space between brackets

Because references are not objects, we cannot have a vector of references.

Defining and Initializing

  1. vector<T> v1; //空vector,潜在的元素是T类型,执行默认初始化
  2. vector<T> v2(v1); //与下一行代码等价
  3. vector<T> v2=v1;
  4. vector<T> v3(n,value);
  5. vector<T> v4(n); //包含了n个重复地执行了初始化的对象
  6. vector<T> v5{a,b,c...};
  7. vector<T> v5={a,b,c...}; //与下一行代码等价

Some restrictions in several forms of initialization 初始化 provided by C++ ( haven't list all )

  1. when we use the copy initialization 拷贝初始化 form(using = ), we can supply only a single initializer

  2. and when we supply an in-class initializer 内类初始值 , we must either use copy initialization or use curly braces花括号

  3. we can supply a list of element values only by using list initialization in which the initializers are enclosed in curly braces

    1. vector<string> v2("a", "an", "the"); // error

Value Initialization 值初始化
We can usually omit the value and supply only a size. In this case the library creates a value-initialized element initializer 值初始化的元素初值 for us. This library-generated value is used to initialize each element in the container. The value of the element initializer depends on the type of the elements stored in the vector.

If the vector holds elements of a built-in type 内置类型 , such as int, then the element initializer has a value of 0. If the elements are of a **class type 类类型 **, such as string, then the element initializer is itself default initialized元素由类默认初始化:

  1. vector<int> ivec(10); // ten elements, each initialized to 0
  2. vector<string> svec(10); // ten elements, each an empty string

There are two restrictions on this form of initialization:

The first restriction is that some classes require that we always supply an explicit initializer 明确的初始值 . If our vector holds objects of a type that we cannot default initialize 不支持默认初始化 , then we must supply an initial element value; it is not possible to create vectors of such types by supplying only a size.
The second restriction is that when we supply an element count 元素数量 without also supplying an initial value 初始值 , we must use the direct form of initialization 直接初始化

  1. vector<int> vi = 10; // error: must use direct initialization to supply a size

Here we are using 10 to instruct vector how to create the vector—we want a
vector with ten value-initialized elements. We are not “copying” 10 into the vector.
Hence, we cannot use the copy form of initialization.

List Initializer 列表初始值 or Element Count?
In a few cases, what initialization means depends upon whether we use curly braces 花括号 or parentheses 圆括号 to pass the initializer(s).

  1. vector<int> v1(10); // v1 has ten elements with value 0
  2. vector<int> v2{10}; // v2 has one element with value 10
  3. vector<int> v3(10, 1); // v3 has ten elements with value 1
  4. vector<int> v4{10, 1}; // v4 has two elements with values 10 and 1

When we use parentheses () , we are saying that the values we supply are to be used to construct 构造 the object. When we use curly braces {} , we’re saying that, if possible, we want to list initialize 列表初始化 the object. That is, if there is a way to use the values inside the curly braces as a list of element initializers, the class will do so.即优先考虑列表初始化 Only if it is not possible to list initialize the object will the other ways to initialize the object be considered.
On the other hand, if we use braces and there is no way to use the initializers to list initialize the object, then those values will be used to construct the object.

  1. vector<string> v5{"hi"}; // list initialization: v5 has one element
  2. vector<string> v6("hi"); // error: can't construct a vector from a string literal
  3. vector<string> v7{10}; // v7 has ten default-initialized elements
  4. vector<string> v8{10, "hi"}; // v8 has ten elements with value "hi"

Only v5 is list initialized. We cannot use an int to initialize a string, so the initializers for v7 and v8 can’t be element initializers. If list initialization isn’t possible, the compiler looks for other ways to initialize the object from the given values.

Adding Elements

we can use a vector member vector成员函数 named push_back to takes a value and “pushes” that value as a new last element onto the “back” of the vector.

  1. vector<int> v2; // empty vector
  2. for (int i = 0; i != 100; ++i)
  3. v2.push_back(i); // append sequential integers to v2 依次把整数放到v2尾端
  4. // at end of loop v2 has 100 elements, values 0 . . . 99

Key Concept: vectors Grow Efficiently

The standard requires that vector implementations can efficiently add
elements at run time. It is often unnecessary to define a vector of a
specific size except all the elements actually need the same value.

Programming Implications of Adding Elements to a vector 对vector对象添加元素蕴含的编程假定

Other implications that follow from the dynamic nature of vectors will become clearer as we learn more about using them. However, there is one implication that is worth noting already: For reasons we’ll explore in § 5.4.3 (p. 188), we cannot use a range for 范围for if the body of the loop adds elements to the vector.

Other Operations

Some vector Operations

We access the elements of a vector the same way that we access the characters in a string: through their position in the vector. For example, we can use a range forto process all the elements in a vector:

  1. vector<int> v{1,2,3,4,5,6,7,8,9};
  2. for (auto &i : v) // for each element in v (note: i is a reference)
  3. i *= i; // square the element value
  4. for (auto i : v) // for each element in v
  5. cout << i << " "; // print the element

The size member returns a value of the size_type defined by the corresponding vector type. To use size_type, we must name the type in which it is defined.

  1. vector<int>::size_type // ok
  2. vector::size_type // error

Subscripting Does Not Add Elements

The subscript operator on vector ( and string ) fetches an existing element. It does not add an element. Subscript Only Elements that are Known to Exist!

Attempting to subscript elements that do not exist is, unfortunately, an extremely common and pernicious programming error. So-called **buffer overflow errors 缓冲区溢出 ** are the result of subscripting elements that don’t exist. Such bugs are the most common cause of security problems in PC and other
applications.

Tip : A good way to ensure that subscripts are in range is to avoid subscripting altogether by using a range for whenever possible.

  1. vector<int> ivec; // empty vector
  2. for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)
  3. ivec[ix] = ix; // disaster: ivec has no elements

3.4 Introducing Iterators

All of the library containers have iterators, but only a few of them support the subscript operator. Technically speaking, a string is not a container type, but string supports many of the container operations. Strings and vectors have a subscript operator and iterators.

We can use an iterator to fetch an element and iterators have operations to move from one element to another. As with pointers, an iterator may be valid or invalid 有效与无效之分 .

Using Iterators

Unlike pointers, we do not use the address-of operator 取地址运算符 to obtain an iterator. Instead, types that have iterators have members that return iterators. In particular, these types have members named begin and end. The begin member returns an iterator that denotes the first element (or first character) if there is one.

  1. // the compiler determines the type of b and e
  2. // b denotes the first element and e denotes one past the last element in v
  3. auto b = v.begin(), e = v.end(); // b and e have the same type

The iterator returned by end is an iterator positioned “one past the end” 尾元素的下一个位置 of the associated container (or string). This iterator denotes a nonexistent element “off the end” 尾后元素 of the container. It is used as a marker indicating when we have processed all the elements. The iterator returned by end is often referred to as the off-the-end iterator 尾后迭代器 or abbreviated as the end iterator 尾迭代器. If the container is empty, begin returns the same ( off-the-end ) iterator as the one returned by end.

Iterator Operations

  1. *iter : Returns a reference to the element denoted by the interator iter

    Dereferencing an invalid iterator or an off-the-end iterator has undefined behavior.

    1. string s("some string");
    2. if (s.begin() != s.end()) { // make sure s is not empty
    3. auto it = s.begin(); // it denotes the first character in s
    4. *it = toupper(*it); // make that character uppercase
    5. }
    6. cout<<s<<endl;//"Some string"
  2. iter->mem/(*iter).mem : Dereferences 解引用 iter and fetcheds the member named mem from the underlying element

    When we dereference an iterator, we get the object that the iterator denotes. If that object has a class type, we may want to access a member of that object. For example, we might have a vector of strings and we might need to know whether a given element is empty. Assuming it is an iterator into thisvector, we can check whether the string that it denotes is empty as follows:

    1. (*it).empty() // dereferences it and calls the member empty on the resulting object
    2. *it.empty(); // error: attempts to fetch the member named empty from it
    3. // but it is an iterator and has no member named empty
    4. // print each line in text up to the first blank line
    5. for (auto it = text.cbegin();it != text.cend() && !it->empty(); ++it)
    6. cout << *it << endl;
  3. ++iter : Increments iter to refer to the next element in the container

    Because the iterator returned from end does not denote an element, it may not be incremented or dereferenced.

  4. --iter : Decrements iter ot refer to the previous element in the container

  5. iter1!=iter2``iter1==iter2

Key Concept: Generic Programming 泛型编程
Programmers coming to C++ from C or Java might be surprised that we used != rather than < in our for loops. C++ programmers use != as a matter of habit for the same reason that they use iterators rather than subscripts: This coding style applies equally well to various kinds of containers provided by the library.

Iterator Types

As with size_type, we generally do not know—and do not need to know—the precise type of an iterator.

  1. vector<int>::iterator it; // it can read and write vector<int> elements
  2. string::iterator it2; // it2 can read and write characters in a string
  3. vector<int>::const_iterator it3; // it3 can read but not write elements
  4. string::const_iterator it4; // it4 can read but not write characters

A const_iterator behaves like a const pointer. A const_iterator may read but not write the element it denotes; an object of type iterator can both read and write.

If a vector or string is const, we may use only its const_iterator type.

The type returned by begin and end depends on whether the object on which they operator is const. If the object is const, then they return a const_iterator; if the object is not const, they return iterator.

  1. vector<int> v;
  2. auto it2 = cv.begin(); // it2 has type vector<int>::const_iterator
  3. const vector<int> cv;
  4. auto it1 = v.begin(); // it1 has type vector<int>::iterator
  5. auto it3 = v.cbegin(); //C++11, it3 has type vector<int>::const_iterator

For reasons we’ll explain in § 6.2.3 (p. 213), it is usually best to use a const type (such as const_iterator) when we need to read but do not need to write to an object. 只读不写

Terminology术语: Iterators and Iterator Types
The term iterator is used to refer to three different entities. We might mean the concept of an iterator, or we might refer to the iterator type defined by a container, or we might refer to an object as an iterator.

What’s important to understand is that there is a collection of types that are related conceptually. A type is an iterator if it supports a common set of actions. Those actions let us access an element in a container and let us move from one element to another.

Each container class defines a type named iterator; that iterator type supports the actions of an (conceptual) iterator

Some vector Operations Invalidate Iterators

We noted that there are implications 副作用 of the fact that vectors can grow dynamically. We also noted that one such implication is that we cannot add elements to a vector inside a range for loop. Another implication is that any operation, such as push_back, that changes the size of a vector potentially invalidates all iterators into that vector 使迭代器失效. We’ll explore how iterators become invalid in more detail in § 9.3.6 .Loops that use iterators should not add elements to the container to which the iterators refer.

Iterator Arithmetic

Iterators for string and vector support additional operations that can move an iterator multiple elements at a time. They also support all the relational operators. These operations are often referred to as iterator arithmetic 迭代器运算.

  1. iter+n``iter-n``iter+=n``iter-=n : The result is still an iterator.
  2. iter1-iter2 : Subtracting two iterators yields the number that added to the right-hand iterator yields the left-hand iterators. The iterators must denote elements in, or one past the end of 尾元素的下一个元素, the same container. The result type is a signed integral type named difference_type. Both
    vector and string define difference_type.
  3. >,>=,<,<= : Relational operators on iterators.

A classic algorithm that uses iterator arithmetic is binary search 二分搜索.

My version:

  1. cin>>tar;
  2. auto head=v.begin(),tail=v.end();
  3. while(head!=tail)
  4. {
  5. if (tar==*((tail-head)/2+head)) {flag=true;break;}
  6. else if (tar>*((tail-head)/2+head)) head=(tail-head)/2+head;
  7. else tail=(tail-head)/2+head;
  8. }

Text book version:

  1. // text must be sorted
  2. // beg and end will denote the range we're searching
  3. auto beg = text.begin(), end = text.end();
  4. auto mid = text.begin() + (end - beg)/2; // original midpoint
  5. // while there are still elements to look at and we haven't yet found sought
  6. while (mid != end && *mid != sought) {
  7. if (sought < *mid) // is the element we want in the first half?
  8. end = mid; // if so, adjust the range to ignore the second half
  9. else // the element we want is in the second half
  10. beg = mid + 1; // start looking with the element just after mid
  11. mid = beg + (end - beg)/2; // new midpoint
  12. }

3.5 Arrays

Defining and Initializing Built-in Arrays 内置数组

Arrays are a compound type 复合类型. The dimension must be known at compile 编译 time, which means that the dimension must be a constant expression 常量表达式.

By default 默认情况下, the elements in an array are default initialized 默认初始化(§ 2.2.1, p. 43).

Warning
As with variables of built-in type, a default-initialized array of built-in type that is defined inside a function will have undefined values. 和内置类型一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数字含有未定义的值

  1. /*Exercise 3.28*/
  2. string sa[10]; //空字符串
  3. int ia[10]; //0
  4. int main() {
  5. string sa2[10]; //空字符串 ????
  6. int ia2[10]; //元素的值全部未定义
  7. }

When we define an array, we must specify a type for the array. We cannot use auto to deduce the type from a list of initializers. As with vector, arrays hold objects. Thus, there are no arrays of references.

Explicitly Initializing Array Elements 显式初始化数组元素

When we list initialize 列表初始化 the elements in an array, we can omit the dimension.If the dimension is greater than the number of initializers, the initializers are used for the first elements and any remaining elements are value initialized 被初始化为默认值.

  1. int a3[5] = {0, 1, 2}; // equivalent to a3[] = {0, 1, 2, 0, 0}
  2. string a4[3] = {"hi", "bye"}; // same as a4[] = {"hi", "bye", ""}

Character arrays have an additional form of initialization: We can initialize such arrays from a string literal 字符串字面值. When we use this form of initialization, it is important to remember that string literals end with a null character. That null character is copied into the array along with the characters in the literal.

  1. char a1[] = {'C', '+', '+'}; //3, list initialization, no null
  2. char a2[] = {'C', '+', '+', '\0'}; //4, list initialization, explicit null
  3. char a3[] = "C++"; //4, null terminator added automatically
  4. const char a4[6] = "Daniel"; // error: no space for the null!

No Copy or Assignment
We cannot initialize an array as a copy of another array, nor is it legal to assign one array to another.

  1. int a[] = {0, 1, 2}; // array of three ints
  2. int a2[] = a; // error: cannot initialize one array with another
  3. a2 = a; // error: cannot assign one array to another

Warning
Some compilers allow array assignment as a compiler extension 编译器扩展. It is usually a good idea to avoid using nonstandard features. Programs that use such features, will not work with a different compiler.

  1. int *ptrs[10]; // ptrs is an array of ten pointers to int
  2. int &refs[10] = /* ? */; // error: no arrays of references
  3. int (*Parray)[10] = &arr; // Parray points to an array of ten ints
  4. int (&arrRef)[10] = arr; // arrRef refers to an array of ten ints
  5. int *(&arry)[10] = ptrs; // arry is a reference to an array of ten pointers

By default, type modifiers 类型修饰符 bind right to left. Because the array dimension follows the name being declared, it can be easier to read array declarations by starting with the array’s name and reading from the inside out rather than from right to left.

Reading the declaration of int *(&arry)[10] = ptrs; from the inside out, we see that arry is a reference. Looking right, we see that the object to which arry refers is an array of size 10. Looking left, we see that the element type is pointer to int. Thus, arry is a reference to an array of ten pointers.

Accessing the Elements

When we use a variable to subscript an array, we normally should define that variable to have type size_t. size_t is a machine-specific 机器相关 unsigned type that is guaranteed to be large enough to hold the size of any object in memory. The size_t type is defined in the cstddef header,

As in the case of string or vector, it is best to use a range for when we want to traverse 遍历 the entire array.

Pointers and Arrays

The compiler ordinarily converts the array to a pointer.

  1. string *p2 = nums; // equivalent to p2 = &nums[0]

There are various implications of the fact that operations on arrays are often really operations on pointers. One such implication is that when we use an array as an initializer for a variable defined using auto, the deduced type is a pointer, not an array.

  1. int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
  2. auto ia2(ia); // ia2 is an int* that points to the first element in ia
  3. ia2 = 42; // error: ia2 is a pointer, and we can't assign an int to a pointer

Although ia is an array of ten ints, when we use ia as an initializer, the compiler treats that initialization as if we had written:

  1. auto ia2(&ia[0]); // now it's clear that ia2 has type int

It is worth noting that this conversion does not happen when we use decltype. The type returned by decltype(ia) is array of ten ints:

  1. // ia3 is an array of ten ints
  2. decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
  3. ia3 = p; // error: can't assign an int* to an array
  4. ia3[4] = i; // ok: assigns the value of i to an element in ia3

Pointers Are Iterators

Pointers to array elements support the same operations as iterators on vectors or strings and have additional operations beyond those we described before.( § 2.3.2 ) We can obtain an off-the-end pointer 尾后指针 by using another special property of arrays.

  1. int arr[10];
  2. int *e=&arr[10]; // pointer just past the last element in arr

Although we can compute an off-the-end pointer, doing so is error-prone 极易出错. To make it easier and safer to use pointers, the new library includes two functions, named begin and end, defined in the iterator header . However, arrays are not class types 类类型, so these functions are not member functions 成员函数. Instead, they take an argument that is an array 数组作为参数:

  1. int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
  2. int *beg = begin(ia); // pointer to the first element in ia
  3. int *last = end(ia); // pointer one past the last element in ia

In particular, we may not dereference or increment an off-the-end pointer. In a case using begin and end to process elements with a loop, the while condition uses p_end to know whether it is safe to dereference p_beg.

Pointer Arithmetic

Pointers that address array elements can use all the iterator operations listed before in this chapter.

We can use the relational operators to compare pointers that point to elements of an array, or one past the last element in that array. We cannot use the relational operators on pointers to two unrelated objects.

Pointer arithmetic is also valid for null pointers 空指针 and for pointers that point to an object that is not an array.

Interaction between Dereference and Pointer Arithmetic 解引用和指针运算的交互

The result of adding an integral value to a pointer is itself a pointer. Assuming the resulting pointer points to an element, we can dereference the resulting pointer:

  1. int ia[] = {0,2,4,6,8}; // array with 5 elements of type int
  2. int last = *(ia + 4); // ok: initializes last to 8, the value of ia[4]

Subscripts and Pointers

  1. int ia[] = {0,2,4,6,8}; // array with 5 elements of type int
  2. int *p = &ia[2]; // p points to the element indexed by 2
  3. int k = p[-2]; // p[-2] is the same element as ia[0]

Warning
Unlike subscripts for vector and string, the index of the built-in subscript operator is not an unsigned type. The index used with the built-in subscript operator can be a negative value. 内置的下标运算符可以处理负值

C-Style Character Strings

Warning
Although C++ supports C-style strings, they should not be used by C++ programs. C-style strings are a surprisingly rich source of bugs and are the root cause of many security problems. They’re also harder to use!

Character string literals 字符串字面值 are an instance of a more general construct that C++ inherits from C: C-style character strings C风格字符串. C-style strings are not a type. Instead, they are a convention for how to represent and use character strings. Strings that follow this convention are stored in character arrays and are null terminated 空字符结束, meaning that the last character in the string is followed by a null character '\0'.

The rest of this section has not been learned and taken note for the disadvantages of using C-style strings.

  1. /*Exercise 3.37: What does the following program do?*/
  2. const char ca[] = {'h', 'e', 'l', 'l', 'o'};
  3. const char *cp = ca;
  4. while (*cp) {
  5. cout << *cp << endl;
  6. ++cp;
  7. }
  8. /*According to [https://github.com/huangmingchuan/Cpp_Primer_Answers/blob/master/ch03/README.md]*/
  9. /*会将ca 字符数组中的元素打印出来。但是因为没有空字符的存在,程序不会退出循环。*/
  10. /*test result in CodeBlocks:
  11. h
  12. e
  13. l
  14. l
  15. o
  16. ,
  17. (
  18. Process returned 0 (0x0) execution time : 0.104 s
  19. Press any key to continue.
  20. */
  21. /*What happended?*/

Interfacing to Older Code 与旧代码的接口

The rest of this section has not been learned and taken note for the disadvantages of using C-style strings.


3.6 Multidimensional Arrays

Strictly speaking, there are no multidimensional arrays in C++. What are commonly referred to as multidimensional arrays are actually arrays of arrays.

  1. int ia[3][4]; // array of size 3; each element is an array of ints of size 4
  2. // array of size 10; each element is a 20-element array whose elements are arrays of 30 ints
  3. int arr[10][20][30] = {0}; // initialize all elements to 0

We can more easily understand these definitions by reading them from the inside out.We start with the name we’re defining ia and see that ia is an array of size 3. Continuing to look to the right, we see that the elements of ia also have a dimension. Thus, the elements in ia are themselves arrays of size 4. Looking left, we see that the type of those elements is int. So, ia is an array of size 3, each of whose elements is an array of four ints.

Initializing

Just like the ways introduced in TanHaoQiang.

Subscripting a Multidimensional Array

If we supply fewer subscripts than there are dimensions, then the result is the inner-array element at the specified index 给定索引处的一个内层数组:

  1. // assigns the first element of arr to the last element in the last row of ia
  2. ia[2][3] = arr[0][0][0];
  3. int (&row)[4] = ia[1]; // binds row to the second four-element array in ia

Range for

Note
To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.

  1. size_t cnt = 0;
  2. for (auto &row : ia) // for every element in the outer array
  3. for (auto &col : row) { // for every element in the inner array
  4. col = cnt; // give this element the next value
  5. ++cnt; // increment cnt
  6. }

We want to change the value of the elements, so we declare our control variables, row and col, as references. In the previous example, we used references as our loop control variables because we wanted to change the elements in the array. However, there is a deeper reason for using references. As an example, consider the following loop:

  1. for (const auto &row : ia) // for every element in the outer array
  2. for (auto col : row) // for every element in the inner array
  3. cout << col << endl;

We still define the control variable of the outer loop as a reference. We do so in order to avoid the normal array to pointer conversion.Had we neglected the reference and written these loops as:

  1. for (auto row : ia)
  2. for (auto col : row)

our program would not compile. In this loop the type of row is int*. The inner for loop is illegal.

Pointers

Because a multidimensional array is really an array of arrays, the pointer type to which the array converts is a pointer to the first inner array:

  1. int ia[3][4]; // array of size 3; each element is an array of ints of size 4
  2. int (*p)[4] = ia; // p points to an array of four ints
  3. p = &ia[2]; // p now points to the last element in ia

Note that the parentheses in this declaration are essential:

  1. int *ip[4]; // array of pointers to int
  2. int (*ip)[4]; // pointer to an array of four ints

With the advent of the new standard, we can often avoid having to write the type of a pointer into an array by using auto or decltype.

  1. // print the value of each element in ia, with each inner array on its own line
  2. // p points to an array of four ints
  3. for (auto p = ia; p != ia + 3; ++p) {
  4. // q points to the first element of an array of four ints; that is, q points to an int
  5. for (auto q = *p; q != *p + 4; ++q)
  6. cout << *q << ' ';
  7. cout << endl;
  8. }

The inner for loop prints the values of the inner arrays. It starts by making q point to the first element in the array to which p points. The result of *p is an array of four ints. As usual, when we use an array, it is converted automatically to a pointer to its first element. The inner for loop runs until we’ve processed every element in the inner array. To obtain a pointer just off the end of the inner array, we again ereference p to get a pointer to the first element in that array. We then add 4 to that pointer to process the four elements in each inner array.
Of course, we can even more easily write this loop using the library begin and end functions.

  1. // p points to the first array in ia
  2. for (auto p = begin(ia); p != end(ia); ++p) {
  3. // q points to the first element in an inner array
  4. for (auto q = begin(*p); q != end(*p); ++q)
  5. cout << *q << ' '; // prints the int value to which q points
  6. cout << endl;
  7. }

Type Aliases Simplify Pointers to Multidimensional Arrays
A type alias 类型别名 can make it easier to read, write, and understand pointers to multidimensional arrays.

  1. using int_array = int[4]; // new style type alias declaration
  2. typedef int int_array[4]; // equivalent typedef declaration
  3. // print the value of each element in ia, with each inner array on its own line
  4. for (int_array *p = ia; p != ia + 3; ++p) {
  5. for (int *q = *p; q != *p + 4; ++q)
  6. cout << *q << ' ';
  7. cout << endl;
  8. }

Here we start by defining int_array as a name for the type “array of four ints.” We use that type name to define our loop control variable in the outer for loop.


施工中 -2019.1.22

string学习完成 -2019.1.22

vector 学习完成 -2019.1.23

iterator 学习完成 -2019.1.23

Array 学习完成 -2019.1.23

Multidimensional Array 学习完成 -2019.1.25

施工完成 -2019.1.25

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注