new (in Java)?]
push %rax
double sqr(double x) { return x * x; }
double standardDeviation(const double arr[], int size) {
// Standard deviation cannot be computed for a single element
if (size <= 1) {
return 0.0;
}
double sum = 0.0;
// Loop through the array and add up all the elements
for (int i = 0; i < size; ++i) {
sum += arr[i];
}
double mean = sum / size;
double sum_of_squares = 0.0;
for (int i = 0; i < size; ++i) {
sum_of_squares += sqr(arr[i] - mean);
}
// Return the square root of the variance (sum of squares divided by size)
// (-1 because of a proof that fills two boards - Google AI got it wrong!)
return sqrt(sum_of_squares / size - 1);
}
double sqr(double x) { return x * x; }
void findStandardDeviationAndMean(const double arr[], int size, double &stddev, double &mean) {
// Standard deviation cannot be computed for a single element
if (size <= 1) {
stddev = mean = 0.0;
return;
}
double sum = 0.0;
// Loop through the array and add up all the elements
for (int i = 0; i < size; ++i) {
sum += arr[i];
}
mean = sum / size;
double sum_of_squares = 0.0;
for (int i = 0; i < size; ++i) {
sum_of_squares += sqr(arr[i] - mean);
}
stddev = sqrt(sum_of_squares / (size - 1));
}
double xs = ...;
int number_of_elements;
double ave, stddev;
findStandardDeviationAndMean(xs, number_of_elements, stddev, ave);
float *distance = new float;
*distance = 4.5;
* in *distance = 4.5
distance: the address
*distance: dereferenced, the data (4.5)
findStandardDeviationAndMean:
void antiqueFindStandardDeviationAndMean(const double arr[], int size,
double* stddev_pointer, double* mean_pointer) {
...
*mean_pointer = sum / size;
...
for (int i = 0; i < size; ++i) {
sum_of_squares += sqr(arr[i] - *mean_pointer);
}
...
*stddev_pointer = sqrt(sum_of_squares / (size - 1));
}
double xs = ...;
int number_of_elements;
double ave, stddev;
antiqueFindStandardDeviationAndMean(xs, number_of_elements, &stddev, &ave);
multiply_by_8
in samples/simple-pointer/code_with_pointers.cpp
int *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 -O3: code_with_pointers.s
some_number to 7: movl
$7, 40(%rsp) (around line 219)
movq %rcx, %rsi # line 91, move address of the parameter to register rsi
...
sall %3, (%rbx) # line 146, 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
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 + n
is 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.cpp
is 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
new places data in the heap
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.5, 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
double *first_negative_location(double *p, int size) {
int i = 0;
while ( i < size && *p >= 0.0 ) {
++i;
++p;
}
return p;
}
double *last_positive_location(double *p, int size) {
int i = 0;
while ( i < size && *p >= 0.0 ) {
++i;
--p;
}
return p;
}
const int SIZE = 300;
double nums[SIZE];
double *first_neg = first_negative_location(nums, SIZE),
*last_pos = last_positive_location(&nums[SIZE-1], SIZE);
if ( last_pos < first_neg ) {
cout << "No negatives before the first positive value." << endl;
first_neg + last_pos would be meaningless
int* nums_1_to_100() {
int nums[100];
for(int i = 1; i <= 100; ++i)
nums[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
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 - cannot 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