Menu
Coddy logo textTech

A Regra do Primeiro Membro

Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 29 de 61.

Quando você incorpora uma struct como o primeiro membro de outra struct, algo poderoso acontece: o endereço de memória da struct externa é idêntico ao endereço de memória de seu primeiro membro. Isso é garantido pelo padrão C.

Considere nosso exemplo anterior:

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // First member
    int grade;
} Child;

Quando você cria uma variável Child, o membro Parent fica logo no início da memória do Child. Isso significa que um ponteiro para Child aponta para exatamente o mesmo endereço que um ponteiro para o seu membro Parent.

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Cast seguro!

Ambos os ponteiros contêm o mesmo endereço. Isso permite que você passe um Child* para qualquer função que espere um Parent*:

void print_id(Parent* p) {
    printf("ID: %d\n", p->id);
}

// No main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Funciona perfeitamente

Esta técnica é a base da herança em C. Uma função escrita para o tipo base pode operar em qualquer tipo derivado, desde que a base seja incorporada primeiro. O cast é seguro porque os layouts de memória se alinham perfeitamente.

challenge icon

Desafio

Fácil

Vamos explorar a regra do primeiro membro construindo uma hierarquia simples de animais. Você criará uma struct base Animal e uma struct derivada Dog, e então demonstrará como um ponteiro para Dog pode ser convertido com segurança para um ponteiro para Animal.

O conceito principal que você aplicará: quando Animal é o primeiro membro de Dog, ambas as structs compartilham o mesmo endereço de memória inicial. Isso significa que você pode passar um Dog* para qualquer função que espere um Animal* através da conversão (casting) do ponteiro.

Você organizará seu código em três arquivos:

  • animal.h: Defina ambas as structs com guards de inclusão. Sua struct Animal deve ter um único campo: legs (um inteiro representando o número de patas). Sua struct Dog deve incorporar Animal como seu primeiro membro e adicionar um campo name (um array de caracteres de 50 posições). Também declare uma função print_animal que recebe um parâmetro Animal*.
  • animal.c: Implemente a função print_animal. Esta função trabalha com o tipo base — ela só conhece Animal e imprime quantas patas o animal possui.
  • main.c: Crie uma variável Dog e preencha seus campos (tanto as patas do animal incorporado quanto o nome do cão). Em seguida, demonstre a regra do primeiro membro convertendo seu Dog* para um Animal* e passando-o para print_animal. Também imprima o nome do cão separadamente para mostrar o quadro completo.

Você receberá duas entradas: o nome do cão (uma string) e o número de patas (um inteiro).

Sua saída deve ser parecida com esta:

Dog: Buddy
Legs: 4

Onde Buddy é o nome do cão e 4 é o número de patas. Imprima o nome do cão primeiro a partir de main.c, depois chame print_animal com o ponteiro convertido para exibir as patas.

Isso demonstra como uma função escrita para o tipo base (Animal) pode funcionar perfeitamente com um tipo derivado (Dog) através da conversão de ponteiro — a base da herança em C.

Folha de consulta

Quando uma struct é incorporada como o primeiro membro de outra struct, ambas as structs compartilham o mesmo endereço de memória inicial. Isso é garantido pelo padrão C.

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // Primeiro membro
    int grade;
} Child;

Um ponteiro para a struct externa pode ser convertido com segurança para um ponteiro para seu primeiro membro:

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Conversão segura - mesmo endereço

Isso permite passar um ponteiro de tipo derivado para funções que esperam o tipo base:

void print_id(Parent* p) {
    printf("ID: %d\n", p->id);
}

Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Funciona porque os layouts de memória se alinham

Esta técnica é a base da herança em C, permitindo que funções escritas para tipos base operem em tipos derivados.

Experimente você mesmo

#include <stdio.h>
#include <string.h>
#include "animal.h"

int main() {
    // Ler entrada
    char name[50];
    int legs;
    scanf("%s", name);
    scanf("%d", &legs);
    
    // TODO: Criar uma variável Dog
    
    // TODO: Definir o nome do cachorro usando strcpy
    
    // TODO: Definir o número de pernas (acesso através do Animal incorporado)
    
    // TODO: Imprimir o nome do cachorro no formato: "Dog: <name>"
    
    // TODO: Realizar o cast de Dog* para Animal* e chamar print_animal
    // Isso demonstra a regra do primeiro membro!
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming