C/C++

Table of Contents

1. C Syntax

1.1. Definition

Functions and variables is defined in two steps: declaration and initialization.

They can be declared and defined separately or simultaneously.

int a; // declaration
a = 10; // initialization

int b = 10;
int f(int a);
int f(int);   // argument name is optional, but recommended for description

int f(int a) {
  return 2 * a;    
}

1.2. Pointer

A variable stored in the memory that holds memory address.

#include <stdio.h>
int n = 5;
int *p = &n;      // initalize ('*' = 'pointer to') an integer. e.g. p = 0x7999990D
printf("%p\n", (void *)p);
printf("%d", *p); // print the ('*' = 'dereference of') the pointer

Deference The type of the pointer determines how to dereference the pointer. Therefore, the pointer to unknown type, void *, must be typecasted before being used.

Pointer to a struct stores the first memory address of the struct. The arrow operator p->m can be used to access the member of the pointed struct. It is equivalent to deferencing the struct and useing the dot operator (*p).m

Pointer to an array of struct automatically increments by the multiple of the size of the struct, when incremented by 1.

Function Pointer Declaration of function pointer takes special form.

int (*f_ptr)(int);
f_ptr = &f; // or just f_ptr = f

An example from signal.h that defines a function that takes integer (int) and function pointer (void (*)(int)) and returns a function pointer (void (*...)(int)).

void (*signal(int, void (*)(int)))(int);

For clarity the same code can be written as follows:

typedef void (*sig_t)(int); // defining type "sig_t"

sig_t signal(int, sig_t);

1.3. Array

Array is declared with its size in square bracket, and the value can be initialized with braces.

#include <stdio.h>
int int_arr[5] = {1, 2, 3, 4, 5}; // aggregate initialization
char str_arr[] = "hello";
char *str_ptr = "world";  // this is special case
printf("%d\n", int_arr[2]);
printf("%s\n", str_arr);
printf("%s\n", str_ptr);

1.4. Struct

Sequence of data bundled together.

1.5. Control Flow

if

switch

switch (expression) {
 case x:
   break;
 default:
   break;
 }

The matching continues to the next case unless break; is encountered.

1.6. Const

Define an unmodifiable variable for compiler.

  • const int *p fixes the dereference *p
  • int *const p fixes the pointer p

1.7. Macro

  • #define VAR VAL
  • #define MACRO(<args>) (EXPRESSION)
    • ## can be used for literal concatenation
  • #include <...> searches the default locations.
  • #include "..." searches current directory.

1.8. extern

  • External Linkage

Function is always linked externally, the symbols are exposed and linked to the definition in the other file.

On the other hand variables are linked internally, that is, defined privately, by default. Each file declares its own variable. extern T a keyword is used in order to share the same variable.

1.9. lvalue and rvalue

lvalue (locator value, or left value of assignment) has a fixed memory address, and & operator can be used.

rvalue (read value, or right value of assignment) is temporary or literal that does not have persistent memory address.

Unnamed non-trivial expression is an prvalue (pure rvalue). e.g. 1+1.

1.10. Others

volatile

  • Indicate that the thing is not persistent.
  • Tells the compiler not to do anything smart such as eliminating duplicate codes. This is useful for memory-mapped hardwares.

typedef struct { ... } <type>

  • Only the <type> needs to be used for type specification in contrast to struct <ident> { ... } for which struct name needs to be used.

2. C++ Syntax

C++ Standard is refered using the year it was published. e.g. C++23.

2.1. Reference

Pointer with compile-time reference counts.

  • Reference to a reference is not allowed.
  • References do not need to be dereferenced.

2.1.1. rvalue Reference

  • T&&

It refers to rvalue, which enables the transfer of temporary objects.

The compiler has much more freedom about the exact machine code, and the copy of large object is often eliminated.

lvalue can be casted into rvalue by std::move in utility.

2.2. Range-For

Loop over the elements

std::vector<int> v;

for (auto &x: v) {
  <body>
    }

2.3. Structured Binding

Unpacking the values into multiple variables

std::unordered_map<std::string, std::string> m;

for (auto &[k, v]: m) {
  <body>
    }

Struct can be unpacked as well.

struct S {
  int a;
  std::string b;
};
S s();

auto [x, y] =  s();

2.4. Lambda Function

auto lambda = [](int x) { return x * x; };

Unnamed function object called functor is defined.

  • [] capture surrounding variables.
    • [=] capture all by value, [x] capture x by value
    • [&] capture all by reference, [&x] capture x by reference.

The return type is inferred, but it can also be specified explicitly:

auto double = [](int n) -> int { return 2*n; };

The lambda function itself can be used with this const auto& since C++23:

auto fib = [](this const auto& self, int n){
  if (n <= 1) return n;
  return self(n-2) + self(n-1);
 };

2.5. Class

struct is extended to have the exact same functionalities of class. The difference is that the members of struct is public and inherited publicly, by default.

2.5.1. Method

Any non-static member function can use this that points to the object itself.

2.5.2. Constructor

class Foo {
  Foo(<params>) : <member initialization> {
    <body>
  }
#optional
  Foo(std::intializer_list<T>) {
    <body>
      }
}
  • member initalization happens during object construction
    • It is a comma seperated list of constructor calls: e.g. a(5), b{param1, param2}.
    • References and const variables are required to be initialized here.
Foo a{1, 2, 3};

Here, {} invokes the list initialization which is the second constructor. Narrowing (of the type) is not allowed when {} is used.

2.5.3. Destructor

Invoked automatically when the object gets out of the scope. Useful for memory management.

class Foo {
  ~Foo() {
    <body>
      }
}

2.5.4. Inheritance

class Child : public Parent {
  private:
    ...
  protected:
    ...
  public:
    ...
}
  • public keeps the public state of the properties and methods.
  • protected change public into protected
  • private change public and protected into private

2.5.5. Abstract Class

Class can have virtual methods and destructor:

class ABS {
  virtual int method1(int a) = 0; // purely virtual
  virtual int method2(int b) {
    ...
    } // default implementation
  virtual ~ABS() = default; // you want this
};

class Derived : public ABS {
  int method1(int a) override {
    ...
  }
  int method2(int b) override {
    ...
  }
  ~Derived() override {
    ...
  }
};

Virtual methods are registered to a special private property called virtual table (vtable) that exists for the base class and its derivatives. Purely virtual methods are marked as no implementation.

When the child class override the methods, they are registered to the vtable that survives the upcasting into the base class. When the method is being called from the base class, they look the address in the vtable and call the method of the derived class.

The same applies for the destructor as well. You need to set the destructor of abstract class virtual, because you want to call the destructor of the concrete class. The concrete destructor would also call the parent destructor implicitly, so the virtual destructor will never get replaced, only get extended.

2.5.6. Operator Overloading

The standard operator can be redefined by defining operator<op> method for the first argument of <op>. The friend operator<op> can be used in the case the main object is on the right of <op>.

2.6. Specifier

2.6.1. constexpr

Evaluated at compile time

constexpr int var = 20;

2.6.2. noexcept

The function is asserted to not throw error. It is okay if every error within the function is catched.

void safe_function() noexcept {
  // no exception is allowed
}

If exception is raised within safe_function, the normal unwinding is bypassed, and std::terminate is called immediately.

The specifier can be applied conditionally with noexcept(<condition>)

2.7. C++ Header

  • No .h extension

Modern header files stored in

  • /usr/include/c++/<version>/
  • /usr/include/x86_64-linux-gnu/c++/<version>/
  • /usr/include/

std namespaces are used by the standard libraries.

2.8. Template

template <typename T>
// function, class, `using`

Template is a blueprint that is instantiated on demand at compile-time.

Template is instantiated for specific types based on the content of the C file. The proper compiler need to see the instances. Otherwise they will not be available for linking. The template can be instantiated manually by declaring instances with template class Class<Type>;.

2.8.1. SFINAE

  • Substitution Failure Is Not An Error

When template instantiation fails, the compiler looks for another matching template instead of throwing an error. If every template fails, then error is thworn.

2.8.2. Concept

  • #include <concepts>
  • Since C++20, via the definition of __cpp_concepts

A template is instantiated if the concepts are met.

Concepts can be asserted in two ways. In the template

template<Incrementable T>
void f(T);

or in the requires clause

template<T> requires Incrementable<T>
void f(T);

2.9. Attribute

  • [[maybe_unused]] suppress unused warning for a variable
[[maybe_unused]] std::string a{"hello"};

3. C Libraries

3.1. stdio.h

  • printf
    • %s
    • %d
    • %f
  • scanf
  • FILE *fopen
  • fprintf(FILE *file, const char *format, ...)
  • fscanf

3.2. stdlib.h

  • void *malloc(size_t size)
  • void free(void *ptr)
  • char *getenv(const char *name) get environment variable
  • int system(const char *string) execute the command given by string
  • int rand(void) generate random number
    • void srand(unsigned int seed) set seed
  • void exit(int status)

3.3. stdarg.h

  • Variadic arguments. (Unknown number of arguments)

3.4. unistd.h

  • ssize_t read(int fd, void buf[.count], size_t count)
  • exec family
    • execl(const char *path, const char *arg, ..., (char *)NULL); variable-length argument list
    • execv(const char *path, char *const argv[]); array of strings
    • exec*p(const char *file, ...); PATH-searching variant
    • exec*e(..., char *const envp[]); set environment variables
  • brk() set the program break
  • sbrk() increment the program break and return the previous address

3.5. errno.h

  • define the global variable errno

3.6. signal.h

  • The signals can come from the environment.
  • void (*signal(int sig, void (*func)(int)))(int)
    • Sets the signal handler.
    • Predefined Signals: =SIGINT=(4), =SIGTERM=(6), …
    • Signal Handlers: =SIGDFL=(1), =SIGERR=(2), =SIGIGN=(3)
  • int raise(int sig)

3.7. math.h

  • mathematical functions

3.8. string.h

  • string(char *) manipulation

3.9. sys/types.h

  • size_t

    • Typically unsigned int that represent the size. It might vary depending on the system?
    • ssize_t
      • Typically int that represent the size, or the error value -1.
      • If -1 is returned, the errno global variable is expected to set to a number that representing the error message.
    • mode_t
      • the type for the file permission node
      • Constants are predefined: S_IRWXU, S_IRUSR, …
      • It is the 5 digit octal number for the file permission. See ((669f0999-3389-4a74-92df-c0842ecd3c3c))
  • sys/stat.h (include sys/types.h)
    • mkdir
    • mknod
    • chmod
    • umask

4. C++ Libraries

4.1. IO Libraries

4.1.1. iostream

  • std::cout the output stream object
    • << operator outputs the right argument, and return this.
  • std::cin
    • >> operator reads into the right argument, and return this.

4.1.2. print

  • Since C++23, via the definition of __cpp_lib_print
  • std::print(std::format_string<Args...> fmt, Args&&... args) Python-like print function. std::print
  • std::println

4.2. Rich-pointer Libraries

4.2.1. memory

  • std::unique_ptr<T> wrapper of a pointer with automatically destructor according to the type T.
    • The pointer returned by malloc does not have destructor attached to it, and manual free is required.
  • std::shared_ptr<T> wrapper of a pointer with reference count over multiple thread.

4.2.2. string

  • std::string wrapper object of a heap-allocated string
  • std::string_view type that can reference both std::string and string literal.

4.2.3. array

  • std::array<T, length> stack-allocated fixed-size array

4.2.4. vector

  • std::vector<T> heap-allocated dynamic array
    • .push_back(int n)

4.2.5. expected

  • Since C++23

This library enables monadic error handling.

  • std::expected<T, E> object with error subtype defined by std::unexpected(...).
    • .and_then(<lambda>) run on success
    • .or_else(<lambda>) run on failure
    • .value() the success value
    • .valule_or(<default>) return default in case of failure

4.2.6. utility

  • std::move the ownership of the heap-allocated data is moved.
    • returning moved variable is unnecessary due to the Return Value Optimization (RVO) is already there.

4.2.7. ranges

4.2.7.1. Range Adaptors
  • Since C++20
  • std::ranges::views:: or std::views::

Create a view object out of iterable with a pipe (|) symbol. e.g.

std::vector<int> numbers = {1, 2, 3, 4};
auto view = numbers | std::views::filter([](int n) {return n % 2 == 0; });
  • filter(<lambda>)
  • transform(<lambda>)
  • all(), counted()
  • take(<n>), drop(<n>)
  • join(), reverse()
  • iota(<start>, <end>) generate sequence
  • repeat(<value>) (C++23)

4.2.8. span

  • Since C++20
  • std::span<T> object that refers to any contiguous sequence: std::string, std::vector, std::array, C array.

4.3. Others

4.3.1. algorithm

4.3.2. thread

  • std::this_thread current thread object
  • std::thread thread object initialized by giving it a function to execute
  • C++23 std::jthread thread object with auto-joining after completion, and cancellation

5. Assembly

5.1. x8664

Register name with prefix

  • r: 64 bits
  • e: 32 bits,

additionally

  • ax, bx, cx, dx: 16 bits
  • al, ah, …: 8 bits

Lables are defined with : at the end. A label can starts with .. The label is equivalent to literal memory address.

5.1.1. GNU Syntax

  • AT&T Syntax

Use prefixes for different kinds of symbols:

  • Directives: .
  • Register: %
  • Immediate Value: $,

Refer to memory like

  • disp(base,index,scale),

Binary operations act on the second argument.

Instruction with suffix

  • b (byte): 8 bits
  • w (word): 16 bits
  • l (long): 32 bits
  • q (quadword): 64 bits.

The data is read and wirrten as little endian, with the given address being the first address of the byte sequence.

5.1.2. Intel Syntax

  • Directive .intel_syntx

Do not have prefixes,

Refer to memory like

  • [expr] where expr can be arithmetic expression consists of register name and numbers,

Binary operations act on the first argument.

BYTE PTR, WORD PTR, DWORD PTR, QWORD PTR right after the instruction indicates the width of the operation.

5.1.3. Instructions

Instruction Description
mov A, B copy
add A, B, sub A, B, mul A, B  
lea A, B load effective address: load the address of the memory reference
call LABEL, ret subroutine

5.2. syscall

It emits the instruction int 0x80 on Linux.

  • %rax system call number
  • %rdi, %rsi, %rdx, %r10, … arguments for the call
%rax Name Description
0 read  
1 write  
2 open  
3 close  
60 exit  

5.3. Assembler Directives

Directive Description
.ascii "..." insert string data
.asciz "..." insett string with zero attached
.global SYMBOL expose SYMBOL to ld
.text, .data, .bss set secion
.equ VAR, VAL define a symbol
.skip SIZE, FILL fill memory of SIZE with FILL

5.4. Call Frame Information

  • CFI

Keep track of the stack frames in the debugging section.

CFI is necessary when

  • the compiler does optimization (-fomit-frame-pointer) and not store former %rbp
  • exception handling
  • need cross-platform consistency

5.5. Procedure Linkage Table

  • PLT

The table contains the virtual addresses of the loaded shared objects, allowing different processes to use the shame physical memory for executables and shared objects.

For example, printf@plt initially points to the PLT setup code and on the second time it is redirected to the loaded printf function directly.

6. Compilation

6.1. Compiler

6.1.1. gcc

GNU Compiler Collection (formerly, GNU C Compiler)

  • Takes care of all the preprocessing (cpp), (proper) compilation (cc1), assembly (as), and linking (ld).

General Options

  • -o Set the name of the target, by default an executable
  • -c Stop before linking
  • -S Stop before assembly
  • -E only perform preprocessing, and output to the standard out
  • -g debugging
    • -ggdb add debugging information for gdb

Compiler Options

  • -I <dir> add the directory to the list of directory to be searched for header files.
    • /usr/include/, /usr/local/include/ is searched by default.
    • The prefixes = or $SYSROOT is replaced by sysroot prefix.
  • -e <entry> specify the entry point.
  • -nostdlib, -nolibc
  • -pie produce a dynamically linked Position Indepnedent Executable.
    • It is the default behavior. -no-pie to disable.

Linker Options

  • -l LIBRARY or -lLIBRARY
  • -LDIR add DIR to the list of library directory
    • /usr/lib by default
  • -shared produce a shared object
6.1.1.1. g++

gcc with the C++ library used by default when linking.

C++ Version

  • -std=c++NN

6.1.2. clang

LLVM based C/C++ Compiler

  • clang -S -emit-llvm FILE output the intermediate representation

6.2. Build System

6.2.1. make

  • Parallel compilation tool

Options

  • -k, --keep-going compile as much as possible
  • -j [N], --jobs [N] number of simultaneous jobs. unlimited if N is not given.
6.2.1.1. Syntax
6.2.1.1.1. Variable

Variables are simply defined

VAR = value

and can be accessed anywhere with $(VAR).

Variable expansion happens after the special character exchange.

6.2.1.1.2. Rule

A rule is defined as follows

target ...: prerequisite ...
    recipe
    ...

Prerequisites are other targets, and recipes are any commands.

Recipes must starts with TAB, unless .RECIPEPREFIX is set otherwise.

The first target that does not begin with . is the default goal.

make checks if a given goal matches any of the targets, then executes the matched recipe.

6.2.1.1.2.1. Pattern Rule

Target can contain % that matches any nonempty string.

% can be reused in the prerequisites to refer back to the matched string.

6.2.1.1.2.2. Automatic Variables
  • $@ the target name
  • $< the first prerequisite
  • $? space-separated list of newly modified prerequisites
  • $^ all prerequisites
6.2.1.1.3. Recipe
6.2.1.1.3.1. @

Stop the output of the command itself.

6.2.1.1.3.2. +

Force execution in the -n (no execution) mode.

6.2.1.2. Phony Targets
.PHONY: clean
clean:
    rm *.o temp

Specify that the target file clean is not a file. make does not check the existence of the phony target file.

6.2.2. cmake

Options

  • -S SRCDIR -B BUILDDIR -Dvar=val generate the build files
  • --build BUILDDIR
  • --install BUILDDIR
    • --prefix CMAKE_INSTALL_PREFIX set the target directory for installation
6.2.2.1. Syntax

Specified in CMakeLists.txt

  • project(<name> [LANGUAGES <lang>]) define project
  • find_package(<package name> [REQUIRED]) looks for <package name>Config.cmake and use the package
  • find_library()
  • add_library() add tartget library. e.g. .o, .a, .so
  • add_executable() add target executable
  • traget_link_libraries()

6.3. cpp

C Preprocessor

6.4. as

GNU Assembler

  • -o output file name
  • -g debug information

6.5. ld

GNU Linker (Loader)

  • -l<namespec> seach in lib<namespec>.a or lib<namespec>.so
    • It only searches for the symbols that the linker has seen so far. The library has to be specified after the source file.
    • If <namespec> looks like :filename, filename is searched instead.
  • -( <filenames or -l options> -) search repeatedly until no undefined references left

7. Language Servers

7.1. clangd

7.1.1. Configuration

clangd can be configured in multiple ways.

  • .cland: configuration in YAML format
  • compile_commands.json: automatically generated by CMake, or captured with the tools like bear.
  • compile_flags.txt: plain text flags

Structure of .clangd looks like:

CompileFlags:
  Add: [-std=c++20 ...]

8. Utilities

8.1. nm

Dump the symbol table and their attributes from a binary executable file.

8.2. objdump

  • -d disassemble
  • -r emit relocation record

8.3. readelf

Read the metadata of ELF file.

  • -a all metadata

8.4. ldd

Print the shared objects required by program

8.5. strace

Trace system calls and signals

8.6. ltrace

Trace library calls

9. Debugging

9.1. gdb

  • r[un] execute the whole program
  • s[tart] execute and stop at the start of main function
  • frame [N], f [N] see and change stack frame (going in and out of function context)
  • info, i show information
    • b breakpoint
    • locals local variables
    • args CLI arguments
  • s[tep] execute one line of the source code
  • c[ontinue] execute until a breakpoint
  • finish run until the current function returns
  • until N run until the line N
  • br N set a breakpoint

C-x 2 rotates through the TUI.

9.2. coredumpctl

Linux kernel stores the coredump as specified by /proc/sys/kernel/core_pattern.

Kernel often alegate the handling of coredump file to the systemd-coredump. Which can be controled by coredumpctl

  • list
  • info MATCH
  • dump MATCH
  • debug MATCH run gdb by default

If MATCH is not specified, the last coredump is used.

9.3. ulimit

Get or set file size limit

$ ulimit -c should return unlimited for the coredump to work?

10. References

Author: Jeemin Kim

Created: 2026-08-09 Sun 07:11