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; }
the contents ofstatic void t(C & c) { C c2; c2 = c; }
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;