Go in 2 Weeks #02 ??Complete Guide
이 글의 핵심
Go pointers, slices, and maps for C++ developers: safe *T, len/cap/append, map lookup with ok, and how slices differ from std::vector. Part of the 2-week Go series.
Series overview
?�� Go in 2 Weeks #02 | Full series index
This post covers Days 3?? of the two-week Go curriculum for C++ developers.
Previous: [#01 Philosophy & syntax](/en/blog/go-series-01-philosophy-syntax/ ??| ??Next: [#03 OOP & composition](/en/blog/go-series-03-oop-composition/
Introduction: the world of safe pointers
In C++, pointer arithmetic (p++, p + offset) lets you walk memory freely?�but segmentation faults come with the territory. Go has pointers but no pointer arithmetic. Safety was chosen on purpose. This article compares Go pointers and core data structures?�slices and maps?�to C++.
You will learn:
- Go pointer restrictions and safety
- Call by value vs pointers
- Slice length and capacity
- Map usage and pitfalls
Real-world notes
Lessons from adopting Go in real projects.
Moving from C++ to Go
I spent over a decade building servers in C++. Go felt almost too simple at first. In production, that simplicity became a strength. Takeaways:
- Faster delivery: work that took three days in C++ often took one in Go
- Stability: fewer memory-leak worries with the GC
- Deployment: single static binaries simplify rollout This series reflects that experience.
Table of contents
- Pointers: no arithmetic, but dereference works
- Arrays: fixed-size value types
- Slices: Go?�s dynamic arrays
- Maps: hash tables
- Exercises
1. Pointers: no arithmetic, but dereference works
C++ vs Go: pointer basics
// C++: pointer arithmetic allowed
int x = 10;
int* p = &x;
*p = 20; // dereference
p++; // pointer arithmetic (next int)
*(p + 5) = 30; // offset access
int arr[10];
int* ptr = arr;
ptr[5] = 100; // array indexing = pointer arithmetic
// Go: no pointer arithmetic
x := 10
p := &x
*p = 20 // ??dereference OK
// p++ // ??compile error: no pointer arithmetic
// *(p + 5) // ??compile error
// Use indexing for arrays
arr := [10]int{}
arr[5] = 100 // ??index access
Key differences:
- Go does not allow pointer arithmetic (safety)
- Only
*(dereference) and&(address-of) - Array access uses index syntax only
C++ vs Go: function arguments
// C++: value, pointer, reference
void byValue(int x) {
x = 100; // original unchanged
}
void byPointer(int* p) {
*p = 100; // original changed
}
void byReference(int& r) {
r = 100; // original changed
}
int main() {
int x = 10;
byValue(x); // x still 10
byPointer(&x); // x becomes 100
byReference(x); // x becomes 100
}
// Go: value or pointer (no references)
func byValue(x int) {
x = 100 // original unchanged
}
func byPointer(p *int) {
*p = 100 // original changed
}
func main() {
x := 10
byValue(x) // x still 10
byPointer(&x) // x becomes 100
}
Pointer usage:
- Small types (int, bool): pass by value
- Large structs (~64+ bytes): pass pointer
- Mutation needed: pass pointer
- Read-only: value or pointer (stay consistent)
Nil pointers
// C++: nullptr
int* p = nullptr;
if (p == nullptr) {
std::cout << "null pointer\n";
}
// Dereference ??segfault
// *p = 10; // crash!
// Go: nil
var p *int
if p == nil {
fmt.Println("nil pointer")
}
// Dereference ??panic
// *p = 10 // panic: runtime error: invalid memory address
2. Arrays: fixed-size value types
C++ vs Go: array declarations
// C++: array
int arr[5] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]); // 5
// Decays to pointer when passed to functions
void process(int* arr, int size) {
// ...
}
// Go: array (size is part of the type)
var arr [5]int = [5]int{1, 2, 3, 4, 5}
// or
arr := [5]int{1, 2, 3, 4, 5}
// or (size inferred)
arr := [...]int{1, 2, 3, 4, 5}
length := len(arr) // 5
// Passed by value?�full copy (no decay to pointer)
func process(arr [5]int) {
// entire array copied
}
Key differences:
- In Go, array size is part of the type:
[5]intand[10]intdiffer - Passing an array copies the whole array (no C-style decay)
- In practice, slices are used far more often than arrays
3. Slices: Go?�s dynamic arrays
Slices are the most common container in Go. They resemble std::vector but with different ergonomics.
C++ vs Go: dynamic arrays
// C++: std::vector
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
std::cout << "Size: " << vec.size() << "\n";
std::cout << "Capacity: " << vec.capacity() << "\n";
vec.reserve(100);
for (const auto& v : vec) {
std::cout << v << " ";
}
}
// Go: slice
package main
import "fmt"
func main() {
var slice []int // nil slice
slice = append(slice, 1)
slice = append(slice, 2)
slice = append(slice, 3)
fmt.Println("Length:", len(slice))
fmt.Println("Capacity:", cap(slice))
slice2 := make([]int, 0, 100) // len=0, cap=100
for i, v := range slice {
fmt.Println(i, v)
}
}
Slice internals
// A slice is a small struct of three fields
type slice struct {
ptr *[...]T // pointer to backing array
len int // length
cap int // capacity
}
graph LR
A[Slice] --> B["ptr: array pointer"]
A --> C["len: length"]
A --> D["cap: capacity"]
B --> E[Backing array memory]
Creating slices
package main
import "fmt"
func main() {
// 1. nil slice
var s1 []int
fmt.Println(s1 == nil) // true
// 2. literal
s2 := []int{1, 2, 3}
// 3. make (length and capacity)
s3 := make([]int, 5) // len=5, cap=5, zero-filled
s4 := make([]int, 5, 10) // len=5, cap=10
// 4. slicing
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[1:4] // [2, 3, 4], shares arr
fmt.Println(s1, s2, s3, s4, s5)
}
Slice expressions
// C++: sub-range (copy)
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int> sub(vec.begin() + 1, vec.begin() + 4); // [2, 3, 4]
// Go: slicing (shares backing array)
slice := []int{1, 2, 3, 4, 5}
sub := slice[1:4] // [2, 3, 4], indices 1..3
sub[0] = 100 // slice[1] becomes 100 too!
// slice[low:high] // low through high-1
// slice[low:] // low to end
// slice[:high] // start through high-1
// slice[:] // whole slice (still a view, not a deep copy)
copied := make([]int, len(sub))
copy(copied, sub)
Caveat: slicing shares the backing array; writes through one slice affect the other.
append and reallocation
package main
import "fmt"
func main() {
slice := make([]int, 0, 3) // len=0, cap=3
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))
slice = append(slice, 1)
slice = append(slice, 2)
slice = append(slice, 3)
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))
slice = append(slice, 4) // len=4, cap=6 (realloc!)
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))
}
Performance tip: when you know final size, use make([]T, 0, capacity).
// ??inefficient: many reallocations
slice := []int{}
for i := 0; i < 10000; i++ {
slice = append(slice, i)
}
// ??preallocate capacity
slice := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
slice = append(slice, i)
}
4. Maps: hash tables
C++ vs Go: maps
#include <unordered_map>
#include <iostream>
int main() {
std::unordered_map<std::string, int> m;
m[apple] = 100;
m[banana] = 200;
m.insert({"cherry", 300});
if (m.find("apple") != m.end()) {
std::cout << "Found: " << m[apple] << "\n";
}
int x = m[nonexistent]; // inserts "nonexistent": 0
m.erase("apple");
for (const auto& [key, value] : m) {
std::cout << key << ": " << value << "\n";
}
}
// ?�키지 ?�언
package main
import "fmt"
func main() {
m := make(map[string]int)
m[apple] = 100
m[banana] = 200
m[cherry] = 300
if v, ok := m[apple]; ok {
fmt.Println("Found:", v)
}
x := m[nonexistent] // 0, map unchanged
delete(m, "apple")
for key, value := range m { // order not defined
fmt.Println(key, ":", value)
}
}
Key differences:
- Go maps do not guarantee iteration order
- Missing keys yield the zero value without inserting (unlike
operator[]onunordered_mapin the ?�read missing key??case?�Go does not insert) - Use the comma-ok form (
v, ok := m[k]) to detect presence
Map creation
package main
func main() {
m1 := make(map[string]int)
m2 := map[string]int{
"apple": 100,
"banana": 200,
}
var m3 map[string]int
// m3[key] = 1 // ??panic: assignment to entry in nil map
v := m3[key] // ??0, read OK
m4 := make(map[string]int, 100) // hint ~100 entries
}
Map patterns
package main
import "fmt"
func wordCount(words []string) map[string]int {
counts := make(map[string]int)
for _, word := range words {
counts[word]++
}
return counts
}
func uniqueElements(numbers []int) []int {
seen := make(map[int]bool)
result := []int{}
for _, num := range numbers {
if !seen[num] {
seen[num] = true
result = append(result, num)
}
}
return result
}
func main() {
words := []string{"go", "is", "go", "is", "simple"}
fmt.Println(wordCount(words))
nums := []int{1, 2, 2, 3, 3, 3, 4}
fmt.Println(uniqueElements(nums))
}
5. Exercises
Exercise 1: reverse a slice in place
package main
import "fmt"
func reverse(slice []int) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}
func main() {
nums := []int{1, 2, 3, 4, 5}
reverse(nums)
fmt.Println(nums) // [5 4 3 2 1]
}
C++ comparison: std::reverse(vec.begin(), vec.end()).
Exercise 2: deduplicate
package main
import "fmt"
func removeDuplicates(slice []int) []int {
seen := make(map[int]bool)
result := []int{}
for _, v := range slice {
if !seen[v] {
seen[v] = true
result = append(result, v)
}
}
return result
}
func main() {
nums := []int{1, 2, 2, 3, 3, 3, 4, 5, 5}
fmt.Println(removeDuplicates(nums))
}
Exercise 3: merge two slices
package main
import "fmt"
func merge(s1, s2 []int) []int {
result := make([]int, 0, len(s1)+len(s2))
result = append(result, s1...)
result = append(result, s2...)
return result
}
func main() {
a := []int{1, 2, 3}
b := []int{4, 5, 6}
fmt.Println(merge(a, b))
}
Exercise 4: group with a map
package main
import "fmt"
type Student struct {
Name string
Score int
}
func groupByScore(students []Student) map[int][]string {
groups := make(map[int][]string)
for _, s := range students {
groups[s.Score] = append(groups[s.Score], s.Name)
}
return groups
}
func main() {
students := []Student{
{"Alice", 90},
{"Bob", 85},
{"Charlie", 90},
{"David", 85},
}
fmt.Println(groupByScore(students))
}
Wrap-up: Days 3?? checklist
Goals
- Go pointers: no arithmetic; dereference only
- Arguments: by value vs by pointer
- Arrays are fixed-size; slices are the default tool
- Understand
len,cap, andappendgrowth - Slicing shares storage?�watch aliasing
- Maps: construction, lookup, comma-ok
- Complete the four exercises
C++ ??Go cheat sheet
| C++ | Go | Notes |
|---|---|---|
int* p; p++ | p := &x (no ++) | Safety first |
std::vector<T> | []T | Simpler surface syntax |
vec.size() | len(slice) | builtin |
vec.capacity() | cap(slice) | builtin |
std::unordered_map | map[K]V | builtin type |
m.find(k) != m.end() | v, ok := m[k] | idiomatic |
Next
Next: OOP without classes?�composition over inheritance, methods and receivers.
?�� Series navigation
| Previous | Index | Next |
|---|---|---|
| [??#01 Syntax](/en/blog/go-series-01-philosophy-syntax/ | ?�� Index | [#03 OOP ??(/en/blog/go-series-03-oop-composition/ |
| Go in 2 weeks: | ||
| Curriculum ??[#01](/en/blog/go-series-01-philosophy-syntax/ ??[#02](/en/blog/go-series-02-memory-data-structures/ ??[#03](/en/blog/go-series-03-oop-composition/ ??[#04](/en/blog/go-series-04-interface/ ??[#05](/en/blog/go-error-handling-guide/ ??[#06](/en/blog/go-series-06-goroutine-channel/ ??#07 ??#08 ??#09 |
TL;DR: Go pointers are safe, slices are powerful, maps are concise?�you can do the same jobs as in C++ with less machinery.
Related reading
- [Go #01] Philosophy & syntax
- Two-week Go curriculum
- C++ stack vs heap guide
Keywords
Go slice, Go map, Go pointer, append len cap, Golang data structures, Go tutorial, C++ vector comparison.
Practical tips
Debugging
- Start from compiler warnings; reproduce with a minimal case.
Performance
- Profile before optimizing; define measurable goals.
Code review
- Align with team conventions; check edge cases and errors.
Field checklist
Before coding
- Is this the right tool for the problem?
- Will teammates maintain it?
- Does it meet performance needs?
While coding
- Warnings cleared?
- Edge cases covered?
- Errors handled?
At review
- Intent clear?
- Tests adequate?
- Docs where needed?
FAQ
Q. Where does this show up in real systems?
A. Understanding vector-vs-slice behavior, safe pointers, and map semantics?�exactly what you need for everyday Go services and CLIs.
Q. What should I read first?
A. Follow Previous / Related links at the bottom of each post, or the C++ series index.
Q. Go deeper?
A. Official Go docs and cppreference for C++ parallels.
Related posts
- Two-week Go curriculum
- C++ technical interview questions
- C++ stack vs heap basics
- C++ stack vs heap deep dive
- C++ vs Go
같이 보면 좋�? 글 (?��? 링크)
??주제?� ?�결?�는 ?�른 글?�니??
- [Go in 2 Weeks #01](/en/blog/go-series-01-philosophy-syntax/
- [Go in 2 Weeks #05](/en/blog/go-error-handling-guide/
- [Go in 2 Weeks #06](/en/blog/go-series-06-goroutine-channel/
??글?�서 ?�루???�워??(관??검?�어)
Go, Golang, C++, Pointers, Slice, Map, Memory ?�으�?검?�하?�면 ??글???��????�니??