
sizeof() built-in function returns a type's size.
sizeof(T) means a value of type
T occupies n bytes.
short and long.
int short intopt long longopt intopt
sizeof(short int) | |
| ≤ | sizeof(int) |
| ≤ | sizeof(long int) |
| ≤ | sizeof(long long int) |
unsigned and signed keywords indicate integer signedness.
signed is the default and is usually omitted.
int find(int a[], int n); int find(int a[], unsigned n);
Always compare the difference between floating-point values to some epsilon.if (x == y) { /* whatever */ }
Picking the right epsilon can be tricky.if (fabs(x-y) < epsilon) {/* whatever */}
char) or two-byte
wchar_t (wide characters).
char data type signedness is compiler dependent.
signed and unsigned keywords can make it definite.
true
and false.
&& (alternatively and) and
|| (alternatively or) are short-circuit
operators.
and B2 is defined to be
B1B1?B2:false
or B2 is defined to be
B1?true:B2
and and or non-commutative.
T is a type, T * is the type pointer to T.
T * value is used as the address of a value of type T.
& generates pointer values.
v is a variable of type T, then the expression &v
is a value of type T*.
*
follows (or dereferences) a pointer value to return the associated
value.
*.
"hello world" == "hello world" may or may not be true.
<, >, <= and >=)
determine the order between two pointers pointing into the same
value sequence.
The sum of a pointer and an integer may not be a valid pointer.
new and delete.
new T allocates a storage chunk big enough to
hold a value of type T.
*.
p contains a valid value of type T *,
the expression delete p recovers the storage being pointed to by
p.
p is not changed; but it's now
invalid.
p was returned by a new statement.
[] provides access to array
elements.
sizeof() operator on an array returns the total number of
bytes in the array, not the number of elements.
a[3];, a is just a constant
pointer to the first (zeroth) element.
a[expr] is just a shorthand for
the expression *(a + expr).

struct C {
data * data_ptr;
C() : data_ptr(new data) { }
};
static void t(void) { C c; }
struct C {
data * data_ptr;
C() : data_ptr(new data) { }
~C() { delete data_ptr; }
};
static void t(void) { C c; }
struct C {
data * p;
C() : p(new data) { }
~C() { delete p; }
};
static void t(C & c) { C c2(c); }
t(), c2.p is c.p.
*(c2.p) changes *(c.p).
*(c2.p) deletes *(c.p).
void t(C c) { }
The call t(c) makes a copy of c.
C::C(const C &).
&.
struct C {
data * data_ptr;
C() : data_ptr(new data) { }
C(const C & c)
: data_ptr(data_clone(c.data_ptr)) {}
~C() { delete data_ptr; }
};
static void t(C & c) { C local_c(c); }
struct C {
data * p;
C() : p(new data) { }
C(const C & c)
: p(data_clone(c.p)) {}
~C() { delete p; }
};
static void t(C & c) { C c2; c2 = c; }
static void t(C & c) { C c2; c2 = c; }
the contents of c.p replaces the contents of
c2.p.
c2.p is garbage.
c2.p and c.p are sharing storage.
=.
=.
struct C {
data * data_ptr;
C() : data_ptr(new data) { }
C(const C & c) : data_ptr(data_clone(c.data_ptr)) {}
~C() { delete data_ptr; }
// Bad example.
C & operator = (const C & c) {
delete data_ptr;
data_ptr = data_clone(c.data_ptr);
return *this;
}
};
a = a, and
a = b = c.
a = a self-assignment does occur in code.
C & C::operator = (const C & rhs) {
delete data_ptr;
data_ptr = copy_data(rhs.data_ptr);
return *this;
}
a = a?
a = a, *this = rhs.
data_ptr deletes rhs.data_ptr.
*this is different from rhs.
*this and rhs are different, proceeds as normal.
*this and rhs are the same, be careful.
C & C::operator = (const C & rhs) {
if (this != &rhs) {
delete data_ptr;
data_ptr = copy_data(rhs.data_ptr);
}
return *this;
}
x = y = z = 0.0;
T & operator = (const T & rhs)
T operator = (const T & rhs)
makes a more expensive copy. (Another one!)
*this.
widget w1 = w2; w1 = w2;