Uma documentação prática e simples feita de universitários para universitários com o objetivo de facilitar o aprendizado da linguagem c.
- Um pouco sobre bibliotecas
- Escopo principal de um programa
- Hello world
- Tipos de dados
- Especificadores mais comuns
- Float vs Double
- Limitando casas decimais em c
- Vetores ou arrays - Em falta
- Vetor de caracteres
- Operadores
- Mais operações com números
- Comentários
- Constantes
- Trabalhando com entradas e saídas
- Condicionais
- Laços de repetiçao
- Funções - Em falta
- Struct - Em falta
- Data e hora - Em falta
As bibliotecas nos auxiliam a executar instruções no nosso programa. para adiciona-las basta utilizar #include + nome da biblioteca:
#include <stdio.h>
// Resto do programa
No caso da biblioteca stdio.h, 'std' vem de standard(padrão) e 'io' significa input/output (Entrada/saída), portanto é facil imaginar que essa biblioteca adiciona instruções de entrada e saída padrões, são o 'printf' e 'scanf'. Existem varias outras bibliotecas como:
- math.h
- stdlib.h
- string.h
- limits.h
- stdbool.h
- time.h
Dentre varias outras.
Em C, é comum ver nosso programa rodar dentro de uma função principal:
//importações
int main() {
//códigos do programa
return 0; //retorno indicando que o programa rodou como esperado
}
Com o que vimos até aqui, já podemos construir o clássico programa hello world:
#include <stdio.h> //biblioteca que adiciona entradas e saídas
int main() { // função principal
printf(“hello world”); // saída da string hello world
return 0; // retorno de sucesso do programa
}
Como em toda linguagem temos vários tipos de dados. Em C podemos utilizar algumas “expressões”, os Especificadores de formato, para tipar as variaveis na entrada e saída. A seguir veja os tipos de dados, seus devidos tamanhos e especificadores de formato:
Tipo de dados | Memoria(bytes) | Intervalo | especificador de formato |
---|---|---|---|
short int | 2 | -32, 768 a 32, 767 | %hd |
unsigned short int | 2 | 0 a 65, 535 | %hu |
unsigned int | 4 | 0 a 4, 294, 297, 295 | %u |
int | 4 | -2,147,483,648 to 2,147,483,647 | %d |
long int | 4 | -2,147,483,648 a 2,147,483,647 | %ld |
unsigned long int | 4 | 0 a 4,294,967,295 | %lu |
long long int | 8 | -(2^63) a (2^63)-1 | %lld |
unsigned long long int | 8 | 0 a 18,446,744,073,709,551,615 | %llu |
signed char | 1 | -128 a 127 | %c |
unsigned char | 1 | 0 a 255 | %c |
float | 4 | %f | |
double | 8 | %lf | |
long double | 16 | %Lf |
De forma mais simplificada, aqui estão os especificadores mais comuns e que serão mais utilizados:
especificador de formato | descrição |
---|---|
%d or %i | É usado para imprimir o valor inteiro com sinal, onde inteiro com sinal significa que a variável pode conter valores positivos e negativos. |
%u | É usado para imprimir o valor inteiro sem sinal, onde o inteiro sem sinal significa que a variável pode conter apenas valor positivo. |
%o | É usado para imprimir o inteiro octal sem sinal, onde o valor inteiro octal sempre começa com um valor 0. |
%x | É usado para imprimir o inteiro hexadecimal sem sinal, onde o valor inteiro hexadecimal sempre começa com um valor 0x. Neste, os caracteres alfabéticos são impressos em letras minúsculas, como a, b, c, etc. |
%X | É usado para imprimir o inteiro hexadecimal sem sinal, mas% X imprime os caracteres alfabéticos em maiúsculas, como A, B, C, etc. |
%f | É usado para imprimir os valores de ponto flutuante decimal. Por padrão, ele imprime os 6 valores após '.'. |
%e/%E | É usado para notação científica. Também é conhecido como Mantissa ou Expoente. |
%g | É usado para imprimir os valores de ponto flutuante decimal e usa a precisão fixa, ou seja, o valor após o decimal na entrada seria exatamente o mesmo que o valor na saída. |
%p | É usado para imprimir o endereço em formato hexadecimal. |
%c | É usado para imprimir o caractere sem sinal. |
%s | É usado para imprimir as strings. |
%ld | É usado para imprimir o valor inteiro com sinal longo. |
A primeria vista eles podem parecer iguais, mas há uma enorme diferença. Como o nome indica, a double tem 2x a precisão de Float.
double possui 52 bits de mantissa + 1 bit oculto: log (2 53 ) ÷ log (10) = 15,95 dígitos
float possui 23 bits mantissa + 1 bit oculto: log (2 24 ) ÷ log (10) = 7,22 dígitos
Essa perda de precisão pode levar ao aumento de erros de truncamento quando cálculos repetidos são feitos.
Em linguagem C, ao contrário de outras linguagens, não existe um tipo de dados string nativo.
Para representar uma string em C, devemos criar um vetor de caracteres, ou seja um vetor do tipo char:
char name[] = "john smith"
// ou
char name[20] = "john smith" // ao passar 20 entre os colchetes, definimos que
// a string terá no máximo 20 caracteres.
Muitas vezes o resultado de um calculo pode ser um valor com muitas casas decimais, o que pode ser estranho para nós. Portanto nós podemos preferir apresentar um valor com um número limitado de casas decimais.
Para limitar as casas decimais de um valor float para fins de saída, podemos utilizar "%.2f":
float number = 3.7855342022;
printf("%.2f", number);
// O resultado será 3.78
troque o numero depois da porcentagem como quiser para diminuir ou aumentar o limite, "%.1f", "%.3f".
Um operador é um símbolo que indica a realização de uma operação sobre uma ou mais variáveis ou valores.
- Relacionais
- '<' menor que
- '>' maior que
- '<=' menor ou igual
- '>=' maior ou igual
- '==' igual
- '!=' diferente
- Aritméticos
- '/' divisão
- '*' multiplicação
- '-' subtração
- '+' adição
- '%' módulo
- Lógicos
- '&&' operador binario E (and)
- '||' operador binario OU (or)
- '!' operador de negação (not)
- Incremento
- '++' incremento
- '--' decremento
podemos utilizar a biblioteca <math.h> para trabalhar operações mais complexas com os números através de funções aritméticas:
-
ceil( ) - arredonda o número fornecido. Ele retorna o valor inteiro que é maior ou igual ao número fornecido.
-
floor( ) - arredonda o número fornecido para baixo. Ele retorna o valor inteiro que é menor ou igual ao número fornecido.
-
sqrt( ) - retorna a raiz quadrada de um determinado número.
-
pow( ) - retorna a potência de um determinado número. recebe 2 parâmetros, base, expoente
ex: pow(2, 4), retorna 16
-
abs( ) - retorna o valor absoluto de um determinado número.
Como toda linguagem, o c tem um bloco especial que não é compilado para o executável e pode ser usado para comentar, documentar, ignorar códigos e etc...
podemos usar comentários de multiplas linhas:
/* Comments can contain keywords such as
for and while without generating errors. */
ou
/* MATHERR.C illustrates writing an error routine
* for math functions.
*/
comentários de linha única dependendo do compilador:
// This is a valid comment
Para utilizar uma constante, apenas colocamos const antes da declaração da "variável"(a partir desse momento deixa de ser uma varável rsrs):
const int number = 4;
Como vimos, ao adicionar a biblioteca <stdio.h>, podemos utilizar inputs e outputs padrões do c:
- printf - saída de dados
- scanf - entrada de dados
int num;
printf("essa é uma saída");
scanf("%d", &num); // Essa é uma entrada
Como podemos ver, o scanf recebe como primeiro parâmetro um especificador de tipo referente ao tipo de dado que esperamos receber e recebe como segundo parâmetro uma variável onde será guardado o valor recebido.
Note que antes da váriavel vai um & comercial, esse & é o operador "endereço de". Então o resultado dele sempre será o endereço de memória do objeto em questão.
Em C essa passagem sempre tem que ser explícita, já que todas passagens se dão por valor. Neste caso está passando o valor que é o endereço de memória, que será interpretado em algum lugar pegando o valor que está sendo apontado por este endereço, criando assim uma indireção. No caso específico o scanf() espera justamente um endereço onde ele deve armazenar o que for digitado pelo usuário.
O operador oposto é o * que é pegar o valor apontado pelo endereço.
O scanf tem uma peculiaridade no mínimo interessante, perceba:
#include <stdio.h>
#include <stdlib.h>
int main() {
char name[20];
printf("informe o seu nome:");
scanf("%s", name);
printf("seu nome é %s", name);
return 0;
}
A resposta desse programa vai ser algo do tipo:
informe seu nome: john
seu nome é john
porém, o problema surge caso seja passado um nome e sobrenome:
informe seu nome: john smith
seu nome é john
Ele continua retornando apenas o primeiro nome.
Uma solução para isso é usarmos o fgets no lugar de scanf:
#include <stdio.h>
#include <stdlib.h>
int main() {
char name[20];
printf("informe o seu nome:");
fgets(name, 20, stdin);
printf("seu nome é %s", name);
return 0;
}
dessa vez, ao executar o programa, tudo segue normalmente:
informe seu nome: john smith
seu nome é john smith
o fgets é usado para se ler uma string num arquivo. A função recebe 3 argumentos: a string a ser lida, o limite máximo de caracteres a serem lidos e o ponteiro para FILE, que está associado ao arquivo de onde a string será lida.
no exemplo acima, passamos como referencia de arquivo o stdin que significa standard input, ou seja, pegamos a referência da entrada padrão, assim como fazemos no scanf.
Condicionais são instruções para testar se uma condição é verdadeira ou não.
if (condição realizada) {
lista de instruções;
}
// ou
if ((condição1)&&(condição2)) {
lista de instruções;
}
// Exemplo:
if (10 > 5) {
printf("10 é maior que 5");
}
if (condição realizada) {
lista de instruções
}
else {
outra série de instruções
}
(condição) ? instrução se verdadeira: instrução se falsa
// Exemplo
( 10 > 5 ) ? printf("10 é maior que 5") : printf("10 não é maior que 5");
obs: A condição sempre deve ficar entre parênteses. Esse tipo de operação retorna um valor.
A Instrução switch efetua vários testes de valores sobre o conteúdo de uma mesma variável.
int number = 10 - 5;
switch (number) {
case 5:
printf("10 - 5 é 5");
break;
case 4:
printf("10 - 5 é 4");
break;
default:
printf("Isso sempre vai acontecer");
}
Os laços de repetição ou loops são estruturas que executam várias vezes a mesma série de instruções até que uma condição não seja mais realizada.
for (contador; condição; modificação do contador) {
lista de instruções;
}
//exemplo:
for (i=1; i<6; i++) {
printf("%d", i);
}
while (condição realizada) {
lista de instruções;
}
// Exemplo:
int n = 0;
while (n < 5) {
n++;
}
Diferente do while, o do-while executa as instruções pelo menos 1 vez antes de testar qualquer condição.
do{
//bloco de comandos
} while (/*expressao*/);
// Exemplo:
int n = 6;
do{
n++;
} while (n < 5);
Perceba que mesmo desde o inicio n sendo maior que 5, o do-while vai ser executado uma vez incrementando o n, assim n será 7 no final.
Códigos em C podem ser maçantes de se escrever, afim de reaproveitar trechos de código e torna-lo mais organizado construimos as funções. Em sumo, funções são pedaços de código que podem ser chamados inúmeras vezes afim de efetuar alguma operação uma vez que especificamos os parametros de entrada e saída, dessa forma conseguimos modularizar o problema pois agora podemos dividir o código em várias partes.