Partilhar: WhatsApp
aulify · Sebenta
UC · Unidade de Competência · UC00669

Sebenta · C/C++ básico (UC00669)

Linguagens de sistema, memória, pointers
50h · 4.5 pontos crédito Curso: T. Desenv. Software ↗ Referencial oficial SNQ
Índice

Introdução

C é a linguagem que mudou a computação. Criada em 1972 por Dennis Ritchie nos Bell Labs, deu origem a praticamente tudo o que veio depois: Unix, Linux, Windows, MacOS, todas as outras linguagens "C-like" (C++, Java, JavaScript, C#, Go, Rust, Swift). Aprender C é aprender como o computador realmente funciona — gestão de memória, pointers, estruturas binárias.

C++ estende C com programação orientada a objectos, templates, exceptions, RAII. É a linguagem dos motores de jogos AAA (Unreal Engine), navegadores (Chrome), sistemas operativos modernos, alta-frequência financeira, e muito mais.

Esta sebenta cobre os fundamentos de C com introdução a C++. O foco é dominar pointers, memória dinâmica, structs e organização de código.

1. Por que C/C++

Características-chave de C

Onde se usa C

C++ vs C

C++ adiciona: - Classes e herança (OOP). - Templates (genéricos). - Exceptions. - Standard Template Library (STL) com vectors, maps, algorithms. - References (alternativa a pointers em muitos casos). - RAII — gestão de recursos via construtores/destrutores.

Onde se usa C++: - Jogos: Unreal Engine, motores próprios da Naughty Dog, etc. - Sistemas grandes: Chrome, Firefox, Photoshop. - Trading: alta frequência financeira. - 3D/CAD: AutoCAD, Maya, Blender (parte).

Curva de aprendizagem

C é simples mas tem armadilhas (pointers, memory leaks, undefined behavior). C++ é gigante — anos para dominar bem. Esta UC cobre o essencial; vais ficar com base sólida para continuar.

2. Setup

Compilador

Linux/Mac: já tens gcc e g++. Senão:

# Ubuntu/Debian
sudo apt install build-essential

# Mac
xcode-select --install

Windows: instala MSYS2 (msys2.org) ou usa WSL (Windows Subsystem for Linux).

Verifica:

gcc --version
g++ --version

IDE

Hello World

Cria hello.c:

#include <stdio.h>

int main() {
    printf("Olá mundo!\n");
    return 0;
}

Compilar e correr:

gcc hello.c -o hello
./hello

Flags úteis

gcc -Wall hello.c -o hello       # warnings
gcc -Wall -Wextra hello.c        # mais warnings
gcc -std=c11 hello.c             # standard C11
gcc -g hello.c -o hello          # com debug info
gcc -O2 hello.c -o hello         # optimização

3. Sintaxe básica de C

Tipos primitivos

char c = 'A';              // 1 byte (8 bits)
short s = 100;             // 2 bytes
int i = 25;                // 4 bytes (em sistemas 32/64-bit)
long L = 1234567890L;      // 4 ou 8 bytes
long long LL = 9223372036854775807LL;  // 8 bytes

unsigned int u = 4000000000U;  // só positivos

float f = 0.21f;           // 4 bytes
double d = 9.99;           // 8 bytes
long double ld = 9.999L;   // 16 bytes (em x86)

// Boolean (C99+, em <stdbool.h>)
#include <stdbool.h>
bool ok = true;

sizeof() em runtime:

printf("%zu\n", sizeof(int));    // %zu para size_t

Constantes

#define PI 3.14159         // pré-processador (substituição textual)
const int MAX = 100;       // constante (preferida)

printf e scanf

#include <stdio.h>

int main() {
    int idade;
    char nome[50];
    float altura;

    printf("Nome: ");
    scanf("%49s", nome);      // limita a 49 chars (evita overflow)

    printf("Idade: ");
    scanf("%d", &idade);      // & = endereço

    printf("Altura: ");
    scanf("%f", &altura);

    printf("%s tem %d anos e mede %.2fm\n", nome, idade, altura);
    return 0;
}

Format specifiers: - %d int decimal - %ld long - %lld long long - %u unsigned - %f float / double (em scanf precisa %lf para double) - %.2f float com 2 casas - %c char - %s string - %p pointer - %x hex - %% literal %

Operadores

Como em quase todas as C-likes:

// Aritméticos
+ - * / %
++ --

// Atribuição composta
+= -= *= /= %= &= |= ^= <<= >>=

// Comparação
== != < > <= >=

// Lógicos
&& || !

// Bitwise
& | ^ ~ << >>

// Ternário
int max = (a > b) ? a : b;

Casting

double d = 9.99;
int i = (int) d;        // 9 (trunca)

int a = 10, b = 3;
double r = (double) a / b;   // 3.333... (sem cast = 3)

Estruturas controlo

Idênticas a outras linguagens C-like (if, else, switch, for, while, do-while). Ver slides para syntax.

4. Arrays

Declaração e acesso

int numeros[5] = {1, 2, 3, 4, 5};
int vazio[10];                   // valores indefinidos!
int auto[] = {10, 20, 30};       // tamanho inferido

numeros[0] = 100;
int primeiro = numeros[0];

// Tamanho em compile-time
int n = sizeof(numeros) / sizeof(int);

Sem bounds checkingnumeros[100] = 0 compila e corre, mas pode corromper memória.

Multi-dimensional

int matriz[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

int v = matriz[1][2];   // 7

// Iterar
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", matriz[i][j]);
    }
    printf("\n");
}

Arrays como parâmetros

// Passar como pointer (não há cópia de array em C)
int soma(int arr[], int tamanho) {
    int s = 0;
    for (int i = 0; i < tamanho; i++) {
        s += arr[i];
    }
    return s;
}

// Equivalente:
int soma(int *arr, int tamanho) { ... }

Importante: dentro da função, sizeof(arr) retorna o tamanho do pointer, não do array. Tens sempre que passar o tamanho.

5. Strings

char arrays terminadas em \0

char nome[10] = "Ana";   // {'A', 'n', 'a', '\0', ...}

// Acesso char a char
nome[0] = 'B';

// Imprimir
printf("%s\n", nome);

O null terminator (\0) marca o fim. Funções como printf param ao encontrá-lo.

Funções de string (<string.h>)

#include <string.h>

char a[50] = "Ana";
char b[50];

// strlen — comprimento (sem contar \0)
size_t len = strlen(a);    // 3

// strcpy — copiar
strcpy(b, a);              // b = "Ana"

// strcat — concatenar
strcat(b, " Silva");       // b = "Ana Silva"

// strcmp — comparar
if (strcmp(a, "Ana") == 0) {
    printf("Igual!\n");
}

// strncpy / strncat — versões seguras com limite
strncpy(b, a, sizeof(b) - 1);
b[sizeof(b) - 1] = '\0';   // garantir terminação

Input seguro de strings

scanf("%s", ...) é perigoso (pode escrever fora do buffer). Usar fgets:

char linha[100];
if (fgets(linha, sizeof(linha), stdin) != NULL) {
    // remover \n no fim
    linha[strcspn(linha, "\n")] = '\0';
    printf("Leu: %s\n", linha);
}

6. Pointers — o coração do C

Conceito

Variáveis guardam valores; pointers guardam endereços de memória.

int x = 42;
int *p = &x;   // p contém o endereço de x

printf("x = %d\n", x);      // 42
printf("&x = %p\n", &x);    // endereço (ex: 0x7ffe...)
printf("p = %p\n", p);      // mesmo endereço
printf("*p = %d\n", *p);    // 42 — deref: valor apontado

Modificar através de pointer

int x = 5;
int *p = &x;
*p = 100;        // x agora vale 100

printf("%d\n", x);   // 100

Pointer NULL

int *p = NULL;

if (p != NULL) {
    printf("%d\n", *p);
}
// Deref de NULL = segfault

Pointers e arrays

int arr[5] = {10, 20, 30, 40, 50};

// Nome do array é (quase) pointer ao primeiro elemento
int *p = arr;            // = &arr[0]

printf("%d\n", *p);      // 10
printf("%d\n", *(p+1));  // 20 (próximo int)
printf("%d\n", p[2]);    // 30 (notação array funciona em pointers!)

// Aritmética de pointers
p++;                     // avança para o próximo int
printf("%d\n", *p);      // 20

// Percorrer array via pointer
for (int *q = arr; q < arr + 5; q++) {
    printf("%d ", *q);
}

Pointers e funções

// Passar por referência — pode modificar o original
void incrementar(int *n) {
    (*n)++;
}

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main() {
    int x = 5;
    incrementar(&x);
    printf("%d\n", x);   // 6

    int a = 1, b = 2;
    swap(&a, &b);
    printf("%d %d\n", a, b);  // 2 1
}

Retornar pointers

// ❌ NUNCA retornar pointer a variável local!
int *errado() {
    int x = 5;
    return &x;   // x deixa de existir após return!
}

// ✅ Retornar pointer a memória alocada
int *correcto() {
    int *p = malloc(sizeof(int));
    *p = 5;
    return p;
}
// Quem chamou tem que free()

Pointer para pointer

int x = 42;
int *p = &x;
int **pp = &p;

printf("%d\n", x);     // 42
printf("%d\n", *p);    // 42
printf("%d\n", **pp);  // 42

Usado em arrays de strings (char **argv), funções que modificam pointers, etc.

7. Memória dinâmica

malloc, free

#include <stdlib.h>

int main() {
    // Alocar memória para 10 ints
    int *arr = (int *) malloc(10 * sizeof(int));

    if (arr == NULL) {
        fprintf(stderr, "Falha de alocação\n");
        return 1;
    }

    // Usar
    for (int i = 0; i < 10; i++) {
        arr[i] = i * i;
    }

    // Libertar
    free(arr);
    arr = NULL;   // evita "use after free"

    return 0;
}

calloc — aloca + zera

int *arr = (int *) calloc(10, sizeof(int));
// arr[0]..arr[9] = 0
free(arr);

realloc — redimensionar

int *arr = malloc(5 * sizeof(int));
// ...

arr = (int *) realloc(arr, 10 * sizeof(int));
if (arr == NULL) { /* erro */ }

Stack vs Heap

Memory leaks

void problema() {
    int *p = malloc(100);
    // esqueço de free
}   // p sai de scope, mas memória continua reservada → LEAK

Ferramentas para detectar: Valgrind, AddressSanitizer.

gcc -fsanitize=address programa.c -o programa
./programa   # detecta leaks e accesses inválidos

8. Funções

Definição

// Prototype (opcional se definido antes do uso)
int somar(int a, int b);

int somar(int a, int b) {
    return a + b;
}

void imprimir(const char *msg) {
    printf("%s\n", msg);
}

int main() {
    int r = somar(2, 3);
    imprimir("Olá");
    return 0;
}

const char * = pointer a chars constantes (não modificar a string).

Recursão

int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

int main() {
    printf("%d\n", factorial(5));   // 120
}

Função com número variável de argumentos

#include <stdarg.h>

int soma(int n, ...) {
    va_list args;
    va_start(args, n);

    int total = 0;
    for (int i = 0; i < n; i++) {
        total += va_arg(args, int);
    }

    va_end(args);
    return total;
}

// soma(3, 10, 20, 30) → 60

9. Structs

Definição

struct Pessoa {
    char nome[50];
    int idade;
    double altura;
};

int main() {
    struct Pessoa p;
    strcpy(p.nome, "Ana");
    p.idade = 25;
    p.altura = 1.65;

    printf("%s, %d anos\n", p.nome, p.idade);

    // Initializer
    struct Pessoa p2 = {"Bruno", 30, 1.80};

    // Designated initializer (C99+)
    struct Pessoa p3 = {.nome = "Carla", .idade = 22};
}

typedef

Para não escrever struct sempre:

typedef struct {
    char nome[50];
    int idade;
} Pessoa;

// Agora:
Pessoa p = {"Ana", 25};

Pointer para struct

Pessoa p = {"Ana", 25};
Pessoa *ptr = &p;

// Sintaxe 1: deref + access
(*ptr).idade = 26;

// Sintaxe 2 (preferida)
ptr->idade = 26;

Struct como parâmetro

// Por valor (cópia)
void imprimir(Pessoa p) {
    printf("%s\n", p.nome);
}

// Por pointer (eficiente, evita cópia)
void imprimir(const Pessoa *p) {
    printf("%s\n", p->nome);
}

Arrays de structs

Pessoa pessoas[3] = {
    {"Ana", 25, 1.65},
    {"Bruno", 30, 1.80},
    {"Carla", 22, 1.70}
};

for (int i = 0; i < 3; i++) {
    printf("%s, %d\n", pessoas[i].nome, pessoas[i].idade);
}

10. Ficheiros

Ler

#include <stdio.h>

int main() {
    FILE *f = fopen("dados.txt", "r");
    if (f == NULL) {
        perror("Erro ao abrir");
        return 1;
    }

    char linha[256];
    while (fgets(linha, sizeof(linha), f) != NULL) {
        printf("%s", linha);
    }

    fclose(f);
    return 0;
}

Escrever

FILE *f = fopen("output.txt", "w");
if (f == NULL) {
    perror("Erro");
    return 1;
}

fprintf(f, "Linha 1\n");
fprintf(f, "Número: %d\n", 42);

for (int i = 0; i < 10; i++) {
    fprintf(f, "%d ", i);
}

fclose(f);

Modos

Ficheiros binários

typedef struct {
    char nome[50];
    int idade;
} Pessoa;

// Escrever
FILE *f = fopen("dados.bin", "wb");
Pessoa p = {"Ana", 25};
fwrite(&p, sizeof(Pessoa), 1, f);
fclose(f);

// Ler
f = fopen("dados.bin", "rb");
Pessoa lida;
fread(&lida, sizeof(Pessoa), 1, f);
fclose(f);

printf("%s, %d\n", lida.nome, lida.idade);

11. Pré-processador

Antes de compilar, o pré-processador processa directivas #:

#include <stdio.h>          // inclui ficheiro
#include "meu_header.h"     // local

#define PI 3.14159          // substituição textual
#define QUADRADO(x) ((x)*(x))  // macro

#ifdef DEBUG
    printf("Debug mode\n");
#endif

#ifndef VERSAO
    #define VERSAO 1
#endif

Header guards são essenciais para evitar inclusões duplas:

// utils.h
#ifndef UTILS_H
#define UTILS_H

int somar(int a, int b);

#endif

Ou (mais simples, compilador-específico):

#pragma once

12. Organização em múltiplos ficheiros

utils.h:

#ifndef UTILS_H
#define UTILS_H

int somar(int a, int b);
int maximo(int a, int b);

#endif

utils.c:

#include "utils.h"

int somar(int a, int b) { return a + b; }
int maximo(int a, int b) { return a > b ? a : b; }

main.c:

#include <stdio.h>
#include "utils.h"

int main() {
    printf("%d\n", somar(5, 3));
    printf("%d\n", maximo(10, 7));
}

Compilar:

gcc main.c utils.c -o programa

Ou separar compilação:

gcc -c main.c       # main.o
gcc -c utils.c      # utils.o
gcc main.o utils.o -o programa

Makefile básico

CC = gcc
CFLAGS = -Wall -g

programa: main.o utils.o
    $(CC) $(CFLAGS) main.o utils.o -o programa

main.o: main.c utils.h
    $(CC) $(CFLAGS) -c main.c

utils.o: utils.c utils.h
    $(CC) $(CFLAGS) -c utils.c

clean:
    rm -f *.o programa

Correr: make.

13. Introdução a C++

Hello world

#include <iostream>
using namespace std;

int main() {
    cout << "Olá mundo!" << endl;
    return 0;
}

Compilar: g++ hello.cpp -o hello.

Cin/cout

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

int main() {
    string nome;
    int idade;

    cout << "Nome: ";
    cin >> nome;

    cout << "Idade: ";
    cin >> idade;

    cout << "Olá, " << nome << ", " << idade << " anos." << endl;

    return 0;
}

<< em cout = "send to". >> em cin = "extract from".

Classes

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

class Pessoa {
private:
    string nome;
    int idade;

public:
    // Construtor
    Pessoa(string n, int i) : nome(n), idade(i) {}

    // Método
    void apresentar() {
        cout << "Sou " << nome << ", " << idade << " anos" << endl;
    }

    // Getter
    string getNome() const { return nome; }

    // Setter
    void setIdade(int i) { idade = i; }
};

int main() {
    Pessoa p("Ana", 25);
    p.apresentar();
}

: na construtor = initializer list (mais eficiente que atribuir no corpo).

Herança

class Empregado : public Pessoa {
private:
    double salario;

public:
    Empregado(string n, int i, double s)
        : Pessoa(n, i), salario(s) {}

    void apresentar() {
        Pessoa::apresentar();
        cout << "Salário: " << salario << "€" << endl;
    }
};

virtual em método do pai e override no filho para polimorfismo:

class Pessoa {
public:
    virtual void apresentar() { ... }
};

class Empregado : public Pessoa {
public:
    void apresentar() override { ... }
};

STL — Standard Template Library

vector — array dinâmico

#include <vector>

vector<int> nums = {1, 2, 3};
nums.push_back(4);
nums.pop_back();
nums.size();

for (int n : nums) {
    cout << n << " ";
}

string

#include <string>

string s = "Olá";
s += " mundo";
cout << s.length() << endl;   // 9
cout << s.substr(0, 3) << endl;  // "Olá" (a unicode varia)

map — dictionary

#include <map>

map<string, int> idades;
idades["Ana"] = 25;
idades["Bruno"] = 30;

for (auto& [nome, idade] : idades) {
    cout << nome << ": " << idade << endl;
}

Smart pointers (C++11+)

Para evitar new/delete manual:

#include <memory>

unique_ptr<Pessoa> p = make_unique<Pessoa>("Ana", 25);
p->apresentar();
// Auto-deleted quando p sai de scope

unique_ptr para ownership único, shared_ptr para partilhado.

14. Boas práticas

Convenções

Princípios

Armadilhas comuns

// 1. Comparação de strings
if (s1 == s2) ...     // ❌ compara endereços
if (strcmp(s1, s2) == 0) ...  // ✅

// 2. Atribuição em vez de comparação
if (x = 5) ...        // ❌ atribui (sempre verdadeiro)
if (x == 5) ...       // ✅

// 3. Off-by-one
for (int i = 0; i <= 10; i++) arr[i];  // ❌ i=10 fora!
for (int i = 0; i < 10; i++) arr[i];   // ✅

// 4. Buffer overflow
char buf[10];
strcpy(buf, "uma string muito grande");   // ❌ overflow
strncpy(buf, longstring, sizeof(buf) - 1);  // ✅
buf[sizeof(buf) - 1] = '\0';

// 5. Use after free
free(p);
*p = 5;   // ❌ UB
p = NULL; // ✅ marca-o

15. Próximos passos

Glossário

Recursos

C é exigente. Vais ter segfaults. Memory leaks. Vais perder horas a entender porque um pointer está corrupto. Mas cada bug que apanhas faz-te um programador melhor — não só em C, mas em qualquer linguagem. Os fundamentos que aprendes aqui são para sempre.