EI SO D+PL 2023-24 Cap04 Linguagemc v01

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 49

Sistemas Operativos

Programação em C
Ambientes Linux
2024
• Patrício R. Domingues
• Departamento de Eng Informática
• ESTG/Politécnico de Leiria
CICLO DE DESENVOLVIMENTO DE
SOFTWARE

(c) Patricio Domingues


2
Ciclo de desenvolvimento

Imagem: http://w3processing.com/index.php?subMenuItemId=222

(c) Patricio Domingues


3
Tipos de ficheiros na linguagem C
• A linguagem C assenta em duas classes de ficheiros: 1)
Escritos pelo programador; 2) Criados pelo compilador
– Ficheiros a cargo do programador – código fonte
• Ficheiro(s) C: extensão .c
• Ficheiro(s) H (header files): extensão .h
– Ficheiros criados do compilador
• Ficheiro(s) objeto: extensão .o
– Existe um ficheiro objeto por cada ficheiro .c
• Ficheiro executável: extensão .exe (Windows), no
Unix/Linux, a extensão pode ser qualquer uma
– E ainda biblioteca(s): extensão .lib/.so
– Library (EN) = biblioteca (PT)
– Exemplo: libc, que contém estruturas e funções da linguagem C
(c) Patricio Domingues
4
Compilação em C
➢Um programa em C é composto por um ou mais ficheiros de
código fonte (.c e os respetivos .h)

➢Cada ficheiro é processado primeiro pelo pré-processador


originando um ficheiro de texto

➢O ficheiro de texto é processado pelo compilador (i.e., é


compilado) originando um ficheiro de código objeto
(assembler)

➢Todos os ficheiros objetos são agregados pelo “linker”, criando-


se o ficheiro executável
Esquema >>
5 (c) Patricio Domingues
5
Ciclo de Compilação em C

Opção -E efetua somente pré-


processamento: gcc -E fich.c

Opção -S cria ficheiro .s (assembler):


gcc -S fich.c

Opção -c cria ficheiro .o (objeto):


gcc -c fich.c

(c) Patricio Domingues


6
Programa exemplo
#include <stdio.h>
int main(void){
printf("Programa em C\n");
return 0;
}
▪ Compilação via gcc (linha de comando)
▪ gcc -g -Wall -Wextra prog.c -o prog.exe
▪ -g: acrescenta info de depuração
▪ -Wall: ativação dos warnings
▪ -Wextra (ou –W): ativação de mais warnings
▪ -o nome: nome do ficheiro executável

▪ Próximos slides: análise às diferentes etapas


(c) Patricio Domingues
7
Préprocessador
• gcc -E prog.c
– Ativa apenas préprocessador
– Produz ficheiro de código C com 1148
linhas
# 1 "helloWorld.c"
# 1 "<built-in>"
# 1 "<command line>“
(…)
# 2 "helloWorld.c" 2
int main(void)
{
printf("Programa em C\n");
return 0;
}
(c) Patricio Domingues
8
Código assembler
▪ gcc -S prog.c
▪ opção -S: produz código “assembler”
.file "helloWorld.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "Programa em C\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
(…)
call _printf
movl $0, %eax
leave
ret
.def _printf; .scl 3; .type 32; .endef
(c) Patricio Domingues
9
Código fonte e código executável
#include <stdio.h>
int main(void){
printf("Programa em C\n");
return 0;
}
• Compiler explorer
https://godbolt.org/

(c) Patricio Domingues


10
Sistemas
Operativos

Patricio Domingues

11
O PRÉPROCESSADOR
DE C

(c) Patricio Domingues 12


Préprocessador (1)
▪ Programa que processa os ficheiros de código
fonte
▪ É executado antes da fase de compilação daí o
prefixo de “pré” Diretivas do pré-processador
– Processa “diretivas” do pré-processador #include
– As diretivas são identificadas por um “#” #define
#ifdef…endif
▪ Exemplos #pragma, #undef
(…)
#include <stdio.h>
– A diretiva “#include” indica ao pré-processador que
deve substituir a linha “#include <stdio.h>” pelo
contéudo do ficheiro "stdio.h"

– Os ficheiros “.h” contém protótipos de funções,


definições de variaveis globais, etc.

– Os ficheiros “.h” do sistema estão em /usr/include

(c) Patricio Domingues


13
Préprocessador (2)
• gcc -E prog.c
– Opção -E: ativa apenas préprocessador
– Produz ficheiro de código C com 1148
linhas
# 1 "helloWorld.c"
# 1 "<built-in>"
# 1 "<command line>“
(…)
# 2 "helloWorld.c" 2
int main(void)
{
printf("Programa em C\n");
return 0;
}
(c) Patricio Domingues
14
#include (1)
▪ A primitiva #include serve para incluir ficheiros .h
▪ Um ficheiro .h está (usualmente) associado a um ficheiro .c
▪ Exemplo:
▪ util.c >> util.h
▪ Um ficheiro .h contém:
▪ Protótipos de funções “públicas”
▪ Referência extern avariáveis globais “públicas”
É boa prática identificar as
variáveis globais com o
▪ Exemplos prefixo G_

▪ extern int G_Contador; /* Variável global */


▪ double CalculaSQRT(double); /* Protótipo */

(c) Patricio Domingues


15
Ficheiro .h (exemplo)
▪ Exemplo de ficheiro .h Proteção contra
“multi-includes”
#ifndef UTIL_H (#include guard)
#define UTIL_H
typedef unsigned char UINT8; /* Definição de tipo (alias) */
typedef signed char INT8;
typedef unsigned short UINT16;
typedef signed short INT16;
typedef unsigned int UINT32;
typedef signed int INT32;
typedef unsigned long long UINT64;
typedef signed long long INT64;
typedef unsigned int size_t;

/** Public functions – prototypes */


void serial_write(UINT8 *p_param);
Usa o tipo de dado UINT8
int serial_read(void);
definido mais acima
size_t _strlen(const char* s);
int _strncmp(const char *s1, const char *s2, size_t n);
#endif
#endif da proteção contra
(c) Patricio Domingues
“multi-includes” 16
Proteção contra multi-includes
• Terminologia Anglo-saxónica: “#include guard”
• Evitar que um ficheiro .h seja (indiretamente) incluído várias
vezes num ficheiro .c
• Exemplo
#ifndef = If NOT defined

#ifndef UTIL_H i) Se a constante UTIL_H não tiver ainda sido


#define UTIL_H definida, o conteúdo do ficheiro .h é incluído (até
ao correspondente #endif) e a constante passa a
(…) estar definida.
#endif
ii) Se a constante UTIL_H já tiver sido definida (de
um include anterior), então nada é incluído,
evitando assim o múltiplo #include

• Muitos compiladores suportam ainda a opção #pragma once


– Dispensa o #ifndef … Atividade
– Não faz parte da norma da linguagem C https://en.wikipedia.org/wiki/Include_guard
(c) Patricio Domingues
https://en.wikipedia.org/wiki/Pragma_once17
#include (2)
▪ Distinção na indicação do ficheiro <…> e “…”
▪ #include <ficheiroSistema.h>
▪ Compilador procura o ficheiro .h nos diretórios do sistema
(/usr/include no linux)

▪ #include "ficheiroUtilizador.h"
▪ Compilador procura o ficheiro .h nos diretórios do
utilizador (usualmente diretório corrente)
▪ Opção -I do compilador permite especificar diretórios
alternativos
▪ Exemplo
#include <stdio.h>
#include <math.h>
#include "util.h" (c) Patricio Domingues
18
Préprocessador (3) - constantes

▪ A directiva #define permita a definição de constantes que são


substituídas pelo pré-processador
▪ Exemplo
#define CIDADE "LEIRIA"

▪ Define a constante CIDADE

▪ Sempre que surgir CIDADE no código fonte, o préprocessador faz a


substituição por "LEIRIA"

▪ Por convenção, as constantes do préprocessador são definidas em


maiúsculas

(c) Patricio Domingues


19
Préprocessador – constantes (4)

• Para as constantes que não tenham valor


definido, pode ser definido um valor através
da linha de comandos
– -DNOME_MACRO=VALOR
• Exemplo
int main(void){
printf("Valor=%d\n",VALOR);
return 0;
} VALOR 2 definido na linha de comandos
gcc -DVALOR=2 -Wall -Wextra a.c -o a.exe
– VALOR é definido com 2

(c) Patricio Domingues


20
Préprocessador (5) - macros
▪ A directiva #define permita ainda a definição de macros que também
são substituídas pelo pré-processador
▪ Uma macro **não** é uma função
#define MAX(x,y) (x>y ? x : y)
▪ MAX(x,y) é uma macro com dois parâmetros
▪ Sempre que o pré-processador encontrar “MAX(x,y)” irá proceder à
substituição pela expressão C “(x>y ? x : y)”

▪ Exemplo Código escrito pelo programador


int a= MAX(10,20);

Passa a... Código que é visto/processado pelo compilador de C


após a fase de pré-processamento
int a = (10>20? 10 : 20);

(c) Patricio Domingues Exercício >>21


Atividades
Exercício #1
▪ Definir uma macro (nome: ABS) que devolve o valor absoluto de
um número inteiro
▪ Valor absoluto de um número positivo ou zero N é o próprio numero N
▪ Valor absoluto de um número negativo N é igual a –N
▪ Macro tem um parâmetro
▪ #define ABS(a) ...
Exercício #2
▪ Definir uma macro (nome: MAX3) que devolve o maior
de três parâmetros (a,b,c).

▪ Sugestão para os exercícios:


usar o operador ternário _ ? _: _

https://xkcd.com/303/
(c) Patricio Domingues
22
Préprocessador (6) - condicional

• O pré-processador suporta directivas de inclusão condicional


• São directivas com início e fim, que delimitam um conjunto
ou mais conjuntos de linhas de código
• Muito empregue para código multiplataforma
#if...#endif
#ifdef ... #endif - Compilação condicional: código que é compilado
#ifndef... #endif depende das constantes/macros definidas
#ifdef...#else...#endif - WIN32 identifica API windows

• Exemplo
#ifdef WIN32
printf("Código para ambiente Windows WIN32\n");
#else
printf("Código para ambiente NÃO Windows\n");
#endif
(c) Patricio Domingues
23
Préprocessador (7) - exemplos
• Condicional - Código só é considerado se a constante/macro
DEBUG estiver definida
#define DEBUG
+info: Capítulo 10, “Practical C Programming”, Steve
... Oualline, O’Reilly. 3rd Edition, 2011.

#ifdef DEBUG
printf(“mensagem quando DEBUG está definido\n”);
#endif
• printf(“Linha:%d, Ficheiro fonte: %s\n”, __LINE__,__FILE__);
• __FILE__ e __LINE__ __ corresponde a dois underscores

– Definidos pelo pré-processador


– __FILE__corresponde ao nome do ficheiro corrente
– __LINE__ corresponde ao número da linha corrente
(c) Patricio Domingues
24
Préprocessador (8)
• Uso do #ifndef para precaver múltiplos includes
– #include guard
#ifndef UTIL_H
#define UTIL_H
(…)
#endif
▪ Sempre que se cria um ficheiro .h, deve-se fazer uso de:
#ifndef NOME_FICHEIRO_H
#define NOME_FICHEIRO_H
(…)
#endif
OU
▪ #pragama once (c) Patricio Domingues
25
Sistemas
Operativos

Patricio Domingues

26
Sistemas Operativos
Programação em C
para ambientes Linux

assert e static_assert

Patricio Domingues

27
A macro assert (#1)
• assert(condição) #include <stdio.h>
#include <assert.h>
– Tradução “afirmar” size_t num_chars(const char *ptr){
#include <assert.h> assert(ptr != NULL );
void assert(scalar expression); const char *workptr = ptr;
• Macro empregue para validar size_t num_elms = 0;
while( workptr && (
código *workptr!='\0')){
• Auxiliar o programador a num_elms++;
detetar situações erradas na workptr++;
aplicação }
return num_elms;
• Caso a condição seja avaliada
}
como falsa (valor lógico FALSO), Se condição avaliada como falsa
a aplicação é terminada com
mensagem de erro a.exe: numchars.c:5: num_chars:
Assertion `ptr != ((void *)0)' failed.
Aborted (core dumped)
Código: https://pastebin.com/hTKsBpKu
(c) Patricio Domingues 28
A macro assert (#2)
Atividade (Linux)
• Como desativar a macro Considere o código do programa divide.c
assert? double divide(int a, int b){
assert(b!=0);
– A macro assert não deve double result = (a*1.0) / (b*1.0);
return result;
ser mantida em código de }
Gera número
produção int main(void){
inteiro aleatório
while(1){
no intervalo [0,9]
– Desativada definindo-se a int a = random() % 10;
int b = random() % 2;
macro NDEBUG printf("%d / %d = %f\n", a, b, divide(a,b));
• Ler: “No debug” usleep(100000); // 100 uSeconds
}
• Desativar }
a) Compile o código: gcc -Wall -Wextra divide.c -o
gcc –DNDEBUG ... divide.exe
- Completar com os includes necessário
b) Execute: ./divide.exe
OU no código O que sucede?
c) Compile, acrescentando -DNDEBUG na linha de
#define NDEBUG compilação. Execute. O que sucede?
(c) Patricio Domingues
29
static_assert – C11
#include <assert.h>
• Exemplo
static_assert( #include <assert.h>
expr,string-literal); #include <stdio.h>
Condição
– Macro avaliada durante a int main(void){
compilação static_assert(sizeof(void*)==8,
"Only 8-byte addresses");
– Se a expressão expr for zero printf("Done\n");
(falsa), então é mostrada a String
return 0;
string-literal e a compilação }
é interrompida
• static_assert
– Surgiu na versão 2011 da
norma C, denominada C11.
– Requer um compilador que
implemente a norma C11
static_assert vs. assert
• static_assert • assert
– avaliada durante a – avaliada em durante a
compilação (compile time) execução (runtime)
– Requer compilador que – É uma macro definida em
suporte a norma C11 <assert.h>
• #include <assert.h>
– É uma macro definida
• Leitura recomendada
em assert.h
– Casalnuovo, Casey, et al. "Assert use in
• #include <assert.h> github projects." 2015 IEEE/ACM 37th IEEE
• A macro encapsula o International Conference on Software
Engineering. Vol. 1. IEEE, 2015.
operador C11
_Static_assert
Sistemas
Operativos

Patricio Domingues

32
Sistemas Operativos
Programação em C
para ambientes
Linux

Utilitário make

Patricio Domingues

33
UTILITÁRIO MAKE

(c) Patricio Domingues


34
Utilitário make (1)
▪ É recomendado que uma aplicação
seja dividido em vários ficheiros de
código fonte. Vantagens:
▪ A alteração de um módulo requer
apenas que esse módulo seja
recompilado
▪ O tempo de compilação aumenta
exponencialmente com o tamanho do
ficheiro de código fonte https://bit.ly/3cWWfpI
▪ A distribuição de um projeto por
vários ficheiros permite que vários
programadores estejam a mexer ao
mesmo tempo no projeto (um
programador por ficheiro)
▪ Como gerir a dependência entre
ficheiros e automatizar o processo
de compilação/linkagem?
▪ Utilitário make

(c) Patricio Domingues


35
Utilitário make (2)
▪ Para compilar a aplicação, basta
executar: make
▪ O utilitário make interpreta um
ficheiro designado de makefile
▪ O ficheiro makefile contém:
▪ Estrutura do projeto
▪ Ficheiros e dependências
▪ Instruções para a
compilação do programa makefile empregue em SO
▪ O make gere dependências entre (https://pastebin.com/ma4dYEwE)
ficheiros
▪ Não está limitado a ficheiros
de programas em linguagem C
(c) Patricio Domingues
36
make - casos de uso
• Casos de uso mais frequentes
– make: compila os ficheiros necessários, criando o
executável

– make clean: apaga todos os ficheiros criados por uma


compilação anterior (*.o, executável)

(c) Patricio Domingues


37
Utilitário make (3)
▪ Exemplo
▪ Programa composto por três ficheiros de código fonte
▪ main.c, sum.c e sum.h
▪ sum.h é empregue (via #include) em sum.c e main.c
▪ O executável deverá chamar-se “sum”

sum (exe)

main.o sum.o

main.c sum.h sum.c

Dependência
entre ficheiros
(c) Patricio Domingues
38
.h e dependências
▪ Automatizar a determinação das dependências de um ficheiro
de código C relativamente aos ficheiros .h
▪ Ficheiros .h que são incluídos (#include) no ficheiro .c
▪ Uso do compilador gcc com a opção -MM
gcc -MM *.c
sum (exe)

main.o sum.o

main.c sum.h sum.c

▪ Exemplo
▪ gcc -MM *.c
main.o: main.c sum.h
Saída produzida por: gcc -MM *.c
sum.o: sum.c sum.h
(c) Patricio Domingues
39
Ficheiro makefile
# Ficheiro com 3 regras (# é indicador de comentário)
# Regra = linha de dependência seguida de linha(s) de ação
sum: main.o sum.o Linha de dependência

gcc -o sum main.o sum.o Linha de ação

main.o: main.c sum.h


gcc -c main.c
# Regra para “sum.o”
sum.o: sum.c sum.h
gcc -c sum.c

40
Sintaxe do makefile
Ficheiro alvo

main.o: main.c sum.h


gcc -c main.c
Comando a ser executado
(inicia-se por um tab)
Ponto 1: main.o é o ficheiro alvo, isto é, a linha descreve o que deve ocorrer
quando main.o tem data anterior a qualquer um dos dois ficheiros de que
depende
Ponto 2: main.o depende de main.c e sum.h
Ponto 3: a segunda linha indica qual é a ação a ser executada quando a regra é
ativada (i.e., quando main.o está desatualizado). Neste caso é chamado o
gcc para compilar main.c, reconstruindo o ficheiro main.o
Ponto 4: a segunda linha inicia-se SEMPRE por um tab (se forem espaços, o
make dá erro)
41
makefile avançado (1)
# Makefile empregue nas aulas práticas
# Bibliotecas a incluir
Tudo o que vem a seguir # é
LIBS=#-pthread interpretado como comentário
# Flags para o compilador
CFLAGS=-Wall -W -g -Wmissing-prototypes #-ggdb
# nome do executavel
PROGRAM=initC Nome do ficheiro executável
# Nome do ficheiro de opcoes do gengetopt
PROGRAM_OPT=#prog_opt

makefile empregue em SO (https://pastebin.com/ma4dYEwE)

Continua >>
42
makefile avançado (2)
# Objectos necessarios para criar o executavel
PROGRAM_OBJS=initC.o debug.o ${PROGRAM_OPT}.o
.PHONY: clean all: ${PROGRAM}
# compilar com depuracao
depuracao: CFLAGS += -D SHOW_DEBUG
depuracao: ${PROGRAM}

${PROGRAM}: ${PROGRAM_OBJS}
${CC} -o $@ ${PROGRAM_OBJS} ${LIBS}

makefile empregue em SO (https://pastebin.com/ma4dYEwE)

Continua >>
43
makefile avançado (3)
# Dependencias
initC.o: initC.c debug.h #${PROGRAM_OPT}.h
${PROGRAM_OPT}.o: ${PROGRAM_OPT}.c ${PROGRAM_OPT}.h

debug.o: debug.c debug.h


semaforos.o: semaforos.c semaforos.h

#como compilar .o a partir de .c .c.o: ${CC}


${CFLAGS} -c $<
# Como gerar os ficheiros do gengetopt
${PROGRAM_OPT}.h: ${PROGRAM_OPT}.ggo
gengetopt < ${PROGRAM_OPT}.ggo --file-name=${PROGRAM_OPT}
makefile empregue em SO (https://pastebin.com/ma4dYEwE)

Continua >> 44
makefile avançado (4)
clean: rm -f *.o core.* *~ ${PROGRAM} *.bak
${PROGRAM_OPT}.h ${PROGRAM_OPT}.c

docs: Doxyfile
doxygen Doxyfile
Doxyfile:
doxygen -g Doxyfile

indent:
dos2unix *.c *.h indent ${IFLAGS} *.c *.h

makefile empregue em SO (https://pastebin.com/ma4dYEwE)


45
Atividade
• Ferramentas auxiliares (disponíveis no makefile)
– gengetopt
• Utilitário que permite criar automaticamente o código fonte para tratamento dos
parâmetros da linha de comando (argv e argc)
• Ficheiro de entrada “.ggo”
– doxygen Chamado com: make docs
• Documentação integrada no código (tipo javadoc)
– indent Chamado com: make indent
• Ferramenta para a formatação do código fonte
• Permite aplicar vários estilos
– pmccabe
• Métrica pmccabe associada à complexidade do código
– cppcheck
• Ferramenta para deteção de anomalias no código (potenciais erros)

(c) Patricio Domingues


46
Bibliografia (1)
▪ Practical C Programming, Steve Oualline, O’Reilly,
3rd edition, 1998 - Capítulo 10: pré-processador

▪ Linguagem C, Luís Damas, FCA Editora

▪ Managing Projects with GNU Make, Robert Mecklenburg,


Robert William Mecklenburg, O’Reilly, 3rd edition, 2004.
▪ Recursos online
▪ The C language (online) – “The C Book”
• http://publications.gbdirect.co.uk/c_book/
▪ The C pre-processor (online)
• http://gcc.gnu.org/onlinedocs/cpp/

(c) Patricio Domingues


47
Bibliografia (2)
▪ GNU Gengetopt, GNU
http://www.gnu.org/s/gengetopt/gengetopt.html

▪ GNU indent, http://www.gnu.org/software/indent/

▪ GCC – GNU Compiler Collection, https://gcc.gnu.org/

▪ doxygen, http://www.doxygen.org

▪ Visual Studio Code, https://code.visualstudio.com/


(c) Patricio Domingues
48
Sistemas
Operativos

49

Você também pode gostar