float *distance = new float;
*distance = 4.5;
distance
: the address
*distance
: dereferenced, the data (4.5)
samples/simple-pointer/code_with_pointers.cpp
float *pointer_to_some_number = &some_number;
-
set pointer to some_number
's address
&
(in code, as a unary operator)
computes the address of something
g++ -S -O
: code_with_pointers.s
some_number
to 1e30: movl
$0x7149f2ca, 40(%rsp)
(around line 157)
$0x7149f2ca
is the hex representation of the
floating-point value 1e30 - floating-point values aren't handled
directly by the assembler, so the compiler has to compute its
representation
movq %rcx, %rbx # line 37, move parameter (the address) to rbx ... sall %3, (%rbx) # line 98, arithmetic shift left by 3 bits # (the optimizer combined the *= 2 and << 2)
int *p;if the
p
is in register %rrr, then *p
would
be (%rrr)
- register indirect
multiply_by_8
:
void multiply_by_8(int &num) { num *= 8; // no dereference operator! - compiler does the work } ... // call: multiply_by_8(value); // no address operator!
num
refers to a valid value - no
runtime exceptions for "null" data
void compute_stats(float nums[], int count, double &ave, double &stdev);
nullptr
int *intp = nullptr;We used to use
NULL
or 0
for this, but that
was error-prone because it conflated integers and addresses
- use nullptr
float scores[10]; float *score_pointer = nullptr; score_pointer = scores;
score_pointer = &scores[0];
*score_pointer = 3.5;to set
scores[0]
to 3.5;
score_pointer += 1;advances
score_pointer
to scores[1]
score_pointer + nis precisely equivalent to adding
n *
sizeof(float)
to score_pointer
in machine code
X* p;the code
p + N
gives you
(X*)(int64_t(p) + N * sizeof(*p))
float *p = scores; for(int i = 0; i < 10; ++i, ++p) { *p = 1.0 / (i + 1); }initializing scores elements to 1.0, 0.5, 1/3.0, ..., 0.1
pointers.cpp
g++ -S pointers.cppis in the file
pointers.s
pointers.s
:
.Lnn
: each line has a label; this is for the debugger
.ascii
: constants that appear in the cod
fib()
's name is _Z3fibi
: the final i
means this is the version that takes a single integer - if have other
versions, those will have additional parameters
report()
: _Z6reportPii
: Pi = int*, i = int
ToDo *todays_activities = new ToDo(); todays_activities->add("eat");
->
instead of .
complex
(Stroustrup, p. 49, with small modifications)
operator+
: defined outside of the class
==
is dangerous!!!
==
does not need to check types - C++ type
system ensures only appropriate values passed in
complex operator-(complex a, complex b) { return a += b; }
new
do? (in Java)
complex base_impedance(1, 2), device_impedance(3, 5); ... base_impedance += device_impedance; ... cout << base_impedance.imaginary();
Student
, Course
, Section
, Instructor
Account
, Transaction
new
)
bignum
allows you to compute with
arbitrary sized integers if you need really big numbers
bignum x = fact(50); x++; if (x
> 1000) ...
int
values
new
/heap/memory management
string
is the same
vector
, other containers will be the same
const
methods: do not change the state of the object
double x; complex c(4.0, 2.0); // some code here x += c.real();If only
const
methods are called on c
between the definition and the x +=
line, then we can optimize
this to
double x; complex c(4.5, 2.0); // some code here x += 4.5;
float *nums = new float[100];allocates 100 floats on the heap and stores it in
nums
float *temp = new float[200]; for(int i = 0; i < 100; ++i) temp[i] = nums[i]; nums = temp;
ToDo
class
(reimplementing todo.cpp
):
class ToDo { public: ToDo(); bool empty() const; bool full() const; void add(string next_item); string next_item(); private: string *items; int count, capacity; }; ToDo::ToDo() : count{0}, capacity{100}, items{new string[100]} { } bool ToDo::empty() const { return count == 0; } bool ToDo::full() const { return false; } // WHY? void ToDo::add(string next_item) { if ( count >= capacity ) { string *old_items = items; items = new string[capacity * 2]; capacity *= 2; for(int i = 0; i < count; ++i) items[i] = old_items[i]; // note: would delete old_items here - more later... } items[count] = next_item; ++count; } string ToDo::next_item() { if ( empty() ) throw "Removing item from empty todo list."; string result = items[0]; for(int i = 0; i < count - 1; ++i) items[i] = items[i + 1]; --count; return result; }
ToDo
StringNode
string operator[]
string&
+---------------+ |Type | | | |field: ___ | |field: ___ | +----------------+ |field: O------+------------->|Type | | | | | +---------------+ |field: ____ | +----------------+
void set_up_today() { ToDo stuff_to_do; stuff_to_do.add("sleep"); stuff_to_do.add("study"); stuff_to_do.remove("sleep"); ...
complex
is just like an array of doubles:
complex nums[100];
class ManyDoubles { private: double nums[100]; public: ManyDoubles(); double set(int ix, double val) { nums[ix] = val; } double sum() const; }; ManyDoubles::ManyDoubles() { // fails: double nums[100] = {0.0}; // declares a new array!! // also doesn't work: // nums = {}; - does nothing! // // CORRECT INITIALIZATION: for(int i = 0; i < 100; ++i) nums[i] = 0.0; } double ManyDoubles::sum() const { double result = 0.0; for(int i = 0; i < 100; ++i) result += nums[i]; return result; }
int nums[4]; ... nums = {10, 9, 8, 7}; // ILLEGAL
vector
, but we are
using arrays for the moment so you get more used to pointers
int* nums_1_to_100() { int nums[100]; for(int i = 1; i <= 100; ++i) num[i - 1] = i; return nums; }
string* make_initials(char first, char last) { string initials = " "; initials[0] = first; initials[1] = last; return &initials; }
new
when creating objects, not &
string *mystr; *mystr = string("my name here");likely causes memory problems!
int *x
some times and int* x
others?
int *x, y; // x is a pointer, y is not! int* a, b; // a is a pointer, b is not
float* create_array(float a, float b);but it could be written as
float *create_array(...
init_array.c
in demo code
optimized_init_array.s
%rcx
movq $1, (%rcx)
(%rcx)
: %rcx
is the address of
the array, not a value
(%register)
: treat this as an address, not a value
movq
- can't determine how much to move
by simply checking the size of the register
destination[i] = i * destination[i - 1]
:
i
: in %rax
imulq -8(%rcx,%rax,8), %rdx
-8(%ebp,%esi,4)
:
address is %ebp + %esi * 4 - 8
(%rcx)
: equivalent to 0(%rcx,0,1)
%rax
and (%rax)
(without notes)
-8(%rcx,%rax,8)
: %rcx + %rax*8 - 8
(i - 1)
%movq %rdx, (%rcx,%rax,8)
: store result in array at
address %rcx + %rax*8
(that
is, destination[i]
)
rcx
is the address of the array -
where from?
main
: leaq 32(%rsp), %rcx
lea
: load effective address - not a mov!
factorials
in the stack frame
%rcx
initialize_factorials
returns, have
movl 144(%rsp), %eax
fully_optimized_init_array.s
in demo code directory
gcc -S -O4 init_array.c
-O4
: optimization level 4 (the max)
initialize_factorials
simply loads the values into
the array with an "unrolled" loop!
movl $1278945280, %eax
int
, float
)
new