Tipos básicos do C
Slides
Inteiros
Variantes de int
Até agora usámos apenas o tipo int
para representar números inteiros, mas existem outros:
Diferentes tipos podem representar números inteiros em diferentes intervalos e ocupam mais ou menos espaço em memória.
Estes intervalos e espaço ocupado em memória pode variar de máquina para máquina, mas é garantido que:
short int < int < long int
Processadores com arquitecturas de 64 bits são cada vez mais a norma e os intervalos de valores comuns são:
Uma forma rápida de verificar os limites de um determinado tipo, é usar a biblioteca <limits.h>
.
#include <limits.h>
#include <stdio.h>
int main(void) {
int v;
printf("max long=%ld\n", LONG_MAX);
}
Podem consultar a lista de todas as constantes na documentação da biblioteca.
Inteiros
Constantes
Até agora definimos constantes de inteiros no formato decimal simples, i.e. usando 10 digitos distintos.
Mas existem outras formas.
É possível definir constantes em formato octal e hexadecimal.
Não iremos explorar estas bases, mas veremos como escrever constantes de inteiros diferentes.
15L // interpretar 15 como um long int
15U // interpretar 15 como um unsigned int
15UL // interpretar 15 como um unsigned long int
Também se pode escrever U e L em minúsculas. Pode-se escrever UL ou LU.
Overflow
Como vimos anteriormente, os tipos inteiros têm um valor máximo e mínimo que podem representar.
#include <limits.h>
#include <stdio.h>
int main(void) {
int v = INT_MAX;
printf("v=%d\n", v);
printf("v=%d\n", v+1);
return 0;
}
Qual é o valor de v
e v+1
?
#include <limits.h>
#include <stdio.h>
int main(void) {
int v = INT_MAX;
printf("v=%d\n", v);
printf("v=%d\n", v+1);
return 0;
}
v = 2147483647
v + 1 = -2147483648
printf e scanf
Reais
Intervalos de valores
O long double
não aparece porque os intervalos variam bastante de máquina para máquina.
constantes
Diferentes formas de escrever o número 57:
printf scanf
Texto
char
Até agora usámos texto apenas no contexto do scanf
e printf
.
O C tem um tipo para texto: o char
.
- Um char guarda 1 letra.
- As constantes de char são escritas entre plicas
''
. Não confundir com:- aspas
"
, usadas para strings (e.g. dentro doprintf
escanf
) - apóstrofos
’
(que não fazem nada).
- aspas
Internamente, um char é apenas um número inteiro que pode ser interpretado como letra, através da tabela ASCII.
ASCII = American standard Code for Information Interchange
Não precisamos de saber esta correspondência, porque podemos sempre interpretar um char
como int
e vice-versa.
A especificação de conversão para char é o %c.
Por serem números inteiros, podemos realizar operações aritméticas sobre char.
Tal como vimos para os tipos inteiros, o char
também pode ser signed
ou unsigned
.
O char
também está sujeito a overflow.
- Já usámos anteriormente caracteres especiais, e.g.
'\n'
e'\t'
. Existem outros. - Alguns só podem ser especificados em formato numérico (octal, decimal ou hexadecimal).
scanf
- Quando o
scanf
acaba de processar um determinado input, existem caracteres que ficam por consumir. - Como todos os caracteres são válidos para o tipo
char
, isso pode ser um problema.
char c1, c2;
printf("Introduza um caracter:");
scanf("%c", &c1);
printf("Introduza outro caracter:");
scanf("%c", &c2);
printf("c1=%c c2=%c \n", c1, c2);
Resulta em…
- Aparentemente, o segundo
scanf
foi ignorado:- o utilizador teve oportunidade de escrever
- o que estaria depois de
c2=
está vazio
- Na verdade, o primeiro
scanf
deixa um enter'\n'
por consumir. - No
scanf
seguinte, o que é pedido é um char. - Como
'\n'
é um char válido, a especificação de conversão aceita-o como input - Deixa de ser necessário pedir mais um input ao utilizador porque as especificações de conversão já foram satisfeitas.
Como confirmar? Vamos interpretar c2
como um inteiro.
char c1, c2;
printf("Introduza um caracter:");
scanf("%c", &c1);
printf("Introduza outro caracter:");
scanf("%c", &c2);
printf("c1=%c c2=%d \n", c1, c2);
alternativas para ler e escrever char
Existem outras formas de ler e escrever um char
:
getchar
A função getchar
lê um único char.
⚠️ Tal como no scanf
, o getchar
não salta espaços em branco quando lê um char.
E se quisermos ler vários chars?
Conversão de tipos
- No C, é possível converter de uns tipos para outros.
- Na verdade, nós já usámos esta funcionalidade sem saber, porque existem conversões que são automáticas e implícitas.
- Outras têm de ser explicitamente declaradas.
Conversões implícitas
- Quando realizamos operações binárias, o C consegue detectar se os 2 operandos são do mesmo tipo.
- Se não forem, um dos tipos é convertido no outro, porque as operações são feitas com operandos do mesmo tipo.
- O resultado da operação será do tipo “superior”.
- Neste caso, o valor de
i
é convertido parafloat
. - Se o contrário ocorresse, perdiamos por completo a componente decimal de
f
.
Operandos da mesma “classe”
reais inteiros
long double unsigned long int
^ ^
| |
double long int
^ ^
| |
float unsigned int
^
|
int
Exemplos de conversões implicitas
char c;
short int s;
int i;
unsigned int u;
long int l;
i = i + c; // c convertido para int
i = i + s; // s convertido para int
u = u + i; // i convertido para unsigned int
l = l + u; // u convertido para long int
Mais exemplos de conversões implicitas
long int l;
unsigned long int ul;
float f;
double d;
long double ld;
ul = ul + l; // l convertido para unsigned long int
f = f + ul; // ul convertido para float
d = d + f; // f convertido para double
ld = ld + d // d convertido para long double
Conversões implicitas na atribuição
char c;
int i;
float f;
double d;
i = c; // c convertido para int
f = i; // i convertido para float
d = f; // f convertido para double
i = 3.14; // 3.14 convertido para 3
c = 10000; // overflow
f = 1.0e100; // excede limite
Casting
- Para fazer uma conversão explicita, escrevemos o nome do tipo final entre parêntises, seguido do valor que queremos converter.
float f = 3 / 2; // 1.0 -> divisão de inteiros dá inteiro
f = (float) 3 / 2; // 1.5 -> converter 3 para float e dividir por 2
- O operador de casting é uma operação unária.
- Operações unárias têm precedência sobre operações binárias, logo…
(float) 3 / 2
equivale a ( (float) 3 ) / 2
Quando realizamos algumas operações aritméticas, pode ser necessário fazer uma conversão explicita.
- O resultado da multiplicação na linha 4 cabe na variável i de tipo long.
- Contudo, o resultado da operação será um int e em algumas máquinas o resultado pode levar a overflow.
long i;
int j = 10000; // 10000 * 10000 -> 100 000 000
i = j * j;
i = (long) j * j; // OK
i = (long) (j * j); // ERRADO
- Para resolver isso, podemos fazer o cast da linha 6.
- Na linha 8, a multiplicação é feita antes da conversão porque está entre
( )
.
Definições de tipos - typedef
O C permite a definição de novos tipos com o comando typedef
.
typedef
é seguido do nome original do tipo- e depois do novo nome que queremos usar
- Essencialmente, o que fizemos foi criar um
int
com um novo nome. - Útil para tornar o código mais legível
- As definições de tipo ocorrem fora de qualquer função, tipicamente após os #include.
- Os nomes dos tipos obedecem às mesmas regras dos nomes das variáveis.
- É convenção no C, capitalizar os nomes dos tipos.
sizeof
A função sizeof recebe um valor ou um tipo e indica qual é o tamanho, em bytes, que esse tipo ocupa em memória.