C++ Primer Notes, Chapter 2

Chapter 2.    Variables and Basic Types

Note 1 (P32): Size of arithmetic types under 64-bit windows 4-bit windows system:


Note 2 (P34): Deciding which type to use:

  • Use an unsigned type when you know the value will never be negative.
  • Use int for integer values. short is usually too small and long often has the same size as int. If your data values are larger than the minimum guaranteed size of an int, then use long long.
  • Do not use plain char or bool in arithmetic expressions. Use them only to hold characters or truth values. Computations using char are especially problematic because char is signed on some machines and unsigned on others. If you need a tiny integer, explicitly specify either signed char or unsigned char.
  • Use double for floating-point computations; float usually do not hold enough precision, and the cost of double-precision calculations versus single-precision is negligible. On some machines, calculation of double-precision is faster than single-precision. The precision offered by long double is usually unnecessary and oftern entails considerable run-time cost.


Note 3 (P35-P37): If we assign an out-of-range value to an object of unsigned type, the result is the remainder of the value modulo the number of values the target type can hold.


If we assign an out-of-range value to an object of signed type, the result is undefined. The program might appear to work, it might crash or it might produce garbage values.

Similar behavior with int:



Don't mix signed and unsigned types. It is essential to remember that signed values are automatically converted to unsigned during mixed calculation.


Note 4 (P39): \x followed by one or more hexadecimal digits or a \ followed by one,two, or three octal digits. If a \ is followed by more than three octal digits, only the first three are associated with the \. i.e. "\1234" represents two characters: the character represented by the octal value 123 and the character 4. "\x" uses all the hex digits following it.


Note 5 (P40): Specifying the type of a literal:

  1. Prefix:
  • u    Unicode 16 character        char16_t
  • U    Unicode 32 character        char32_t
  • L    Wide character                  wchar_t
  • u8    utf-8 (string literals only)    char

    2. Suffix (integer literals)

  • u or U    unsigned
  • l or L      long
  • ll or LL    long long

    3. Suffix (floating-point literals)

  • f or F    float
  • l or L    long double

We can combine U with either L or LL.


Note 6 (P41): Octal integer with prefix 0: int a = 012; (decimal a = 10)

Hexadecimal integer with prefix 0x: int a = 0xC; (decimal a = 12)


Note 7 (P41): nullptr is a pointer literal.


Note 8 (P43): Initialization is not assignment. Initialization happens when a variable is givien a value when it is created. Assignment obliterates an object's current value and replaces that value with a new one.


Note 9 (P43): The generalized use of curly braces for initialization was introduced as part of the new standard. This form of initialization previously had been allowed only in more restricted ways. This form of initialization is referred to as list initialization. Braced lists of initializers can now be used whenever we initialize an object and in some cases when we assign a new value to an object.


Note 10 (P45): An uninitialized variable has an indeterminate value. Trying to use the value of an uninitialized variable is an error that is often hard to debug. We recommend initializing every object of built-in type. It is not always necessary but it is easier and safer to provide an initializer until you can be certain it is safe to omit the initializer.


Note 11 (P45-P46): A declaration makes a name known to the program. A definition creates the associated entity.

To obtain a declaration that is not also a definition, we add the extern keyword and may not provide an explicit initilizer. We can provide an initializer on a variable defines as extern, but doing so overrides the extern.

extern int i;        //declares but does not define i
int j;                  //declares and defines j
extern double pi = 3.1416;    // definition

It is an error to provide an initializer to an extern inside a function.

Variables must be defined exactly once buy can be declared many times. To use the same variable in multiple files, we must define that variable in one-and only one-file. Other files that uses that variable must declare-but not define-that variable.


Note 12 (P46): Identifiers can be composed of letters, digits and the underscore character and must begin with a letter or an underscore.


Note 13 (P50, P55): A reference is not an object. It is just another name for an already existing object. A reference must be initialized. There is no way to make the reference refer to a different object. When we use a reference, we always get the object to which the reference was initially bound.



Note 14 (P54, P58): Ways to obtain null pointer:

int *p1 = nullptr;

int *p2 = 0;

int *p3 = NULL;

The most direct approach is to initialize the pointer using the literal nullptr, which was introduced by the new standard. nulptr can be converted to any other pointer type.

A reference is not an object. Hence, we may not have a pointer to a reference.


Note 15 (P56): Generally, we use a void* pointer to deal with memory as memory, rather than using the pointer to access the object stored in that memory.


Note 16 (P57): int* p1, p2;     // p1 is a pointer to int, p2 is an int


Note 17 (P58): reference to pointer:


It can be easier to understand complicated pointer or reference declarations if you read them from right to left.


Note 18 (P59, P60): Any attempt to change the value of const variable causes error. After we create const variable, it must be initialized.

The compiler will usually replace uses of the const variable, with its corresponding value during compilation. const variables are defined as local to a file. If we want to share across multiple files, use extern keyword.

// file_1.cc defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_1.h
extern const int bufSize;    // same bufSize as defined in file_1.cc

Note 19 (P61, P62): reference to const

const int ci = 1024;
const int &r1= ci;    // ok:both reference and underlying object are const
ri = 42;    // error:r1 is a reference to const
int &r2 = ci;    // error:nonconst reference to a const object
int i = 42;
const int &r1 = i;    // we can bind a const int& to a plain int object
const int &r2 = 42;    // ok:r1 is reference to const
const int &r3 = r1 * 2    // ok:r3 is reference to const
int &r4 = r * 2    // error:r4 is a plain, nonconst reference
Binding a reference to const to an object says nothing about whether the underlying object itself is const.
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;    // r1 is not const; i is now 0
r2 = 0;    // error:r2 is a reference to const


Note 20 (P62): A pointer to const may not be used to change the object to which the pointer points. We may store the address of a const object only in a pointer to const.
const double dval = 3.14;
double *ptr = dval    // error:ptr is a plain pointer
const double *cptr = dval    // ok:cptr may point to a double that is const
*cptr = 42;    // error:cannot assign to *cptr

double dval = 3.14;
cptr = &dval;    // ok:but can't change dval through cptr

Note 21 (P63): A const pointer must be initialized, and once initialized, its value (i.e., the address that it holds) may not be changed.

int errNum = 0;
int *const curErr = &errNum;    // curErr will always point to errNum
const double dval = 3.1415926;
const double *const pip = dval    // pip is a const pointer to a const object

*pip = 2.72;    // error:pip is a pointer to const
*curErr = 0;    // ok:reset the value of the object to which curErr is bound

The easiest way to understand these declarations is to read them from right to left.


Note 22 (P64): Top-level const: object itself is const. Top-level const can appear in any object type.

Low-level const: appears in the base type of compound types such as pointers or references.

int i = 0;
int *const p1 = &i;    // we can't change the value of p1; const is top-level
const int ci = 42;    // we cannot change the value of ci; const is top-level
const int *p2 = &ci;    // we can change p2; const is low-level
const int *const p3 = p2;    // right-most const is top-level, left-most is low-level
const int &r = ci;    // const in reference types is always low-level

When we copy an object, top-level consts are ignored while low-level consts are never ignored.  When copyting, both objects must have the same low-level const qualification or there must be a conversion between the types of the two objects. In general, we can convert a nonconst to const but not the other way round.

int *p = p3;    // error:p3 has a low-level const but p does not
p2 = p3;    // ok: p2 has the same low-level const qualification as p3
p2 = &i;    // ok:we can convert int* to const int*
int &r = ci;    // error:can't bind an ordinary int& to a const int object
const int &r2 = i;    // ok:can bind const int& to plain int

Note 23 (P65): A constant expression is an expression whose value cannot change and that can be evaluated at compile time. 

const int max_files = 20;    // max_files is a constant expression
const int limit = max_files+1;    // limit is a constant expression
int staff_size = 27;    // staff_size is not a constant expression
const int sz = get_size();    // sz is not a constant expression

C++ 11: We can ask the compiler to verify that a variable is a constant expression by declaring the variable in a constexpr declaration. Variables declared by constexpr are implicitly const and must be initialized by constant expressions:

constexpr int mf = 20;    // 20 is a constant expression
constexpr int limit = mf+1;    // mf+1 is a constant expression
constexpr int sz = size();    // ok only if size is a constexpr function

constexpr functions must be simple enough so that the compiler can evaluate them at compile time.

Generally, it is a good idea to use constexpr for variables that you intend to use as constant expressions.


Note 24 (P67): constexpr imposes a top-level const on the objects it defines.

const int *p = nullptr;    // p is a pointer to const
constexpr int *q = nullptr;    // q is a const pointer
A constexpr pointer can point to a const or a nonconst type:
constexpr int *np = nullptr;    // np is a constant pointer to int that is null
int j = 0;
constexpr int i = 42;    // type of i is const int
constexpr const int *p = &i;    // p is a constant pointer to const int i, equal to const int *const p = &i
constexpr int *p1 = &j;    // p1 is a constant pointer to the int j, equal to int *const p1 = &j

Note 25 (P67): Traditionally we use typedef to define a type alias.

C++ 11: alias declaration:

using SI = Sales_item;    // SI is a synomnym for Sales_item, equivalent to typedef Sales_item SI;


Note 26 (P68): 

typedef char *pstring;
const pstring cstr = 0;    // cstr is a constant pointer to char, equivalent to char *const cstr = 0
const pstring *ps;    // ps is a pointer to a constant pointer to char

pstring is pointer to char, so const pstring is constant pointer to char.


Note 27 (P68): auto tells the compiler to deduce the type from initializer. By implication, a variable that uses auto as its type specifier must have an initializer.

We can define multiple variables using auto. Because a declaration can involve only a single base type, the initializers for all the variables in the declaration must have types that are consistent with each other.

auto i = 0, *p = &i;    // ok:i is int and p is pointer to int
auto sz = 0, dval = 3.14;    // error:inconsistent type for sz and dval


Note 28 (P69, P70): When we use a reference, we are really using the object to which the reference refers. 

auto ordinarily ignores top-level consts. Low-level consts, such as when an initializer is a pointer to const, are kept.

When we ask for a reference to an auto-deduced type, top-level consts in the initializer are not ignored.

int i = 0, &r = i;
auto a = r;    // a is an int
const int ci = i, &cr = ci;
auto b = ci;    // b is int (top-level const in ci is dropped)
auto c = cr;    // c is int (cr is an alias for ci whose const is top-level)
auto d = &i;    // d is int*
auto e = &ci;    // e is const int* (& of a const object is low-level const)
const auto f = ci;    // deduced type of ci is int, f has type const int
auto &g = ci;    // g is a const int& that is bound to ci
auto &h = 42;    // error:we can't bind a plain reference to a literal
const auto &j = 42;    // ok:we can bind a const reference to a literal
auto k = ci, &l = i;    // k is int, l is int&
auto &m = ci, *p = &ci;    // m is a const int, p is pointer to const int
auto &n = i, *p2 = &ci;    // error:n is reference to int, p2 is pointer to const int

Note 29 (P70, P71): decltype returns the type of its operand. The compiler analyzes the expression to determine its type but does not evaluate the expression.

decltype(f()) sum = x;    // sum has whatever type f returns, the compiler does not call f

decltype returns the type of the variable, including top-level const and references.

const int ci = 0, &cr = ci;
decltype(ci) x = 0;    // x is const int
decltype(cr) y = x;    // y is const int& and is bound to x
decltype(cr) z;    // error:z is a reference and must be initialized

decltype returns a reference type for expressions that yield objects that can stand on the left-hand side of the assignment.

decltype((variable)) is always a reference type.

int i=42, *p = &i, &r = i;
decltype(r+0) b;    // ok:b is an uninitialized int
decltype(*p) c;    // error:c is int& and must be initialized
decltype((i)) d;    // error: d is int& and must be initialized
decltype(i) e;    // ok: e is an uninitialized int

Note 30 (P77): C++ programs also use preprocessor to define header guards, and we can use it to guard against multiple inclusion as follows:

#ifndef SALES_DATA_H
#define SALES_DATA_H

...

#endif
Headers should have guards, even if they aren't yet included by another header. Header guards are trivial to write, and by habitually defining them you don't need to decide wheather they are needed.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值