#include <iostream>
#include <cstring>
using namespace std;

void timesFour(int *num){
	*num *= 4; //what the pointer points to equals itself times 4
	return;
}

int main(){
	int var1 = 3;
	float var2 = 3.1415926536;
	string var3 = "Hello World!";
	char var4[] = "Goodbye";

	int *ptr1 = nullptr;
	cout << "ptr1: " << ptr1 << endl; //nullptr has a special address of 0
	ptr1 = &var1; // CAUTION!  Tricky syntax!!!
	float *ptr2 = &var2;
	string *ptr3 = &var3;
	char *ptr4 = var4;
	char *ptr5 = &var4[0];

	cout << "\n*** Memory Addresses: ***" << endl;
	// &varname means "the address of varname" (i.e., where is it in memory)
	cout << var1 << " is at address: " << &var1 << endl;
	cout << var2 << " is at address: " << &var2 << endl;
	cout << var3 << " is at address: " << &var3 << endl;

	cout << "\n*** Pointer Addresses: ***" << endl;
	cout << "ptr1: " << ptr1 << endl;
	cout << "ptr2: " << ptr2 << endl;
	cout << "ptr3: " << ptr3 << endl;

	cout << "\n*** Pointer Contents: ***" << endl;
	cout << "ptr1: " << *ptr1 << endl; // "what pointer 1 points to"

	// change the value using the pointer
	*ptr1 = 5;
	cout << "ptr1: " << *ptr1 << endl; // "what pointer 1 points to"

	// use the pointer in a function == "call-by-reference"
	timesFour(ptr1);
	cout << "ptr1: " << *ptr1 << endl; // "what pointer 1 points to"
	cout << "var1: " << var1 << endl;
	*ptr2 += 10;
	cout << "ptr2: " << *ptr2 << endl; // "what pointer 2 points to"
	cout << "var2: " << var2 << endl;

	// strings don't work like the above...
	string var5 = "Goodbye, cruel world!";
	ptr3 = &var5; //just make the pointer point to a new string variable
	cout << "var3: " << var3 << endl;
	cout << "ptr3: " << *ptr3 << endl; // "what pointer 3 points to"

	cout << "var4: " << var4 << " is at address: " << &var4 << endl;
	// a pointer to an array points to the first element's address.
	cout << "ptr4: " << *ptr4 << endl;
	cout << "var4[0]: " << var4[0] << " is at address: " << &var4 << endl;

	// if you have an address, you can print the value that lives at an address:
	cout << *(&var4) << endl;

	cout << "Pointer arithmetic (specific index) example:\n";
	cout << "*(ptr4 + 1): " << *(ptr4 + 1) << endl; // "what pointer 3 points to"
	cout << "var4[1]: " << var4[1] << endl;
	cout << "*(ptr4 + 2): " << *(ptr4 + 2) << endl; // "what pointer 3 points to"
	cout << "var4[2]: " << var4[2] << endl;
	cout << "*(ptr4 + 3): " << *(ptr4 + 3) << endl; // "what pointer 3 points to"
	cout << "var4[3]: " << var4[3] << endl;

	// use pointer arithmetic to print each character in a loop
	cout << "Pointer arithmetic (loop) example:\n";
	for(int i=0; i<strlen(var4); i++){
		cout << *(ptr4 + i);
	}
	cout << endl;
	// conveniently, you can just do this:
	cout << "Printing a pointer to a character array: " << ptr4 << endl;
	// and it prints the whole character array that the pointer points to.

	cout << "ptr5: " << *ptr5 << endl;
	ptr5++; // incrementing a character pointer advances to the next character
	cout << "After ptr5++ (incrementing the pointer):" << endl;
	cout << "ptr5: " << *ptr5 << endl;

	return 0; 
}