Skip to content

Commit 7370528

Browse files
committed
learncpp pointer
1 parent 7dd6abe commit 7370528

File tree

1 file changed

+237
-8
lines changed

1 file changed

+237
-8
lines changed

content/posts/cpp-learn.md

Lines changed: 237 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -646,29 +646,32 @@ int main()
646646

647647

648648
## 12. Compound Types: References and Pointers
649+
- `Compound data types` (also called composite data type) are data types that can be constructed from fundamental data types (or other compound data types).
650+
-
649651
### 12.1. lvalues and rvalues
650-
- `lvalues` is an expression that evaluates to an identifiable object or function (or bit-field). Can be accessed via an identifier, reference, or pointer, and typically have a lifetime longer than a single expression or statement.
652+
- `lvalues` is an expression that evaluates to an identifiable object or function (or bit-field). Can be accessed via an identifier, reference, or pointer, and typically have a lifetime longer than a single `expression` or `statement`.
651653
- `rvalues` is an expression that evaluate to a value. Only exist within the scope of the expression in which they are used.
652654
- `lvalues` can be used anywhere an `rvalue` is expected.
653655
- An **assignment operation** requires its `left` operand to be a modifiable `lvalue` expression. And its `right` operand to be a `rvalue` expression.
654656

655657
### 12.2. References
656-
- `references` is an alias for an existing object/function. 
658+
- **references** is an alias for an existing object/function. 
659+
- Declared as `<type>& reference_name`
657660
- Any operation on the reference is applied to the object being referenced.
658661
- All references must be initialized.
659662
- Cannot be reseated.
660663
- They aren't objects
661664
- Can only accept modifiable lvalue arguments (const or non-const)
662665

663-
- `pass-by-reference` allows us:
666+
- **pass-by-reference** allows us:
664667
- to pass arguments to a function without making copies of those arguments each time the function is called. (class types)
665668
- to change the value of an argument
666669

667-
- `pass-by-const-reference` guaranteeing that the function can not change the value being referenced.
668-
- `lvalue-reference` just a reference for an existing lvalue.
669-
- `lvalue-reference-types` determines what type of object it can reference by using a single ampersand  `<type>&` .
670-
- `lvalue-reference-variable` is a variable that acts as a reference to an lvalue.
671-
- `lvalue-reference-to-const` can bind with const or non-const objects.
670+
- **pass-by-const-reference** guaranteeing that the function can not change the value being referenced.
671+
- **lvalue-reference** just a reference for an existing lvalue.
672+
- **lvalue-reference-types** determines what type of object it can reference by using a single ampersand  `<type>&` .
673+
- **lvalue-reference-variable** is a variable that acts as a reference to an lvalue.
674+
- **lvalue-reference-to-const** can bind with const or non-const objects. `const <type>& name`
672675

673676
- E.g.
674677
```cpp
@@ -727,5 +730,231 @@ int main() {
727730
}
728731
```
729732
733+
### 12.3. Pointer
734+
- **address-of-operator (&<variable>)** returns the memory address of its operand, **but not as an address literal. Instead, it returns a pointer to the operand.** This pointer holds the address value, and when passed to cout, the stream simply prints that value.
735+
- **dereference-operator  (*<address>)** returns the value at a given memory address as an lvalue, used to access the object being pointed at.
736+
- **pointer** is an object that holds a memory address as its value:
737+
- declared as `<type>* ptr_name`
738+
- This allows us to store the address of some other object to use later.
739+
- we should init the pointers.
740+
- the size of pointer is allways the same (32 or 64-bit architecture)
741+
- can assign an invalid pointer a new value, such as `nullptr`
742+
- `wild pointer`: pointer that has not been initialized is sometimes called a .
743+
- `dangling pointer`: pointer that is holding the address of an object that is no longer valid
744+
- **pointer-type** is a type that specifies a pointer (like reference-type) by using an asterisk `(<type>*)`.The type of the pointer has to match the type of the object being pointed at.
745+
- **null-pointer** (its type is `std::nullptr_t`) means something has no value. It often associated with memory address 0.
746+
747+
- **pointer-to-const**: that points to a value that cannot be modified through the pointer, but the pointer itself is not const.
748+
- declared as `const <type> ptr_name*`.
749+
- cannot change the value being pointed to, but can make the pointer point to a different address.
750+
- may also point to non-const variables.
751+
- **const-pointer**: whose stored address cannot be changed after initialization, but the value at that address can be modified.
752+
- declared as `<type>* const ptr_name`.
753+
- fixed to one address, but we can change the value at that address.
754+
- **const-pointer-to-const**: cannot be reseated (address fixed) and cannot modify the value it points to.
755+
- declared as `const <type>* const ptr_name`.
756+
- can only be dereferenced to read the value.
757+
758+
- e.g.
759+
```cpp
760+
#include <iostream>
761+
#include <cstdint> // for uintptr_t
762+
763+
int main() {
764+
int x = 42;
765+
766+
// address-of operator (&) returns a pointer to x (not an address literal)
767+
int* ptr = &x;
768+
769+
// Printing the pointer: cout prints the stored address value
770+
std::cout << "Address of x (&x): " << &x << "\n";
771+
std::cout << "Value stored in pointer (ptr): " << ptr << "\n";
772+
773+
// dereference operator (*) gives access to the value at the stored address
774+
std::cout << "Value of x via *ptr: " << *ptr << "\n";
775+
776+
// a pointer is an object that holds a memory address
777+
// we can change its value
778+
ptr = nullptr;
779+
std::cout << "Pointer reset to nullptr: " << ptr << "\n";
780+
781+
// pointer type is declared with '*'
782+
double d = 3.14;
783+
double* pd = &d; // type matches: double* for double
784+
// uintptr_t lets us see the raw numeric value of the address
785+
std::cout << "Numeric address of d: " << (uintptr_t)pd << "\n";
786+
std::cout << "Value of d via *pd: " << *pd << "\n";
787+
788+
// NullPTR =======
789+
int* myNullPtr {}; // value-initialized to nullptr
790+
int* myNullPtr2 {nullptr}; // explicitly initialized to nullptr
791+
792+
// Old C-style (still works, but less safe in C++):
793+
// int* myNullPtr3 {NULL}; // requires <cstddef>
794+
795+
// Const ptr =======
796+
int a = 10;
797+
int b = 20;
798+
// 1. pointer-to-const (const int*)
799+
const int* p1 = &a; // can point to non-const variable
800+
// *p1 = 15; // ❌ error: cannot modify value through p1
801+
p1 = &b; // ✅ can point to another address
802+
std::cout << "p1 points to: " << *p1 << '\n';
803+
// 2. const-pointer (int* const)
804+
int* const p2 = &a; // must be initialized, fixed address
805+
*p2 = 30; // ✅ can modify the value at that address
806+
// p2 = &b; // ❌ error: cannot change stored address
807+
std::cout << "p2 points to: " << *p2 << '\n';
808+
// 3. const-pointer-to-const (const int* const)
809+
const int* const p3 = &b; // fixed address + read-only value
810+
// *p3 = 40; // ❌ error: cannot modify value
811+
// p3 = &a; // ❌ error: cannot reseat pointer
812+
std::cout << "p3 points to: " << *p3 << '\n';
813+
814+
815+
return 0;
816+
}
817+
```
818+
819+
### 12.4. Pass by value/reference/address
820+
821+
```cpp
822+
#include <iostream>
823+
#include <string>
824+
825+
void printByValue(std::string val) // The function parameter is a copy of str
826+
{
827+
std::cout << val << '\n'; // print the value via the copy
828+
}
829+
830+
void printByReference(const std::string& ref) // The function parameter is a reference that binds to str
831+
{
832+
std::cout << ref << '\n'; // print the value via the reference
833+
}
834+
835+
void printByAddress(const std::string* ptr) // The function parameter is a pointer that holds the address of str
836+
{
837+
std::cout << *ptr << '\n'; // print the value via the dereferenced pointer
838+
}
839+
840+
int main()
841+
{
842+
std::string str{ "Hello, world!" };
843+
844+
printByValue(str); // pass str by value, makes a copy of str
845+
printByReference(str); // pass str by reference, does not make a copy of str
846+
printByAddress(&str); // pass str by address, does not make a copy of str
847+
848+
return 0;
849+
}
850+
```
851+
- **pass-by-address** allows us: ~ **pass-by-references**
852+
- to pass arguments to a function without making copies of those arguments each time the function is called. (class types)
853+
- to change the value of an argument
854+
- #null checking
855+
> Pass by reference when you can, pass by address when you must
856+
857+
- **!! C++ really passes everything by value**
858+
859+
### 12.5. Return by value/reference/address
860+
- **T returnValue(...)**: returns a copy (or move) of the object. The caller gets its own value.
861+
- **T& returnReference(...)** returns a reference to an existing object. The caller does not own it, so the object must outlive the reference.
862+
- **T * returnAddress(...)** returns a pointer (an address) to an object. The caller must handle the pointer carefully (ensure it’s valid and points to a live object).
863+
864+
- `return-by-reference`:
865+
- avoids making a copy of the object.
866+
- the referenced object must live beyond the scope of the function, otherwise the reference will dangle.
867+
- never return a non-static local variable or temporary by reference.
868+
- `return-by-address` works almost identically to return-by-reference.
869+
- `return-by-value` just make a copy
870+
> Prefer return by reference over return by address unless the ability to return “no object” (using nullptr) is important.
871+
872+
- e.g.
873+
```cpp
874+
#include <iostream>
875+
876+
int global = 42;
877+
878+
// Return by value: makes a copy
879+
int returnValue() {
880+
int x = 10;
881+
return x; // copy returned
882+
}
883+
884+
// Return by reference: must refer to existing object
885+
int& returnReference() {
886+
return global; // safe: global outlives the function
887+
}
888+
889+
// Return by address: returns a pointer
890+
int* returnAddress(bool valid) {
891+
if (valid)
892+
return &global; // valid pointer
893+
else
894+
return nullptr; // no object
895+
}
896+
897+
int main() {
898+
int a = returnValue();
899+
std::cout << "By value: " << a << '\n';
900+
901+
int& b = returnReference();
902+
std::cout << "By reference: " << b << '\n';
903+
b = 100; // modifies global
904+
std::cout << "Global after modification: " << global << '\n';
905+
906+
int* c = returnAddress(true);
907+
if (c) std::cout << "By address: " << *c << '\n';
908+
909+
int* d = returnAddress(false);
910+
if (!d) std::cout << "By address: got nullptr\n";
911+
}
912+
```
913+
914+
### 12.6. In/Out Params
915+
- `in-parameters`:are typically passed `by value` or `by const reference`
916+
- `out-parameters`:a function parameter that is used only for the purpose of returning information back to the caller.
917+
- Avoid out-parameters (except in the rare case where no better options exist).
918+
- Prefer pass by reference for non-optional out-parameters.
919+
920+
- e.g.
921+
```cpp
922+
#include <iostream>
923+
#include <string>
924+
925+
// In-parameter by value (cheap to copy)
926+
void greet(std::string name) {
927+
std::cout << "Hello, " << name << "!\n";
928+
}
929+
930+
// In-parameter by const reference (avoid copy for large objects)
931+
int length(const std::string& text) {
932+
return text.size();
933+
}
934+
935+
// Out-parameter by reference (rare case)
936+
void square(int input, int& output) {
937+
output = input * input;
938+
}
939+
940+
int main() {
941+
// in-parameter by value
942+
greet("Alice");
943+
944+
// in-parameter by const reference
945+
std::string msg = "Hello World";
946+
std::cout << "Length = " << length(msg) << "\n";
947+
948+
// out-parameter by reference (not preferred, but possible)
949+
int result;
950+
square(5, result);
951+
std::cout << "Square = " << result << "\n";
952+
}
953+
```
954+
955+
### 12.7. Type deduction (auto) with pointers, references, and const
956+
https://www.learncpp.com/cpp-tutorial/type-deduction-with-pointers-references-and-const/
730957
958+
### 12.8. std::optional
959+
https://www.learncpp.com/cpp-tutorial/stdoptional/
731960

0 commit comments

Comments
 (0)