Preprocessador. Macros. #include. #define

Documentos relacionados
13 a Aula - Instruções Condicionais. Ciclos. Pré-processador. Variáveis de ambiente. Mestrado em Engenharia Física Tecnológica

Revisão de Programação em C++ Leandro Tonietto Estruturas de Dados em C++ Segurança da Informação

Linguagem C: diretivas, compilação separada. Prof. Críston Algoritmos e Programação

Introdução à Programação Aula 16. Prof. Max Santana Rolemberg Farias Colegiado de Engenharia de Computação

Ambiente de desenvolvimento

Estruturas da linguagem C. 1. Identificadores, tipos primitivos, variáveis e constantes, operadores e expressões.

Estruturas de Dados. Módulo 4 Funções. 9/8/2005 (c) Dept. Informática - PUC-Rio 1

Programação de Computadores I Introdução ao C PROFESSORA CINTIA CAETANO

Prof. A. G. Silva. 28 de agosto de Prof. A. G. Silva INE5603 Introdução à POO 28 de agosto de / 1

3.1 - Funções para manipular dados de entrada e saída padrão

Elementos de Linguagem C

Linguagem C: Introdução

MESMO QUE ESTAS VARIÁVEIS TENHAM NOME IDÊNTICOS

PROGRAMAÇÃO E ALGORITMOS (LEII) Universidade da Beira Interior, Departamento de Informática Hugo Pedro Proença, 2016/2017

Introdução à linguagem C

INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA RIO GRANDE DO NORTE

Linguagem C Princípios Básicos (parte 1)

6 Alguns conceitos e comandos em programação

Linguagem de Programação C. Linguagem de Programação C. Linguagem de Programação C. Linguagem de Programação C. Linguagem de Programação C

Linguagem de Programação C

SSC304 Introdução à Programação Para Engenharias. Introdução a Linguagem C. GE4 Bio

Algoritmos e Programação

Revisão da Linguagem C Prof. Evandro L. L. Rodrigues

# Estrutura de Dados # Aula - Revisão de C/C++ na Prática. Prof. Leinylson Fontinele Pereira

Conhecendo a Linguagem de Programação C

Introdução à Lógica de Programação

Programação: Vetores

Estruturas de Repetição

Universidade Estadual de Mato Grosso do Sul Ciência da Computação Algoritmos e Estruturas de Dados I (AED-I) Prof. Nilton

Métodos Computacionais em Física Noções Básicas de Linguag

Linguagem de Programação C. Prof. Fabrício Olivetti de França

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

Implementação da programação modular II

#include <stdio.h> Void main() { printf( Cheguei!\n"); } INTRODUÇÃO A LINGUAGEM C

CICLOS DE REPETIÇÃO. Luís Charneca.

Introdução à Programação em C

Universidade de São Paulo São Carlos Instituto de Ciências Matemáticas e de Computação. Profa Rosana Braga

Revisão Linguagem C Parte 1

Revisão. Profa Marina Gomes

Estrutura de Programas e Tipos de Dados Simples

3. Linguagem de Programação C

Introdução à Programação

4. Estruturas Fundamentais de Programação em C

Departamento de Engenharia Informática. Sistemas Operativos 1. Utilitário Make

LINGUAGEM C: FUNÇÕES FUNÇÃO 08/01/2018. Funções são blocos de código que podem ser nomeados e chamados de dentro de um programa.

Estrutura do programa

Linguagem C. André Tavares da Silva.

Algoritmos II prof. Daniel Oliveira

Controlo de Execução. K&R: Capitulo 3

INTRODUÇÃO À LINGUAGEM C

Programação Estruturada

Fundamentos de Programação. Linguagem C++ aula II - Variáveis e constantes. Prof.: Bruno Gomes

Curso Profissional de Técnico de Informática - Sistemas

Introdução à Linguagem C Variáveis e Expressões

Sumário. Introdução à Ciência da Computação. Ponteiros em C. Introdução. Definição. Por quê ponteiros são importantes?

Programação em C. Variáveis e Expressões. Universidade Federal do Rio Grande do Norte Departamento de Engenharia de Computação e Automação

Apêndice 1. Padrão de composição de módulos

Variáveis primitivas e Controle de fluxo

INTRODUÇÃO À LINGUAGEM C

Métodos Computacionais

Aula 03: Introdução a C

INTRODUÇÃO À LINGUAGEM C

Programação de Computadores II

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Programação. MEAer. Bertinho Andrade da Costa. Instituto Superior Técnico. Introdução ao Pré-Processador. 2011/2012 1º Semestre

The Cyclops Project. Introdução: C++

Abaixo seguem os comandos e suas particularidades, bem como exemplos para o indicador de linha

A Linguagem C. A forma de um programa em C

Tipos Básicos. Operadores de Incremento e Decremento. Operador Sizeof. Estruturas de Dados Aula 2: Estruturas Estáticas

Programação II. Módulos, Encapsulamento e TADs. Bruno Feijó Dept. de Informática, PUC-Rio

Programação I A Linguagem C. Prof. Carlos Alberto

LÓGICA DE PROGRAMAÇÃO. PROFª. M.Sc. JULIANA H Q BENACCHIO

Estruturas de Dados Aula 2: Estruturas Estáticas. Tipos Básicos. Quantos valores distintos podemos representar com o tipo char?

Estruturas de Dados Aula 2: Estruturas Estáticas 02/03/2011

Carlos Eduardo Batista. Centro de Informática - UFPB

Linguagens de Programação

TECNOLOGIA EM REDES DE COMPUTADORES. computadores. Aula 5

Programação I PRG Engenharia de Telecomunicações 2ª Fase Professor: Cleber Jorge Amaral

Programação I Funções. Prof. Carlos Alberto

Conceitos Básicos da Linguagem C++ Prof. Leonardo Barreto Campos 1

#define SIM 1 equivale a definição de constantes; a palavra SIM é substituída por 1 toda vez que é utilizada no programa.

O pré-processador executa transformações controladas no arquivo fonte antes da compilação. Os comandos (sentenças) sempre são iniciados por #.

Programação Estruturada Prof. Rodrigo Hausen Organização e Gerenciamento de Memória

Fundamentos de Programação de Computadores Linguagem C Função Unidade 08 Linguagem C - Função 1/18

MC-102 Aula 04 Expressões Relacionais, Lógicas e Comandos Condicionais

Laboratório de Programação II

#include <stdio.h> void Swap (int *a,int *b) { int temp; temp=*a; *a=*b; *b=temp; } main () {

EXERCÍCIO DE SONDAGEM TURMA 02 SEMESTRE DATA: 01/11/2016. Matrícula Nome Nota

PROGRAMAÇÃO de COMPUTADORES: LINGUAGEM FORTRAN 90/95

Estruturas de Dados. Introdução Definição de Ponteiros Declaração de Ponteiros em C Manipulação de Ponteiros em C

Laboratório de Introdução à Ciência da Computação I

Laboratório de Introdução à Ciência da Computação I

10 Comandos de repetição

Programação Estruturada

Linguagem e Técnicas de Programação

1 Introdução e Conceitos básicos

Linguagem C Introdução. Contexto Histórico Principais diferenças do Java Funções em C Compilar programas em C no Linux

LINGUAGEM C: FUNÇÕES FUNÇÃO 04/07/2017. Funções são blocos de código que podem ser nomeados e chamados de dentro de um programa.

Transcrição:

Preprocessador O preprocessamento é uma das primeiras etapas na compilação do código em C. Toda linha que começa com # é uma diretiva para o preprocessador, e se extende até o fim da linha (diferentemente da sintaxe do restante da linguagem C, que ignora quebras de linha). O preprocessador funciona simplesmente usando substituição de texto. #include Inclui um arquivo. (Isso é feito com um copy-paste automático). Se o nome do arquivo está entre < e >, ele será procurado num caminho global (/usr/include no Linux). Se o nome está entre aspas duplas, ele será procurado primeiro no diretório em que o arquivo que contém o #include está. Nota: é uma boa prática manter as diretivas #include no começo do arquivo. Nota: não se deve incluir um arquivo.c. Inclua apenas arquivos.h (headers). Além disso, os headers não devem conter definições de funções, mas apenas declarações; do contrário, teríamos múltiplas definições de uma função se mais de um arquivo incluir aquele header, e isso gera um erro de ligação. Macros #define Define uma macro. Por exemplo: #define MAX_SIZE 256 Dessa linha em diante, todas as ocorrências de MAX_SIZE serão substiuídas por 256. É possível também criar macros que recebem "parâmetros", como se fossem funções:

#define MAX(x, y) ((x) > (y)? (x) : (y)) Note o uso de parênteses ao redor de todos os "parâmetros", e da expressão inteira: isso é feito para evitar que ocorram erros de precedência de operador, independentemente das expressões usadas como "parâmetro". Com essa definição, z = MAX(5,10); se transforma em z = ((5) < (10)? (5) : (10)); O uso de macros desse tipo pode ter efeitos imprevistos, por avaliarem seu argumento mais de uma vez. Por exemplo, à primeira vista, essa parece ser uma maneira razoável de incrementar x e y ao mesmo tempo em que obtemos o máximo entre eles: int x = 5; int y = 3; int z = MAX(x++, y++); printf("x = %d, y = %d\n", x, y); Imprime x = 7, y = 4 -- ou seja, x foi incrementado duas vezes! Isso ocorre porque a expansão de MAX, nesse caso, é: int z = ((x++) > (y++)? (x++) : (y++)) Ao avaliar a condição (x++) > (y++) ambas as variáveis são incrementadas. Como a condição é verdadeira, o valor da expressão se torna (x++) e x é incrementado novamente! Nota: nomes de macros são sempre em maiúsculas, por convenção (para diferenciá-las de variáveis e funções). Porém, a própria biblioteca padrão foge dessa convenção às vezes (por exemplo, a "função" putc de stdio.h é na verdade uma macro). do {...} while(0) Outro efeito imprevisto surge com o uso de if em macros com diversos comandos (cada comando termina com um ; ):

#define INCR_THREE(x,y,z) (x)++; (y)++; (z)++ Ou equivalentemente, colocando cada comando em uma linha: #define INCR_THREE(x,y,z) \ (x)++; \ (y)++; \ (z)++ (Note o uso de \ ao final de cada linha: isso faz com que o compilador veja todas a linha seguinte como uma continuação da atual. Isso é necessário porque o pré-processador entende que suas diretivas terminam com o fim da linha.) Quando o if tem apenas um comando, é possível omitir as chaves; porém, visualmente, pode parecer que if(condicao) INCR_THREE(x, y, z); Está correto, porque só há um ; no final; porém, esse código expande para if(condicao) x++; y++; z++; Então apenas o primeiro comando ( x++ ) está dentro do if. Há duas maneiras de corrigir isso: a primeira é usar sempre as chaves no if, mesmo quando poderiam ser omitidas; a segunda é colocar macros que tenham vários comandos dentro de um do...while(0) : #define INCR_THREE(x,y,z) \ do { \ (x)++; \ (y)++; \ (z)++ \ } while(0)

O corpo de um do...while(0) será executado apenas uma vez; logo, esse loop não é realmente um loop: seu único efeito é fazer com que todo o corpo da macro conte como um único comando. (Além disso, o compilador provavlemente eliminará o loop ao otimizar o código.) # e ## em macros Caso o argumento de uma macro seja precedido por #, coloca-se aspas duplas ao redor do argumento passado, de modo que ele se torna uma string literal. Por exemplo: /* note o uso de # antes do x */ #define PRINT_VARNAME(x) printf("minha variavel se chama %s", #x) Com essa definição, PRINT_VARNAME(my_var); Expande para printf("minha variavel se chama %s", "my_var"); Já o ## permite concatenar o argumento da macro com outro nome: #define ADD_GENERIC(X) add_##x Com essa definição, ADD_GENERIC(int)(x, y); Expande para add_int(x, y); #undef

Desfaz um #define anterior. Compilação condicional Estas diretivas são usadas para se obter compilação condicional - ou seja, certos trechos de código podem ser usados ou omitidos em um arquivo se certas condições forem atendidas. #ifdef, #ifndef, O trecho de código entre o #ifdef e o será usado se, e só se, a macro nomeada após o #ifdef estiver definida. A macro #ifndef é similar, mas o código será usado se a macro não está definida (IF Not DEFined). Por exemplo: int main(void) { #ifdef FOO printf("foo!\n"); printf("bar!\n"); return 0; } Se a macro FOO estiver definida (com um uso anterior de #define ), a função main se tornará, depois do pré-processamento, int main(void) { printf("foo!\n"); printf("bar!\n"); return 0; } Do contrário, o código entre as linhas do #ifdef e do será omitido, e teremos

int main(void) { printf("bar!\n"); return 0; } A compilação condicional tem diversos usos. Por exemplo, é comum ver este trecho de código no começo dos headers em C: #ifdef cplusplus extern "C" { E este trecho no final: #ifdef cplusplus } /* C++ */ Se a macro cplusplus estiver definida, entende-se que estamos compilando em C++, logo todo o código do header é colocado dentro de um bloco extern C {... } (isso é necessário para evitar erros de ligação, porque C++ tem suporte para overload de funções - funções com mesmo nome mas com protótipos diferentes). Header guards Considere um header foo.h com o seguinte código: /* foo.h */ struct foo { int a; int b; }; E um arquivo foo.c como segue: /* foo.c */

#include "foo.h" #include "foo.h" /* use struct foo de algum modo */ foo.c não compila, porque a inclusão do header duas vezes faz com que a definição de struct foo apareça duas vezes (redefinição de struct não é permitido, ainda que as definições sejam idênticas). Nesse caso, podemos detectar o erro facilmente e remover o segundo #include. Mas considere que temos também um segundo header, bar.h, como segue: /* bar.h */ #include "foo.h" struct bar { struct foo f; char *nome; }; Esse header define um tipo que tem uma instância de struct foo ; por isso, o header deve ter a definição desse tipo, o que se obtem incluindo foo.h. (É comum ter um header que inclui outro header, não só para usar tipos definidos no outro, mas também macros, protótipos de função etc.) Suponha agora que queremos criar usar ambas struct s em foo.c. Como cada uma foi declarada em um header, é natural fazer: #include "foo.h" #include "bar.h" /* use struct foo e struct bar */...mas esse código não compila, pelo mesmo motivo do anterior: redefinição de struct foo. Poderíamos resolver isso incluir apenas bar.h, mas esse header pode ser mudado no futuro e não mais incluir foo.h ; além disso, manter nota de quais headers incluem quais se torna rapidamente impraticável à medida que o número de headers aumenta. A solução mais prática é usar um header guard em foo.h : /* foo.h */ #ifndef FOO_H #define FOO_H struct foo { int a;

int b; }; Um header guard protege contra múltiplos #include s: se o header não foi incluído antes, então supõese que a macro do header guard (no caso, FOO_H ) nunca foi declarada, logo todo o código do header (entre o #ifndef na primeira linha e o na última) é usado. Já na primeira linha dentro desse código, essa macro é definida; assim, da próxima vez que o header for incluído, o #ifndef omitirá todo o código do header. Nota: por convenção, a macro do header guard tem o mesmo nome do header, mas em maiúsculas e com a extensão.h substituída por _H. Nota: o header guard só funciona se a macro usada não é definida em nenhum outro lugar; por isso, tome cuidado ao declarar uma macro cujo nome termine em _H. #if Para condições mais complexas, podemos usar a diretiva #if. Por exemplo: #if CHAR_BIT!= 8 #error "non 8-bit chars are not supported!" Isso gera um erro de compilação (com a diretiva #error ) se a macro CHAR_BIT não está definida ou tem qualquer outro valor que não seja 8. #ifdef FOO e #ifndef FOO são equivalentes a #if defined(foo) e if!defined(foo) respectivamente. Podemos usar isso juntamente com && e, por exemplo: #if defined(foo)!defined(bar) /* ou FOO está definido, ou BAR não está (ou os 2) */ #else É possível também usar a diretiva #else para que um trecho alternativo de código seja usado se a condição de um #if ou #ifdef for falsa. Por exemplo, isso permite usar funções típicas de um sistema operacional em outros sistemas:

/* queremos usar strdup(), mas essa função só está implementada em sistemas UNIX. * Nesses sistemas, queremos usar a implementação que já está pronta; nos demais, * temos que implementar "na mão". */ #ifdef UNIX /* em UNIX, a função strdup() está implementada: use-a */ # define my_strdup strdup #else /* em outros sistemas, implemente a função */ char *my_strdup(const char *s) { /*... */ } Se a macro UNIX estiver definida, o código que está entre o #ifdef e o #else será usado - ou seja, my_strdup se torna um outro nome para strdup, e o código entre o #else e o será descartado. Se a macro não estiver definida, ocorre o oposto, e usa-se a implementação fornecida para my_strdup. Misc #error Essa diretiva gera um erro de compilação, com a mensagem fornecida. Por exemplo: #ifndef MY_AWESOME_MACRO # error "compilacao falhou: MY_AWESOME_MACRO nao esta definida!" É quase sempre usada juntamente com as diretivas de compilação condicional (do contrário, a compilação do código sempre falharia). #pragma

O efeito dessa diretiva depende da implementação. Por exemplo, alguns compiladores suportam header guards da forma #pragma once O problema dessa diretiva é justamente o fato de seu efeito ser dependente da implementação, então não se pode contar que um #pragma vá ter o mesmo efeito em qualquer compilador. # Essa diretiva não faz nada. (Ela é normalmente usada entre duas diretivas "de verdade", para dar um efeito visual de quebra de linha.) Revision #25 Created Mon, Jan 28, 2019 11:39 PM by Mateus Updated Sun, Mar 17, 2019 7:01 PM by Mateus