C typedef

O typedef (de type definition, "definição de tipo") dá um apelido a um tipo que já existe. Com ele, struct Aluno pode virar só Aluno, e um double que guarda dinheiro pode se chamar Dinheiro. Ele não cria nada de novo na memória e não muda o que o programa faz: só deixa o código mais curto e mais fácil de ler. Você já usou vários typedef sem saber: size_t, FILE e int32_t são apelidos criados assim, dentro dos cabeçalhos da biblioteca.

Você vai aprender
  • A criar um apelido com typedef tipo Apelido;.
  • Por que o apelido não é um tipo novo, e o que isso deixa passar.
  • As três formas de usar typedef com struct, e qual escolher.
  • Como escrever uma struct que aponta para ela mesma (o erro unknown type name).
  • Por que não esconder um ponteiro num typedef, e por que typedef é melhor que #define para tipos.
  • De onde vêm size_t, FILE e int32_t, e o que quer dizer o _t.

Esta aula supõe que você já leu Structs e Structs e Ponteiros. Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c/td, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.


A ideia: o apelido

Na escola da Ana, o professor de matemática se chama José Carlos de Oliveira, mas todo mundo o chama de Zé. "Zé" não é outra pessoa: é o mesmo professor, com um nome mais curto. Quem o chama de "Zé" e quem o chama de "José Carlos" está falando dele do mesmo jeito, e ninguém precisa avisar a secretaria.

O typedef é isso para os tipos: typedef struct Aluno Aluno; diz ao compilador "quando eu escrever Aluno, entenda struct Aluno". O tipo é o mesmo; só ganhou um nome a mais.

Na escolaEm C
O nome completo: José Carlos de OliveiraO tipo original: struct Aluno, double, unsigned long
O apelido: ZéO nome do typedef: Aluno, Dinheiro, size_t
"Zé" e "José Carlos" são a mesma pessoaO apelido e o original são o mesmo tipo: dá para misturar à vontade

1. O primeiro typedef

A lojinha da Ana guarda preços em double. Para deixar claro, no código, quais double são dinheiro, ela cria um apelido:

#include <stdio.h>

typedef double Dinheiro;

int main(void) {
    Dinheiro preco = 19.90;
    Dinheiro desconto = 2.50;
    double pago = preco - desconto;

    printf("Preço: R$ %.2f\n", preco);
    printf("Pago:  R$ %.2f\n", pago);
    printf("sizeof(Dinheiro) = %zu, sizeof(double) = %zu\n",
           sizeof(Dinheiro), sizeof(double));
    return 0;
}
typedef double Dinheiro;
A forma é typedef + o tipo que já existe + o apelido novo + ;. Um jeito de lembrar: escreva uma declaração de variável comum (double Dinheiro;) e ponha typedef na frente. O que seria o nome da variável vira o nome do tipo. Fica fora das funções, no começo do arquivo, para valer no arquivo todo.
Dinheiro preco = 19.90;
Daqui em diante, Dinheiro pode ser usado em qualquer lugar onde caberia double: variáveis, parâmetros, retorno de funções, sizeof.
double pago = preco - desconto;
Misturar Dinheiro e double é permitido, sem conversão nenhuma: é o mesmo tipo.
%.2f
O printf nem sabe que existe Dinheiro: para ele é um double, e o formato continua sendo o do double.
$ gcc -std=c23 -Wall -Wextra dinheiro.c -o dinheiro
$ ./dinheiro
Preço: R$ 19.90
Pago:  R$ 17.40
sizeof(Dinheiro) = 8, sizeof(double) = 8

Os dois sizeof dão 8: Dinheiro é um double, byte por byte. A vantagem aparece no dia em que a Ana decidir guardar os preços em centavos, num long long: ela muda uma linha (typedef long long Dinheiro;) em vez de caçar quais dos double do programa eram dinheiro. (Os %.2f também teriam de mudar; o typedef não resolve tudo.)

O apelido não protege nada

Como o apelido é o mesmo tipo, o compilador não reclama se você trocar um pelo outro. A Ana criou dois apelidos para int:

#include <stdio.h>

typedef int Idade;
typedef int Ano;

void fazer_aniversario(Idade *idade) {
    (*idade)++;
}

int main(void) {
    Idade ana = 29;
    Ano hoje = 2026;

    fazer_aniversario(&ana);
    fazer_aniversario(&hoje);

    printf("Ana tem %d anos; estamos em %d.\n", ana, hoje);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra mistura.c -o mistura      « nenhum aviso
$ ./mistura
Ana tem 30 anos; estamos em 2027.

O fazer_aniversario(&hoje) é claramente um engano (o ano fez aniversário?), mas Ano * e Idade * são os dois int *, e nem o gcc nem o Clang avisam. O typedef serve para documentar a intenção para quem lê, não para o compilador conferir. Se você quiser que o compilador barre a mistura, embrulhe o valor numa struct (typedef struct { int valor; } Ano;): duas structs diferentes nunca se misturam.


2. typedef com struct

Este é o uso mais comum. Em C, o nome do tipo de uma struct são duas palavras: struct Aluno (aula Structs). Com um typedef, vira uma só. Dá para escrever a struct e o apelido de uma vez:

#include <stdio.h>

typedef struct Aluno {
    char nome[30];
    double nota;
} Aluno;

void mostrar(const Aluno *a) {
    printf("%s: %.1f\n", a->nome, a->nota);
}

int main(void) {
    Aluno ana = {"Ana", 9.5};
    struct Aluno bia = {"Bia", 8.0};
    mostrar(&ana);
    mostrar(&bia);
    printf("Uma ficha ocupa %zu bytes.\n", sizeof(Aluno));
    return 0;
}
typedef struct Aluno { … } Aluno;
Duas coisas numa frase só: define a struct struct Aluno (o "tipo" do typedef é a struct inteira, com as chaves) e dá a ela o apelido Aluno (a palavra entre o } e o ;).
struct Aluno / Aluno
Repare que o nome aparece duas vezes, igual. Não há conflito: o C guarda os nomes de struct (as "etiquetas", tags, que vêm depois da palavra struct) numa lista separada dos outros nomes. struct Aluno e Aluno podem conviver, e aqui são o mesmo tipo.
const Aluno *a
Um ponteiro para uma ficha que a função não vai mudar (aula Structs e Ponteiros). Sem o apelido, seria const struct Aluno *a.
struct Aluno bia
O nome completo continua valendo: ana e bia são do mesmo tipo, e as duas vão para o mesmo mostrar.
$ gcc -std=c23 -Wall -Wextra ficha.c -o ficha
$ ./ficha
Ana: 9.5
Bia: 8.0
Uma ficha ocupa 40 bytes.

(Os 40 bytes são os 30 do nome, 2 de enchimento e os 8 da nota; veja sizeof.)

As três formas

Você vai encontrar três jeitos de escrever a mesma coisa:

FormaCódigoO que dá para escrever depois
a) struct e typedef separadosstruct Aluno { … };
typedef struct Aluno Aluno;
Aluno e struct Aluno
b) Tudo junto, com etiquetatypedef struct Aluno { … } Aluno;Aluno e struct Aluno
c) Tudo junto, sem etiqueta (struct "anônima")typedef struct { … } Aluno;Só Aluno

As três funcionam para quase tudo. A forma (c) é a mais curta, e muito usada (aparece em Arquivos Binários); o preço é que a struct não tem nome próprio, e struct Aluno deixa de existir. As formas (a) e (b) são necessárias quando a struct precisa falar dela mesma, como você vai ver na seção 3. Este guia usa a (b) quando precisa da etiqueta e a (c) quando não precisa.

Missão: o mapa do jogo da Ana escreve struct Ponto seis vezes. Crie o apelido Ponto (com qualquer uma das três formas) e use-o em todo lugar: na função somar e nas variáveis do main. A saída continua Herói em (3, 2).

#include <stdio.h>

struct Ponto {
    int x;
    int y;
};

struct Ponto somar(struct Ponto a, struct Ponto b) {
    struct Ponto r = {a.x + b.x, a.y + b.y};
    return r;
}

int main(void) {
    struct Ponto heroi = {2, 3};
    struct Ponto passo = {1, -1};
    heroi = somar(heroi, passo);
    printf("Herói em (%d, %d)\n", heroi.x, heroi.y);
    return 0;
}
#include <stdio.h>

typedef struct Ponto {
    int x;
    int y;
} Ponto;

Ponto somar(Ponto a, Ponto b) {
    Ponto r = {a.x + b.x, a.y + b.y};
    return r;
}

int main(void) {
    Ponto heroi = {2, 3};
    Ponto passo = {1, -1};
    heroi = somar(heroi, passo);
    printf("Herói em (%d, %d)\n", heroi.x, heroi.y);
    return 0;
}

O mesmo vale para enum e union: typedef enum { … } Status; e typedef union { … } Valor; (aulas Enums e Unions).


3. A struct que aponta para ela mesma

Um nó de lista ligada guarda um valor e um ponteiro para o próximo nó, que é do mesmo tipo. Com a forma (c), sem etiqueta, parece natural escrever assim:

#include <stdio.h>

typedef struct {
    int valor;
    No *prox;
} No;

int main(void) {
    No b = {2, NULL};
    No a = {1, &b};
    printf("%d %d\n", a.valor, a.prox->valor);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra autoref.c -o autoref
autoref.c:5:5: error: unknown type name ‘No’
    5 |     No *prox;
      |     ^~
autoref.c: In function ‘main’:
autoref.c:10:16: error: initialization of ‘int *’ from incompatible pointer type ‘No *’ [-Wincompatible-pointer-types]
   10 |     No a = {1, &b};
      |                ^
autoref.c:10:16: note: (near initialization for ‘a.prox’)
autoref.c:11:38: error: request for member ‘valor’ in something not a structure or union
   11 |     printf("%d %d\n", a.valor, a.prox->valor);
      |                                      ^~
error: unknown type name ‘No’
"Nome de tipo desconhecido: No". O compilador lê o arquivo de cima para baixo, e o apelido No só passa a existir no } No; do fim. Na linha 5, dentro das chaves, ele ainda não existe. É o erro que importa; os outros dois são consequência.
initialization of ‘int *’ from incompatible pointer type
Sem saber o que é No, o gcc tratou o campo prox como int * para continuar lendo, e aí &b (um No *) não combina. Corrija o primeiro erro e este some.

O Clang, no editor, mostra só o primeiro: error: unknown type name 'No'. O conserto é usar a etiqueta, que já existe desde o struct No do começo:

#include <stdio.h>

typedef struct No {
    int valor;
    struct No *prox;
} No;

int main(void) {
    No b = {2, NULL};
    No a = {1, &b};
    for (No *p = &a; p != NULL; p = p->prox) {
        printf("%d\n", p->valor);
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra autoref2.c -o autoref2
$ ./autoref2
1
2

Dentro das chaves, o campo usa o nome completo, struct No *prox; fora delas, o apelido No já vale. A outra saída é a forma (a) com o typedef antes da struct: typedef struct No No; numa linha e, depois, struct No { int valor; No *prox; };. Isso funciona porque um typedef pode apontar para uma struct que ainda vai ser descrita (uma declaração adiantada); assim o apelido já vale lá dentro.

Missão: a fila de pedidos da Ana não compila: rode e leia o unknown type name. Conserte dando uma etiqueta à struct (typedef struct Item { … } Item;) e usando struct Item *prox; dentro das chaves. A saída deve ser os números 1, 2 e 3, um por linha.

#include <stdio.h>

typedef struct {
    int pedido;
    Item *prox;
} Item;

int main(void) {
    Item c = {3, NULL};
    Item b = {2, &c};
    Item a = {1, &b};
    for (Item *p = &a; p != NULL; p = p->prox) {
        printf("%d\n", p->pedido);
    }
    return 0;
}
#include <stdio.h>

typedef struct Item {
    int pedido;
    struct Item *prox;
} Item;

int main(void) {
    Item c = {3, NULL};
    Item b = {2, &c};
    Item a = {1, &b};
    for (Item *p = &a; p != NULL; p = p->prox) {
        printf("%d\n", p->pedido);
    }
    return 0;
}

4. Não esconda o ponteiro

Dá para fazer typedef de um ponteiro: typedef char *Texto;. Parece prático, mas confunde, e o caso do const mostra por quê. A Ana quis prometer que mostrar não muda o texto:

#include <stdio.h>

typedef char *Texto;

void mostrar(const Texto t) {
    t[0] = 'A';
    printf("%s\n", t);
}

int main(void) {
    char nome[] = "ana";
    mostrar(nome);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra texto.c -o texto      « nenhum aviso
$ ./texto
Ana

A função mudou o texto, e o compilador aceitou. O motivo: const Texto não é "const + char *" colados como palavras. O const vale para o tipo inteiro do apelido, que é o ponteiro: const Texto t quer dizer char *const t, "o ponteiro t não muda" (não pode apontar para outro lugar), e as letras continuam livres. A promessa que a Ana queria era const char *t, "as letras não mudam" (aula const e Ponteiros). Sem o apelido, o compilador barra a escrita:

$ gcc -std=c23 -Wall -Wextra texto2.c -o texto2      « o mesmo programa, com const char *t
texto2.c: In function ‘mostrar’:
texto2.c:4:10: error: assignment of read-only location ‘*t’
    4 |     t[0] = 'A';
      |          ^

"Atribuição a um lugar só de leitura, *t". No editor, o Clang diz error: read-only variable is not assignable. Moral: deixe o * à vista. Quem lê char *t sabe na hora que é um ponteiro, que pode ser NULL e que talvez precise de free; quem lê Texto t não sabe. A exceção aceita é o ponteiro para função, cujo tipo é tão difícil de ler que o apelido ajuda muito: typedef int (*Operacao)(int, int); (aula Ponteiros para Funções).

Apelido para um array

Também dá, e às vezes é útil para tamanhos fixos que se repetem:

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

typedef char Nome[16];

int main(void) {
    Nome cliente = "Ana";
    Nome outro;
    strcpy(outro, "Bia");
    printf("%s e %s, %zu bytes cada\n", cliente, outro, sizeof(Nome));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra nome.c -o nome
$ ./nome
Ana e Bia, 16 bytes cada

typedef char Nome[16]; segue a mesma receita: a declaração char Nome[16]; com typedef na frente. Nome cliente é um char cliente[16]. Cuidado com o mesmo problema de antes: um Nome passado para uma função vira um char *, como qualquer array (aula Ponteiros e Arrays), e o sizeof lá dentro dá o tamanho de um ponteiro (8 no gcc), não 16.


5. typedef × #define

Um #define (aula Macros) também parece criar um nome para um tipo. Mas ele só troca texto antes da compilação, e com ponteiros isso dá errado:

#include <stdio.h>

#define TEXTO char *

int main(void) {
    TEXTO primeiro = "Ana", segundo = "Bia";
    printf("%s e %s\n", primeiro, segundo);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra define.c -o define
define.c: In function ‘main’:
define.c:6:39: error: initialization of ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
    6 |     TEXTO primeiro = "Ana", segundo = "Bia";
      |                                       ^~~~~
define.c:7:19: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘int’ [-Wformat=]
    7 |     printf("%s e %s\n", primeiro, segundo);
      |                  ~^               ~~~~~~~
      |                   |               |
      |                   char *          int
      |                  %d

O pré-processador trocou TEXTO por char *, e a linha virou char * primeiro = "Ana", segundo = "Bia";. Numa declaração com vírgula, o * vale só para o primeiro nome: segundo é um char, uma letra só, e não cabe um texto nele ("inicializar um char com um char * transforma ponteiro em inteiro"). No editor, o Clang diz error: incompatible pointer to integer conversion initializing 'char' with an expression of type 'char[4]' [-Wint-conversion]. Com typedef char *Texto;, os dois seriam ponteiros, porque o typedef é entendido pelo compilador como um tipo de verdade, e não como texto colado. Para tipos, use sempre typedef.


6. Os typedef da biblioteca e o _t

Lembra do size_t, do FILE, do int32_t? São todos apelidos, criados nos cabeçalhos que você inclui. Dá para ver no Linux da Ana com o comando grep, que procura um texto dentro de arquivos:

$ grep -n 'typedef.*size_t;' /usr/lib/gcc/x86_64-linux-gnu/15/include/stddef.h
229:typedef __SIZE_TYPE__ size_t;
231:typedef long ssize_t;
$ grep -n 'typedef' /usr/include/x86_64-linux-gnu/bits/types/FILE.h
7:typedef struct _IO_FILE FILE;
$ grep -n 'typedef.* int32_t;' /usr/include/x86_64-linux-gnu/bits/stdint-intn.h
26:typedef __int32_t int32_t;
$ grep -n '__int32_t;' /usr/include/x86_64-linux-gnu/bits/types.h
41:typedef signed int __int32_t;
typedef __SIZE_TYPE__ size_t;
__SIZE_TYPE__ é um nome que o próprio gcc define com o tipo certo para a máquina: unsigned long no Linux de 64 bits. É por isso que o size_t tem 8 bytes no gcc e 4 no editor do guia: o apelido é o mesmo, o tipo por trás muda. O código que usa size_t funciona nos dois sem mudar nada. Essa é a maior utilidade do typedef: esconder o que muda de máquina para máquina.
typedef struct _IO_FILE FILE;
O FILE do fopen (aula Arquivos) é uma struct da glibc com um apelido, na forma (a). Você só usa FILE * e nunca mexe nos campos lá dentro.
int32_t → __int32_t → signed int
Um apelido de um apelido: no fim, um int32_t é um int comum (aula Inteiros de Tamanho Fixo).
O sufixo _t

O _t de size_t, time_t e int32_t quer dizer type, "tipo". É a convenção da biblioteca padrão. A norma POSIX (a dos sistemas como o Linux) reserva para ela mesma todos os nomes terminados em _t, para poder criar tipos novos no futuro sem bater nos seus. Por isso, nos seus programas, prefira nomes como Aluno, Dinheiro e Ponto, com letra maiúscula, que é o estilo deste guia. Você vai ver muito código com aluno_t; funciona, só não é o recomendado.

O primo do C23: typeof C23

O typeof(expressão) dá o tipo de uma variável ou expressão, sem você precisar escrevê-lo: typeof(preco) troco = 50 - preco; cria troco com o mesmo tipo de preco (num programa com Dinheiro preco = 19.90;, ele mostrou Troco: R$ 30.10 (8 bytes) no gcc e no editor). Ele não substitui o typedef, que dá um nome ao tipo; é útil dentro de macros. Veja Novidades do C99 ao C23.


Qual usar

SituaçãoRecomendação
Uma struct, enum ou union usada em vários lugarestypedef, forma (c) typedef struct { … } Nome;
Uma struct que aponta para ela mesma (listas, árvores)Forma (b) typedef struct Nome { … struct Nome *prox; } Nome; ou (a) com o typedef antes
Um número com um significado (dinheiro, medida, identificador)typedef, sabendo que ele documenta mas não protege
Um tipo que muda de máquina para máquinaOs da biblioteca (size_t, int32_t) ou um typedef seu num cabeçalho
Ponteiro para funçãotypedef: fica muito mais legível
Ponteiro comum (char *, Aluno *)Sem typedef: deixe o * à vista
Nome para um tipo com #defineNão: use typedef
Nem todo mundo usa typedef em struct

Alguns projetos grandes, como o núcleo do Linux, proíbem typedef para structs: preferem escrever struct aluno sempre, para que fique claro, em cada linha, que aquilo é uma struct. Outros, como muitas bibliotecas de jogos e de interface gráfica, usam typedef em tudo. As duas escolhas são boas; o importante é seguir a do projeto em que você está.


Quando dá errado

error: unknown type name ‘Aluno’; use ‘struct’ keyword to refer to the type
$ gcc -std=c23 -Wall -Wextra semtypedef.c -o semtypedef
semtypedef.c: In function ‘main’:
semtypedef.c:9:5: error: unknown type name ‘Aluno’; use ‘struct’ keyword to refer to the type
    9 |     Aluno ana = {"Ana", 9.5};
      |     ^~~~~
      |     struct
«… mais erros, todos consequência deste …»

O que significa: o programa tem struct Aluno { … }; mas nenhum typedef, e usa Aluno sozinho. O gcc até sugere: "use a palavra struct". No editor, o Clang diz error: must use 'struct' tag to refer to type 'Aluno'.

Como consertar: escreva struct Aluno ana, ou crie o apelido: typedef struct Aluno Aluno; depois da struct.

error: unknown type name ‘No’ dentro da própria struct

O que significa: a struct usa o apelido antes de ele existir (seção 3).

Como consertar: dê uma etiqueta à struct e use struct No *prox; dentro das chaves, ou ponha typedef struct No No; antes da struct.

error: storage size of ‘ana’ isn’t known com struct Aluno ana
$ gcc -std=c23 -Wall -Wextra incompleto.c -o incompleto
incompleto.c: In function ‘main’:
incompleto.c:9:12: error: variable ‘ana’ has initializer but incomplete type
    9 |     struct Aluno ana = {"Ana", 9.5};
      |            ^~~~~
«… avisos de "excess elements in struct initializer" …»
incompleto.c:9:18: error: storage size of ‘ana’ isn’t known
    9 |     struct Aluno ana = {"Ana", 9.5};
      |                  ^~~

O que significa: a struct foi criada na forma (c), typedef struct { … } Aluno;, sem etiqueta. Então struct Aluno não existe: o compilador acha que é outra struct, ainda não descrita ("tipo incompleto"), e não sabe quantos bytes ela tem ("o tamanho de ana não é conhecido"). No editor: error: variable has incomplete type 'struct Aluno'.

Como consertar: escreva só Aluno ana, ou dê a etiqueta à struct (forma b).

error: conflicting types for ‘Idade’; have ‘long int’
$ gcc -std=c23 -Wall -Wextra conflito.c -o conflito
conflito.c:5:14: error: conflicting types for ‘Idade’; have ‘long int’
    5 | typedef long Idade;
      |              ^~~~~
conflito.c:4:13: note: previous declaration of ‘Idade’ with type ‘Idade’ {aka ‘int’}
    4 | typedef int Idade;
      |             ^~~~~

O que significa: o mesmo apelido foi definido duas vezes com tipos diferentes (as linhas 4 e 5). Repetir o mesmo typedef (as linhas 3 e 4 do programa, typedef int Idade; duas vezes) é permitido desde o C11; com tipos diferentes, não. O {aka ‘int’} ("também conhecido como") é como o gcc mostra o tipo por trás de um apelido. No editor: error: typedef redefinition with different types ('long' vs 'int').

Como consertar: deixe um typedef só, num cabeçalho incluído por todos os arquivos que usam o apelido (aula Arquivos de Cabeçalho).

makes integer from pointer without a cast num tipo criado com #define

O que significa: o #define de um tipo com * só faz do primeiro nome de uma declaração com vírgula um ponteiro (seção 5).

Como consertar: troque o #define por typedef, ou declare cada variável numa linha.


Referência rápida

CódigoO que cria
typedef double Dinheiro;Dinheiro = double
typedef struct { … } Aluno;Aluno = uma struct sem etiqueta
typedef struct No { … struct No *prox; } No;No = struct No, que aponta para ela mesma
typedef struct No No;O apelido antes da descrição da struct (declaração adiantada)
typedef enum { … } Status; / typedef union { … } Valor;Apelidos para enum e union
typedef char Nome[16];Nome x; = char x[16];
typedef int (*Operacao)(int, int);Apelido para ponteiro de função
typedef char *Texto;Funciona, mas evite: const Texto é char *const
size_t, FILE, int32_t, time_tApelidos da biblioteca; o _t é reservado pelo POSIX

Exercícios

1. Um apelido simples

Complete para que Nota seja um apelido de double:

double Nota;

2. O nome que fica

Depois de typedef struct { int x; int y; } Ponto;, complete a declaração da variável p:

p = {1, 2};

3. A etiqueta

Complete o campo para a struct apontar para ela mesma:

typedef struct Celula { int valor; *prox; } Celula;

4. Com typedef int Metros; e typedef int Segundos;, o que acontece se uma função que recebe Metros for chamada com uma variável Segundos?

5. Com typedef char *Texto;, o que é const Texto t?

6. Por que size_t tem 8 bytes no gcc do Linux e 4 no editor do guia?

Missão final: o estoque da lojinha

Missão: o programa usa os tipos Dinheiro e Produto, mas ninguém os criou, e ele não compila. Escreva os dois typedef no lugar do comentário: Dinheiro como apelido de double, e Produto como uma struct com os campos nome (char[20]), quantidade (int) e preco (Dinheiro), nessa ordem. A última linha da saída deve ser Valor do estoque: R$ 364.50.

#include <stdio.h>

/* crie aqui os tipos Dinheiro e Produto */

Dinheiro valor_estoque(const Produto *itens, int n) {
    Dinheiro total = 0;
    for (int i = 0; i < n; i++) {
        total += itens[i].quantidade * itens[i].preco;
    }
    return total;
}

int main(void) {
    Produto loja[] = {
        {"Caderno", 10, 12.50},
        {"Caneta", 30, 1.99},
        {"Mochila", 2, 89.90},
    };
    int n = sizeof loja / sizeof loja[0];
    for (int i = 0; i < n; i++) {
        printf("%-8s %3d x R$ %6.2f\n", loja[i].nome, loja[i].quantidade, loja[i].preco);
    }
    printf("Valor do estoque: R$ %.2f\n", valor_estoque(loja, n));
    return 0;
}
#include <stdio.h>

typedef double Dinheiro;

typedef struct {
    char nome[20];
    int quantidade;
    Dinheiro preco;
} Produto;

Dinheiro valor_estoque(const Produto *itens, int n) {
    Dinheiro total = 0;
    for (int i = 0; i < n; i++) {
        total += itens[i].quantidade * itens[i].preco;
    }
    return total;
}

int main(void) {
    Produto loja[] = {
        {"Caderno", 10, 12.50},
        {"Caneta", 30, 1.99},
        {"Mochila", 2, 89.90},
    };
    int n = sizeof loja / sizeof loja[0];
    for (int i = 0; i < n; i++) {
        printf("%-8s %3d x R$ %6.2f\n", loja[i].nome, loja[i].quantidade, loja[i].preco);
    }
    printf("Valor do estoque: R$ %.2f\n", valor_estoque(loja, n));
    return 0;
}

Repare que o typedef double Dinheiro; tem de vir antes do Produto, que o usa: o compilador lê de cima para baixo. Depois de passar na missão, experimente trocar o double por float só na linha do typedef: o resto do programa se ajusta sozinho.


Resumo

  • typedef tipo Apelido; dá um nome novo a um tipo que já existe. A receita: escreva a declaração como se fosse uma variável e ponha typedef na frente.
  • O apelido é o mesmo tipo: mistura sem aviso. Ele documenta a intenção, mas não protege.
  • Com struct: typedef struct { … } Aluno; (sem etiqueta) ou typedef struct Aluno { … } Aluno; (com etiqueta). Uma struct que aponta para ela mesma precisa da etiqueta: struct No *prox;, senão dá unknown type name.
  • Não esconda ponteiros num typedef (const Texto é char *const); a exceção é o ponteiro para função.
  • Para tipos, use typedef, não #define: o #define só troca texto, e TEXTO a, b; faz de b um char.
  • size_t, FILE e int32_t são typedef da biblioteca; é por isso que o size_t muda de tamanho entre o gcc (8 bytes) e o editor (4). O sufixo _t é reservado pelo POSIX.