Elementos de Linguagem C Parte I Mário Simões 2010.04 Elementos de linguagem C – Parte I 1 Linguagem C • Eficiência da execução – O código gerado corre de forma eficiente, mesmo em processadores modestos, usados em sistemas embebidos • Acesso ao hardware – Manipulação directa da memória de dispositivos de I/O • Portabilidade – Disponibilidades de compiladores para numerosas arquitecturas – APIs estabilizadas e suportadas em diversas plataformas – Dependência da plataforma de execução • O código compilado é especificamente gerado para uma determinada arquitectura • Portar uma aplicação para outra plataforma implica, pelo menos, a recompilação do código • Automatismo reduzido na gestão da memória – O alojamento dinâmico é controlado explicitamente – Não existe garbage collection nem outros de gestão automática da memória alojada dinamicamente Mário Simões 2010.04 Elementos de linguagem C – Parte I 2 Introdução #include <stdio.h> int main(void){ printf("Hello world!\n"); return 0; } Mário Simões 2010.04 Elementos de linguagem C – Parte I 3 Variáveis – Tipos básicos • Inteiros – char – int 8 bits com ou sem sinal – armazenamento de caracteres com sinal, dimensão natural da máquina – típico 16 ou 32 bits • Vírgula flutuante – float – double Mário Simões 2010.04 precisão simples (típico 32 bits) precisão dupla (típico 64 bits) Elementos de linguagem C – Parte I 4 Variáveis – Qualificadores • Aplicáveis a inteiros – – – – signed [char | int] unsigned [char | int] short [int] long [int] valor com sinal valor sem sinal inteiro curto – típico 16 bits inteiro longo – típico 32 ou mais bits • Aplicáveis a vírgula flutuante – long double precisão extra (p. ex. 80 ou 96 bits) • Outros, com significados diversos... Mário Simões 2010.04 Elementos de linguagem C – Parte I 5 Variáveis - Declaração • Formato tipo nome; • Exemplos char c; /* Uma variável do tipo char */ int x, y; /* Duas variáveis do tipo int */ unsigned u; /* Uma variável do tipo int sem sinal */ short s; /* Uma variável do tipo short int */ long l; /* Uma variável do tipo long int */ double d; /* Uma variável do tipo double */ Mário Simões 2010.04 Elementos de linguagem C – Parte I 6 Variáveis – Localização • Exterior às funções – variáveis não automáticas – Alojamento numa posição fixa da memória do sistema • Interior às funções – variáveis automáticas – No interior da função ou de qualquer scope – Alojamento gerido dinamicamente, suportado pelo stack do programa – Resolução de nomes iguais – considera a variável do scope corrente (não se recomenda o uso e abuso) Mário Simões 2010.04 Elementos de linguagem C – Parte I 7 Variáveis – Valor inicial • Atribuído explicitamente – Formato tipo nome = valor; – Variáveis não automáticas • Valor carregado no acto de lançamento do programa – Variáveis automáticas • Valor carregado sempre que se inicia a execução do respectivo scope • Atribuído implicitamente – Variáveis não automáticas – valor 0 • carregado no acto de lançamento do programa – Variáveis automáticas – indeterminado • Não é carregado nenhum valor – a memória mantém o valor, aleatório, que tem Mário Simões 2010.04 Elementos de linguagem C – Parte I 8 Variáveis – Exemplos int a; /* variável com o valor por omissão: 0 */ int b = 10; /* variável com o valor atribuído: 10 */ int main(void){ char c = 'a'; /* variável com valor inicial */ while(...){ char d; /* variável sem valor inicial conhecido */ ... if(...){ float f = 1.23e-4; /* variável iniciada cada vez que entrar no if */ } ... } ... } Mário Simões 2010.04 Elementos de linguagem C – Parte I 9 Constantes – Inteiros • int – sequência de dígitos 1234 10 -20 • long – sufixo L ou l ou dígitos que excedem int 1234L 12345678l • unsigned – sufixo U ou u 1234U 5678u • unsigned long – combinação de sufixos 1234UL Mário Simões 2010.04 5678ul Elementos de linguagem C – Parte I 10 Constantes – Inteiros • Representação em octal – prefixo 0 0123 (equivale a 83 em decimal) 0456 (equivale a 302 em decimal) • Representação em hexadecimal – prefixo 0x 0xffff (equivale a 65535 em decimal) 0x0a (equivale a 10 em decimal) • Estas formas também admitem os sufixos U, u, L e l Mário Simões 2010.04 Elementos de linguagem C – Parte I 11 Constantes – Caracteres • Valores inteiros gerados pelo compilador segundo a tabela ASCII 'a' 'b' 'A' 'B' '0' '1' '2' '3' ... • Sequências com significado especial '\b' '\t' '\n' '\r' '\\' '\'' '\”' • Valores expressos por dígitos – em octal '\ooo' '\0' '\033' '\007' – em hexadecimal '\xhh' '\x00' '\x1b' Mário Simões 2010.04 '\x07' Elementos de linguagem C – Parte I 12 Constantes – Strings • Sequências de caracteres delimitadas por aspas "uma frase com diversas palavras" • Armazenamento em memória – sequência de bytes, com os códigos dos caracteres, terminada por um byte com o valor 0 ('\0'), inserido automaticamente • Admite a inserção dos caracteres especiais na sequência "primeira linha \n segunda linha" • Strings consecutivas formam uma única sequência em memória "uma frase com" " diversas palavras" é equivalente a "uma frase com diversas palavras" Mário Simões 2010.04 Elementos de linguagem C – Parte I 13 Constantes – Enumerados • Definição de um conjunto de constantes com nome enum boolean { NO, YES }; • Admite atribuição dos valores aos nomes enum escapes { BELL = '\a', BACKSPACE = '\b', TAB = '\t', NEWLINE = '\n', VTAB = '\v', RETURN = '\r' }; • Admite atribuição de um valor, gerando os restantes em sequência enum months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; • Os nomes têm que ser distintos, mesmo em enumerados diferentes • Os valores podem coincidir, em enumerados diferentes ou no mesmo Mário Simões 2010.04 Elementos de linguagem C – Parte I 14 Operadores • Aritméticos + - * / % • Incremento e decremento ++ -- • Relacionais e lógicos > >= < <= == != && || ! • Bit a bit & | ^ << >> ~ • Conversões de tipo – Implícitas • – trunca ou expande adequadamente o número de bits Explícitas (cast) (tipo) expressão • Afectação = += -= *= /= %= <<= >>= &= ^= |= • Expressão condicional (operador trenário) expr ? expr1 : expr2 Mário Simões 2010.04 Elementos de linguagem C – Parte I 15 Precedência e Associatividade Operators ( ) [ ] -> . ! ~ ++ -- + - * & (type) sizeof */% +<< >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= , Mário Simões 2010.04 Associativity left to right right to left left to right left to right left to right left to right left to right left to right left to right left to right left to right left to right right to left right to left left to right Elementos de linguagem C – Parte I Comments Unary operators Binary operators Binary operators Bitwise and 16 Controlo • Um Statemtent é – Uma expressão seguida de ';' expression; – Ou uma sequência de statements delimitada por chavetas {statement statement ... statement} • If-else if (expression) statement1 else statement2 Mário Simões 2010.04 Elementos de linguagem C – Parte I 17 Controlo • Else-if if (expression) statement else if (expression) statement else if (expression) statement else if (expression) statement else statement Mário Simões 2010.04 Elementos de linguagem C – Parte I 18 Controlo • Switch switch (expression) { case const-expr: statements case const-expr: statements default: statements } Mário Simões 2010.04 Elementos de linguagem C – Parte I 19 Controlo • While while (expression) statement • Do-while do statement while (expression); Mário Simões 2010.04 Elementos de linguagem C – Parte I 20 Controlo • For for (expr1; expr2; expr3) statement Equivalente a: expr1; while (expr2) { statement expr3; } Mário Simões 2010.04 Elementos de linguagem C – Parte I 21 Controlo • Break – Provoca a saída imediata do ciclo interior ou de um switch • Continue – Provoca a o fim da iteração corrente e regresso à condição de teste do ciclo interior • No caso do for, executa a expressão de incremento – Não se aplica a switch – actua sobre o ciclo interior que contém o switch Mário Simões 2010.04 Elementos de linguagem C – Parte I 22 Controlo • Goto – É útil quando não se pode aplicar break – por exemplo, para quebrar um ciclo que não é o interior – Funciona associado a uma label, com o formato nome: Mário Simões 2010.04 Elementos de linguagem C – Parte I 23 Funções • As funções são as entidades que contêm todo o código executável do programa • O programa começa na função main, chamada a partir do módulo de arranque • A função main chama outras funções para cumprir os objectivos do programa • As funções recebem parâmetros e produzem valores de retorno Mário Simões 2010.04 Elementos de linguagem C – Parte I 24 Funções • Formato de escrita das funções tipo_retorno nome( parâmetros ) { statements } • Formato da invocação ... = nome( argumentos ); • Se uma função não tiver parâmetros, a invocação tem igualmente os parêntesis – operador de chamada ... = nome(); • Se o valor de retorno de uma função não existir ou não for útil, omite-se simplesmente a utilização nome( argumentos ); Mário Simões 2010.04 Elementos de linguagem C – Parte I 25 Funções – Parâmetros • Os parâmetros são sempre passados por valor – No caso de se passar uma variável, a função não pode modificá-la • Se isso for necessário é a solução é passar o endereço – ver adiante: ponteiros • Se a função não tiver parâmetros, isso deve ser indicado pela palavra void tipo_retorno nome( void ){ ... – Se não indicar nada, permite que a função aceite quaisquer parâmetros (não se recomenda, por ser propenso a erros) Mário Simões 2010.04 Elementos de linguagem C – Parte I 26 Funções – Retorno • O valor de retorno pode ser de um tipo básico ou de outros, definidos pelo programador – Existe um tipo por omissão, que é int (contudo, recomenda-se a indicação explícita) • A emissão do valor de retorno faz-se com o statement return int add3int( int a, int b, int c ) { return a + b + c; } float add3float( float a, float b, float c ) { return a + b + c; } • Se uma função não produzir valor de retorno, isso deve ser indicado pela palavra void e não é necessário o statement return void nome(parâmetros){ ... } Mário Simões 2010.04 Elementos de linguagem C – Parte I 27 Comentários • Sequências de caracteres ignoradas pelo compilador – Início: /* – Fim: */ – Podem ocupar várias linhas de texto Mário Simões 2010.04 Elementos de linguagem C – Parte I 28 struct • A struct é uma estrutura de dados que suporta armazenamento não homogéneo (elementos cujo tipo pode ser diferente) • Os elementos que formam uma struct são designados por campos ou membros e têm nome • O tipo de cada campo pode ser qualquer, válido na linguagem, incluindo outras struct ou arrays • É também admitida a definição de "bit fields" – campos inteiros com um número arbitrário de bits • O acesso aos campos de uma struct faz-se combinando o nome da variável que a contém com o nome do campo, ligados por um ponto • As struct são alojadas assegurando o alinhamento de cada um dos seus campos – a dimensão indicada pelo operador sizeof reflecte o alinhamento Mário Simões 2010.04 Elementos de linguagem C – Parte I 29 struct – definição e acesso struct st1{ int a; /* campo inteiro */ float b; /* campo float */ char c[10]; /* campo array de char */ int d:4; /* bit-fild de 4 bits */ }x; /* a struct é alojada numa variável com o nome x */ struct st1 y; /* outra struct idêntica com o nome y */ ... = x.a; /* acesso ao campo a de x */ y.c[n] = ...; /* acesso ao campo c de y */ x = y; /* cópia integral de struct */ Mário Simões 2010.04 Elementos de linguagem C – Parte I 30 struct – declaração de tipo typedef struct int a; float b; char c[10]; int d:4; }S; S x; S y; st1{ /* Tag st1 opcional */ /* campo inteiro */ /* campo float */ /* campo array de char */ /* bit-fild de 4 bits */ /* S é, neste caso, um nome de tipo */ /* uma struct S, alojada numa variável com o nome x */ /* uma struct S, alojada numa variável com o nome y */ ... = x.a; y.c[n] = ...; x = y; Mário Simões 2010.04 /* acesso ao campo a de x */ /* acesso ao campo c de y */ /* cópia integral de struct */ Elementos de linguagem C – Parte I 31 Iniciação de variáveis struct ... typedef struct st1{ /* Tag st1 opcional */ int a; float b; char c[10]; int d:4; }S; S x = {10, 1.234, “ola”, 2}; /* uma struct S, alojada numa variável com o nome x, com valores iniciais, todos explícitos*/ S y = {100, 12.34}; /* uma struct S, alojada numa variável com o nome y com valores iniciais, alguns explícitos e outros implícitos */ Mário Simões 2010.04 Elementos de linguagem C – Parte I 32 Ponteiros • Um ponteiro é uma variável que representa o endereço de outra variável – Permite aceder a variáveis diversas, segundo as mudanças de valor do ponteiro • A declaração de um ponteiro é indicada pelo operador unário * int * p1; /* ponteiro para variáveis do tipo int */ float * p2; /* ponteiro para variáveis do tipo float */ • • O acesso à variável apontada faz-se também com o operador * O endereço de uma variável pode obter-se com o operador unário & cujo tipo é de ponteiro para variáveis do tipo mencionado int a, b; int * p; ... p = &a; *p = 0; b = *p; Mário Simões 2010.04 /* obtém o endereço de a */ /* equivalente: a = 0; */ /* equivalente: b = a; */ Elementos de linguagem C – Parte I 33 Arrays • O array é uma estrutura de dados que suporta o armazenamento homogéneo (colecção de elementos do mesmo tipo) • O tipo dos elementos armazenados pode ser qualquer dos básicos ou outro, definido pelo programador • O acesso aos elementos de um array é seleccionado por um índice • Os valores válidos para o índice são de 0 a N-1, em que N é o número de elementos do array • Declaração tipo nome_array[dimensão]; • Utilização ... = nome_array[índice]; nome_array[índice] = ... Mário Simões 2010.04 Elementos de linguagem C – Parte I 34 ponteiros e Arrays • O nome de um array (sem os parêntesis rectos nem o índice) é um ponteiro para o primeiro elemento do array Considerando a declaração de um array: tipo a[dimensão]; *a = ...; é exactamente o mesmo que a[0] = ...; • O acesso a elementos através de um ponteiro pode ser feito indiferentemente com o operador * ou com o operador [] *p = ...; é exactamente o mesmo que p[0] = ...; • Aritmética de ponteiros: – Se o ponteiro p contém o endereço do elemento i de um array, a expressão p+n representa o endereço do elemento i+n *(p+n) = ...; é exactamente o mesmo que p[n] = ...; – O cálculo do endereço apontado é feito pelo compilador, de acordo com a dimensão do elemento apontado – A aritmética de ponteiros só é válida para aceder a elementos de um mesmo array e não a quaisquer variáveis declaradas em separado Mário Simões 2010.04 Elementos de linguagem C – Parte I 35 ponteiros e Arrays • Na declaração de parâmetros das funções pode ser indiferentemente usada a notação de ponteiro ou de array int strlen(char * s){... é exactamente o mesmo que int strlen(char s[]){... – em ambos os casos, o acesso aos dados representados pelo parâmetro pode ser feito com a notação de ponteiro ou de array ... = *s; ou ... = s[...]; Mário Simões 2010.04 Elementos de linguagem C – Parte I 36 ponteiros e Arrays • A declaração de variáveis ponteiro ou array tem significados diferentes – A declaração de um array reserva espaço em memória para os elementos que ele irá conter – A declaração de um ponteiro só reserva espaço para o próprio ponteiro e não para quaisquer dados apontados – Um ponteiro só tem significado válido depois de afectado com o endereço de uma variável adequadamente alojada – A afectação de um ponteiro pode ser realizada na declaração, na forma de valor inicial int int int int Mário Simões 2010.04 a[...]; b; *p1 = a; *p2 = &b; Elementos de linguagem C – Parte I 37 Arrays – Iniciação • Na declaração de um array pode ser feita a sua iniciação – Se a dimensão for omitida, o compilador calcula – Se for indicada, preenche restantes com 0 – Se a dimensão indicada for insuficiente, é erro int a[] = {1, 2, 3}; /* array de 3 int: valores 1, 2 e 3 */ int b[10] = {4, 5, 6}; /* array de 10 int: os primeiros valem 4, 5, e 6; os restantes valem 0 */ char c[] = "ola"; /* array de 4 char: 'o', 'l', 'a' e '\0' */ char d[3] = "ola"; /* erro!, não cabe o terminador '\0' */ • A declaração de ponteiros iniciados como se fossem arrays tem significado diferente int *p = {1, 2, 3}; /* ponteiro para int, iniciado com o endereço de uma área de constantes com os valores 1, 2 e 3 */ char *p = "ola"; /* ponteiro para char, iniciado com o endereço de uma área de constantes com os valores 'o', 'l', 'a' e '\0' */ Mário Simões 2010.04 Elementos de linguagem C – Parte I 38 Arrays multidimensionais • Podem definir-se arrays de arrays int a[10][20]; /* a é um array de 10 elementos, sendo cada um deles um array de 20 inteiros */ char b[10][5][20]; /* b é um array de 10 elementos, sendo cada um deles um array de 5 elementos que são arrays de 20 char */ • Se forem definidos parâmetros de funções como arrays multidimensionais, é necessários especificar as dimensões, excepto a primeira void func(char x[][5][20]); /* O parâmetro x é o endereço de um array cujos elementos são arrays de 5 elementos, sendo cada um deles um array de 20 char */ Mário Simões 2010.04 Elementos de linguagem C – Parte I 39 Argumentos de linha de comando • Está previsto o acesso argumentos introduzidos na linha de comando pelo utilizador – Cada argumento é uma palavra – O parâmetro argc indica o número de palavras, incluindo o nome do executável – O parâmetro argv representa o endereço de um array de ponteiros para as localizações, em memória, das palavras • Exemplo: mostrar todos os argumentos int main(int argc, char * argv[]){ int i; for(i = 0; i != argc; ++i){ printf("%s ", argv[i]); } } Mário Simões 2010.04 Elementos de linguagem C – Parte I 40 Campos ponteiro para struct • Em estruturas de dados como listas e árvores, declaram-se campos ponteiros para a própria ou outra struct typedef struct st{ struct st *p; /* ponteiro para a própria struct – usa a tag st */ int a; /* outros campos... */ int b; }X; X x1, x2; /* duas variáveis deste tipo de struct*/ ... x1.p = &x2; /* acesso ao campo ponteiro Mário Simões 2010.04 Elementos de linguagem C – Parte I 41 Arrays e iniciação de struct • Podem definir-se arrays de struct typedef struct stx{ int a; int b; } X; X ax[100]; ... ax[i].a = ...; • Podem preencher-se com valores iniciais X x = {10, 20}; /* x.a = 10, x.b = 20 X ax[] = {{10, 20}, {11, 21}, {12, 22}}; /* array de três estruturas iniciadas. As chavetas interiores são opcionais, para legibilidade Mário Simões 2010.04 Elementos de linguagem C – Parte I 42 Ponteiros para struct typedef struct stx{ int a; int b; } X; X ax[100]; X *p; ... p = ax; /* ou, por ex.: p = &ax[i]; equivalente a: p = ax+i; */ ... (*p).a = ...; /* acesso ao campo a da struct apontada, operadores * e . */ p->a = ...; Mário Simões 2010.04 /* forma de escrita equivalente, com o operador -> */ Elementos de linguagem C – Parte I 43 Struct em parâmetros de funções • Um parâmetro struct implica a passagem por valor, isto é, a cópia do conteúdo da struct typedef struct{...} X; tipo_ret nome_func(X x){... – Se não se pretender a cópia, define-se o parâmetro de modo a passar um ponteiro para a struct tipo_ret nome_func(X *xp){... – Ou define-se o tipo de dados com a struct encapsulada num array de um só elemento (um parâmetro array é um ponteiro) typedef struct{...}X[1]; tipo_ret nome_func(X x){... Mário Simões 2010.04 Elementos de linguagem C – Parte I 44 Struct em retorno de funções • O tipo de retorno de uma função pode ser struct – O espaço para suportar este retorno é gerido pela função chamadora – A função chamada declara, normalmente, uma instância da struct para usar em return typedef struct {...}X; X func(parâmetros){... X x; x.campo = ...; ... return x; } Mário Simões 2010.04 Elementos de linguagem C – Parte I 45 Struct e Union • A declaração de variáveis union é idêntica à de struct • A diferença está no alojamento dos campos: todos têm o mesmo endereço – A union é uma forma de aceder ao mesmo espaço de memória com tipos diferentes – A dimensão total da union é a dimensão do maior dos seus campos Mário Simões 2010.04 Elementos de linguagem C – Parte I 46 Funções com lista de parâmetros variável • A linguagem C suporta funções com parâmetros variáveis, indicados por '...' – A mesma função, em utilizações diferentes, recebe um número de parâmetros diferente ou com tipos diferentes – A função recebe, tipicamente, um parâmetro fixo que lhe permite identificar os variáveis – Exemplo: printf(char *fmt, ...); int i; char car; char str[] = "frase"; ... printf("%d\n", i); printf("%c\n", car); printf("%c %d %s\n", car, i, str); Mário Simões 2010.04 Elementos de linguagem C – Parte I 47 Funções com lista de parâmetros variável • As funções definidas pelo programador também podem ter lista de parâmetros variável • Neste caso o acesso aos parâmetros variáveis pode se feito usando um ponteiro iniciado a partir do último parâmetro fixo int addIntList(int count, ...){ int *p = &count + 1; int res = 0; while(count--){ res += *(p++); } return res; } Mário Simões 2010.04 Elementos de linguagem C – Parte I 48 Parâmetros variáveis – stdarg.h • Uma forma sistemática e flexível de acesso a parâmetros variáveis está disponível no ficheiro stdarg.h, com o tipo va_list e macros associadas int addIntList(int count, ...){ va_list ap; int res = 0; va_start(ap, count); while(count--){ res += va_arg(ap, int); } va_end(ap); return res; } Mário Simões 2010.04 Elementos de linguagem C – Parte I 49 ponteiros para função • • A definição de ponteiros para funções implica a especificação dos parâmetros e retorno da função apontada Exemplos: – Declaração de um ponteiro para função sem parâmetros nem valor de retorno void (*fp)(void); – Declaração de um ponteiro para função com dois parâmetros e retorno do tipo int int (*fp)(int, int); – Definição de tipos e declaração de ponteiros com esses tipos typedef void (*fpt1)(void); typedef int (*fpt2)(int, int); fpt1 fp1; fpt2 fp2; Mário Simões 2010.04 Elementos de linguagem C – Parte I 50 ponteiros para função • • A utilização do ponteiro para chamar a função é infdicada pelos parêntesis curvos – operador de chamada – que contêm os parâmetros, se existirem Exemplos void (*fp)(void); void f(void){...} ... fp = f; fp(); void(*fpa[10])(void) = {f1, f2, ...}; ... for(...; ...; ...){ fpa[i](); } Mário Simões 2010.04 Elementos de linguagem C – Parte I 51 ponteiros descomprometidos • void * p; – p aponta um tipo indefinido – p é compatível com qualquer outro ponteiro void * p; int * q; p = q; q = p; – a mudança do tipo de ponteiro, de ou para void *, não altera o endereço apontado • É frequente como parâmetro ou retorno de funções – Exemplos: void *malloc(size_t size); void free(void *p); Mário Simões 2010.04 Elementos de linguagem C – Parte I 52