introduÇÃo À linguagem c - bfeijo/prog2/introducao_a_linguagem_c... · um programa em c consiste...
TRANSCRIPT
INTRODUÇÃO À LINGUAGEM C Prof. Bruno Feijó, Dept. de Informática, PUC-Rio (2018)
C foi criado no início da década de 70, quando os programas mais eficientes eram escritos em linguagem
Assembly, bem próxima à máquina. C surgiu como uma substituição ao assembly, porém mais fácil e com
eficiência próxima - … tanto que o Sistema operacional Unix e vários jogos da época foram escritos em C.
Programadores de C trabalham muito mais próximos da máquina do que em outras liguagens “high level”
e por isto precisam entender como a memória do computador funciona.
1
O sistema de numeração decimal (ou sistema de numeração posicional de base 10) requer 10 dígitos diferentes
(0,1,2,3,4,5,6,7,8,9) para representar números. Por exemplo, o número 253 é a seguinte soma:
O sistema binário (ou de base 2) usa 2 dígitos (0,1). Por exemplo, 1101 equivale ao número 13 no sistema decimal:
Vamos usar o sistema binário para armazenar números no computador.
bit (abreviação de binary digit) é a menor unidade de armazenamento (um bit armazena apenas um 0 ou 1)
byte (lê-se baite) é uma unidade de informação formada por 8 bits.
Menor número: 0; maior número: 255, total de números diferentes: 256
BITS E BYTES
2 5 3
3 x 100 = 3
5 x 101 = 50
2 x 102 = 200
253
1 0 1
1 x 20 = 1
0 x 21 = 0
1 x 22 = 4
13
1
1 x 23 = 8
2
Poderíamos pensar em usar o bit mais à esquerda do byte (leftmost) para armazenar o sinal. Neste caso, o maior
número positivo seria 127, i.e. [0111 1111]:
E como seria o número -2 ? Seria [1000 0010] ?
Nos computadores, -2 seria [1111 1110] !!
Note que desta maneira: 2 + (-2) = 0. Isto é: [0000 0010] + [1111 1110] = [0000 0000]
Nos computadores, usamos um artifício matemático chamado de “Two’s Complement” (complemento de dois) para representar
números negativos. Isto também facilita a implementação em hardware das operações com números binários.
Com este artifício, o maior número negativo é -128 (e não -127). E a faixa de valores é -128 a 127.
1 byte é muito pouco para representar números inteiros. Por isto adotamos 1 byte para representar caracteres,
como ‘A’, ‘z’, ‘[‘, … . E a ideia é bem simples: estabelecemos uma correspondência entre caracteres e códigos
numéricos. Por exemplo, 65 representa a letra A, 91 representa o abre cochete [, 122 representa a letra z, … . E
dizemos que caracteres são do tipo char. O tamanho de um char é 1 byte !
Para números inteiros, que chamamos de tipo int, juntamos 4 bytes (32 bits) e consideramos um bit para o sinal.
Desta maneira, podemos representar números de -2,147,483,648 a +2,147,483,647.
Se não usarmos o bit do sinal, podemos ter números de 0 a 4,294,967,295 e chamamos de tipo unsigned int.
float tem 4 bytes, 32 bits (aprox. 6 casas decimais de precisão), faixa: 3.41038 a 3.41038.
double tem 8 bytes, 64 bits (aprox. 15 casas decimais de precisão), faixa: 1.710308 a 1.710308.
Os tamanhos de int, float e double podem variar (com tipo de máquina e sistema operacional).
BITS, BYTES E TIPOS
1 1 111 1 10sinal
3
Na prática, você inverte os dígitos e
soma 1, e.g.
-2: [1111 1101] + 1 = [1111 1110]
Se o leftmost bit é 1, então é o número
negativo.
Uma vez que definimos tipos (vimos alguns até agora: char e int), podemos armazenar dados numéricos em blocos
de bytes que são identificados por um número chamado endereço (address). Em uma máquina byte addressable:
Uma variável é um nome dado a uma área da memória que armazena o valor (i.e. o conteúdo) de um determinado
tipo. Portanto, uma variável tem um nome (identificador), um tipo de dado e um valor. Por exemplo, as instruções
int x; : a variável x representa uma área de memória reservada de 4 bytes (tipo int)
x = 2; o número inteiro 2 é colocado na memória reservada (ver figura acima)
Uma variável é equivalente ao valor armazenado. Portanto se y = x + 2; então y contém 4.
O código do programa (instruções) também são transformados em blocos de bytes (e.g. 64 bits) e armazenados.
MEMÓRIA
…
MemóriaEndereço
1023
1022
1021
1020
3
2
1
0
int x;x = 2; fazem o seguinte:
…1027
1026
1025
10240000 0010
0000 0000
0000 0000
0000 0000
1023
1022
1021
1020
…
x
Endereço
da
variável xé 1020
Tipo int
ocupa
4 bytes
Em C, o delimitador de
uma instrução é o ;
4
0000 00100000 00000000 00000000 0000
y1024y
1020x
valor 2 em x
Mas, quando fazemos referência a uma variável, apontamos para o
primeiro endereço e consideramos o conjunto de bytes como uma única
unidade endereçável:
isto é:
1020x 0000 0000 0000 0000 0000 0000 0000 0010
1024y
1028
Um programa em C consiste basicamente de variáveis e funções.
Uma função em C recebe valores através de seus argumentos, realiza tarefas e retorna um valor.
C tem uma função principal, chamada main, que é chamada pelo Sistema Operacional quando o usuário roda o
programa. Portanto, a função main é sempre o primeiro código executado quando um programa começa.
Uma função pode chamar outras funções auxiliares. Algumas destas funções você mesmo escreveu e outras estão
em bibliotecas (libraries) que são disponibilizadas pela linguagem para você. As funções de bibliotecas requerem
definições e declarações que estão em arquivos texto com extensão .h, chamados de header files. Depois veremos
o que são estes arquivos .h.
FUNÇÕES
5
Especificação de formato:
%c um char
%d um int
%u um unsigned int
%f um double (ou float)
%e um double (ou float) no formato científico
%g um double (ou float) no formato mais apropriado (%f ou %e)
%s uma cadeia de caracteres
%p um ponteiro (endereço)
Notações: \n (new line), \t (tab), ..., \” (double quote), ...
Equação de uma reta y = ax + b, com a = 2 e b = 1:
UM PROGRAMA EM C
#include <stdio.h>
int equation1(int x){
int a = 2;int b = 1;return a*x + b;
}
int main(void){
int x = 10;int y;y = equation1(x);printf("Hello\n");printf("%d resultado %d\n", x, y);return 0;
}
Início e fim de bloco: { … }
O cabeçalho de uma função é tipoDeRetorno nome(argumentos)
Não pode ter duas funções com o mesmo nome.
Argumentos são passados by value (i.e. a função recebe uma cópia do valor do argumento e não
o seu endereço).
printf() tem um número variável de argumentos
O primeiro argumento é uma cadeia de caracteres (string), onde cada %x indica onde cada um
dos outros argumentos deve ser inserido e em que forma deve ser impresso. Por exemplo, %d
especifica um argumento inteiro. A notação \x indica um número que representa algo especial.
6
7
COMPILER & LINKER
prog.c prog.obj prog.exe
O tamanho (em bytes) de um tipo é dado pelo operador sizeof(tipo) e o endereço de uma variável é obtido com o
operador &. Acompanhe o código a seguir:
8
ENDEREÇOS
#include <stdio.h>int main(void){
char c = 'A';int a = 5;printf("Tamanho char = %d bytes\n",sizeof(char));printf("Endereco de c que contem %c(%d): %p\n", c, c, &c);printf("Proximo endereco (+1): %p\n", &c + 1);printf("\nTamanho int = %d bytes\n", sizeof(int));printf("Endereco de a: %p\n", &a);printf("Proximo endereco (+1): %p\n", &a + 1);return 0;
}Endereços são números inteiros (mas não do tipo int) no sistema
Hexadecimal, que é um sistema de numeração posicional de base 16:
(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F). Em C, usamos o prefixo 0x para representar
um hexadecimal (e.g.: 0x00CFFB80). Para imprimir, usamos %08X
printf("%08X\n", 0x00CFFB80);As conversões são feitas automaticamente. Por exemplo, para int b = 13630336; e printf("%08X\n", b); é impresso 0x00CFFB80. Tente imprimir
int b = 0x00CFFB80; como hexa e como decimal.
E você sabe fazer o cálculo? Por ex., 4A3 é (3x160) + (10x161) + (4 x 162) = 1187
Responda: você tem direito de ler ou depositar
valores neste próximo endereço?
Responda: quanto é a diferença 00CFFB80 – 00CFFB7C ?
Variáveis que guardam endereços são chamadas de ponteiros (pointers) e declaradas com *, e.g.: int * p;
9
PONTEIROS
Experimente lidar com ponteiros. Primeiro crie um ponteiro:
int * p;Depois coloque o endereço de a: p = &a;e imprima pdepois faça p = p + 1;e imprima o novo valor de p (que também é um endereço, porque
ponteiros só guardam endereços).