Em C, além da capacidade de incluir arquivos de cabeçalho personalizados, temos acesso a um conjunto robusto de bibliotecas padrão que oferecem funcionalidades pré-implementadas para uma vasta gama de tarefas. Este artigo explorará algumas das mais úteis, divididas por categoria.
Funções de Manipulação de Strings
A biblioteca <string.h> é fundamental para trabalhar com cadeias de caracteres. Ela fornece funções para calcular o comprimento, concatenar, copiar e comparar strings.
Determinando o Comprimento de uma String
A função strlen() retorna o número de caracteres em uma string, excluindo o terminador nulo \0.
#include <stdio.h>
#include <string.h>
int main() {
const char* texto = "Exemplo de C!";
// strlen retorna um valor do tipo size_t, que é um inteiro sem sinal.
printf("O comprimento do texto é: %zu\n", strlen(texto));
return 0;
}
Saída:
O comprimento do texto é: 13
Concatenando Strings
A função strcat() anexa uma string a outra. É crucial garantir que a string de destino tenha espaço suficiente para acomodar a string anexada.
#include <stdio.h>
#include <string.h>
int main() {
char destino[30] = "Bem-vindo "; // Buffer com espaço suficiente
const char* fonte = "ao mundo C!";
// Concatena 'fonte' ao final de 'destino'
strcat(destino, fonte);
printf("String concatenada: %s\n", destino);
return 0;
}
Saída:
String concatenada: Bem-vindo ao mundo C!
Copiando Conteúdo de Strings
A função strcpy() copia o conteúdo de uma string de origem para uma string de destino. Assim como strcat(), o buffer de destino deve ser grande o suficiente.
#include <stdio.h>
#include <string.h>
int main() {
char buffer[20];
const char* mensagem_original = "Olá, programador!";
// Copia 'mensagem_original' para 'buffer'
strcpy(buffer, mensagem_original);
printf("String copiada: %s\n", buffer);
return 0;
}
Saída:
String copiada: Olá, programador!
Comparando Strings
A função strcmp() compara duas strings lexicograficamente (baseado nos valores ASCII dos caracteres). Ela retorna um valor inteiro:
- Menor que zero: se a primeira string for lexicograficamente menor que a segunda.
- Zero: se as strings forem idênticas.
- Maior que zero: se a primeira string for lexicograficamente maior que a segunda.
#include <stdio.h>
#include <string.h>
int main() {
const char* s1 = "Apple";
const char* s2 = "Banana";
const char* s3 = "Apple";
int resultado1 = strcmp(s1, s2); // 'A' vs 'B'
int resultado2 = strcmp(s1, s3); // 'Apple' vs 'Apple'
printf("Comparando '%s' com '%s': %d\n", s1, s2, resultado1);
printf("Comparando '%s' com '%s': %d\n", s1, s3, resultado2);
return 0;
}
Saída:
Comparando 'Apple' com 'Banana': -1
Comparando 'Apple' com 'Apple': 0
Funções Matemáticas
A biblioteca <math.h> oferece uma vasta coleção de funções para operações matemáticas complexas, como raízes, potências e funções trigonométricas. Lembre-se que, ao compilar programas C que usam <math.h>, geralmente é necessário linkar com a biblioteca matemática adicionando -lm ao comando de compilação (ex: gcc seu_codigo.c -o seu_executavel -lm).
Raiz Quadrada
A função sqrt() calcula a raiz quadrada de um número de ponto flutuante.
#include <stdio.h>
#include <math.h>
int main() {
double valor = 25.0;
double raiz = sqrt(valor);
printf("A raiz quadrada de %.2f é %.2f\n", valor, raiz);
return 0;
}
Saída:
A raiz quadrada de 25.00 é 5.00
Cálculo de Potências
A função pow() eleva um número a uma determinada potência.
#include <stdio.h>
#include <math.h>
int main() {
double base = 3.0;
double expoente = 4.0;
double resultado_potencia = pow(base, expoente);
printf("%.1f elevado a %.1f é %.1f\n", base, expoente, resultado_potencia);
return 0;
}
Saída:
3.0 elevado a 4.0 é 81.0
Esta função pode simplificar a implementação de algoritmos, como encontrar números de Narciso (números de 3 dígitos onde a soma dos cubos de seus dígitos é igual ao próprio número):
#include <stdio.h>
#include <math.h>
int main() {
printf("Números de Narciso de 3 dígitos:\n");
for (int num = 100; num < 1000; ++num) {
int d1 = num / 100; // Centenas
int d2 = (num / 10) % 10; // Dezenas
int d3 = num % 10; // Unidades
if (pow(d1, 3) + pow(d2, 3) + pow(d3, 3) == num) {
printf("%d\n", num);
}
}
return 0;
}
Saída:
Números de Narciso de 3 dígitos:
153
370
371
407
Funções Trigonométricas
<math.h> inclui funções como sin(), cos(), tan(), antre outras. Elas esperam ângulos em radianos.
#include <stdio.h>
#include <math.h> // Para M_PI e funções trigonométricas
int main() {
// Calcular seno de 30 graus (PI/6 radianos)
double angulo_rad = M_PI / 6.0;
printf("Seno de PI/6 (30 graus): %.4f\n", sin(angulo_rad));
// Calcular tangente de 45 graus (PI/4 radianos)
angulo_rad = M_PI / 4.0;
printf("Tangente de PI/4 (45 graus): %.4f\n", tan(angulo_rad));
// Exemplo de um caso especial: tangente de PI/2 (90 graus)
printf("Tangente de PI/2 (90 graus): %f\n", tan(M_PI / 2.0)); // Retorna um valor muito grande ou INF
return 0;
}
Saída (pode variar ligeiramente):
Seno de PI/6 (30 graus): 0.5000
Tangente de PI/4 (45 graus): 1.0000
Tangente de PI/2 (90 graus): 16331239353195370.000000
Note que tan(M_PI / 2.0) se aproxima do infinito e, devido à precisão de ponto flutuante, o resultado pode ser um número muito grande em vez de um erro explícito ou infinito.
Arredondamento e Valor Absoluto
Funções como ceil() (teto), floor() (piso) e fabs() (valor absoluto para ponto flutuante) são úteis para manipulação de números.
#include <stdio.h>
#include <math.h>
int main() {
double num_decimal = 7.89;
double num_negativo = -4.56;
printf("Número original: %.2f\n", num_decimal);
printf("Arredondado para cima (ceil): %.2f\n", ceil(num_decimal));
printf("Arredondado para baixo (floor): %.2f\n", floor(num_decimal));
printf("\nValor original: %.2f\n", num_negativo);
printf("Valor absoluto (fabs): %.2f\n", fabs(num_negativo));
return 0;
}
Saída:
Número original: 7.89
Arredondado para cima (ceil): 8.00
Arredondado para baixo (floor): 7.00
Valor original: -4.56
Valor absoluto (fabs): 4.56
Funções de Utilitários Gerais
A biblioteca <stdlib.h> é um tesouro de funções de uso geral, incluindo alocação de memória dinâmica, conversão de tipos e ordenação.
Ordenação de Arrays com qsort
A função qsort() implementa o algoritmo QuickSort para ordenar arrays. Ela é genérica e pode ordenar qualquer tipo de dado, desde que seja fornecida uma função de comparação.
A assinatura de qsort é void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *));
base: Ponteiro para o primeiro elemento do array a ser ordenado.num: Número de elementos no array.size: Tamanho de cada elemento em bytes.compar: Ponteiro para uma função de comparação que define a ordem. Esta função recebe dois ponteirosconst void*e deve retornar um inteiro: negativo se o primeiro elemento for menor, zero se forem iguais, ou positivo se o primeiro for maior.
#include <stdio.h>
#include <stdlib.h> // Para qsort
// Função de comparação para inteiros (ordem crescente)
int comparar_inteiros(const void* elem1, const void* elem2) {
int val1 = *(const int*)elem1; // Converte void* para int* e desreferencia
int val2 = *(const int*)elem2;
return val1 - val2; // Retorna a diferença: negativo, zero ou positivo
}
int main() {
int numeros[] = {9, 1, 5, 8, 3, 2, 7, 4, 6, 0};
int tamanho_array = sizeof(numeros) / sizeof(numeros[0]);
printf("Array original: ");
for (int i = 0; i < tamanho_array; ++i) {
printf("%d ", numeros[i]);
}
printf("\n");
// Chama qsort para ordenar o array
qsort(numeros, tamanho_array, sizeof(int), comparar_inteiros);
printf("Array ordenado: ");
for (int i = 0; i < tamanho_array; ++i) {
printf("%d ", numeros[i]);
}
printf("\n");
return 0;
}
Saída:
Array original: 9 1 5 8 3 2 7 4 6 0
Array ordenado: 0 1 2 3 4 5 6 7 8 9
Terminando um Programa com exit
A função exit() encerra o programa imediatamente. Diferente de um return da função main, que desempilha as funções e retorna o controle ao chamador, exit() fecha todos os arquivos abertos e libera buffers antes de terminar o processo. Ela recebe um código de status que é retornado ao sistema operacional.
#include <stdio.h>
#include <stdlib.h> // Para exit e EXIT_SUCCESS
int main() {
printf("Iniciando o programa...\n");
// Alguma condição que exige o término imediato
if (1) { // Exemplo: se uma condição for verdadeira
printf("Saindo do programa com sucesso.\n");
exit(EXIT_SUCCESS); // EXIT_SUCCESS é geralmente 0
}
printf("Esta linha não será executada.\n"); // Não alcançável
return 0; // Se exit() não for chamado, este return seria executado
}
Saída:
Iniciando o programa...
Saindo do programa com sucesso.
Alocação Dinâmica de Memória: malloc e free
As funções malloc() e free() são essenciais para gerenciamento de memória dinâmica. malloc() aloca um bloco de memória do tamanho especificado em bytes e retorna um ponteiro void* para o início do bloco. Se a alocação falhar, retorna NULL. free() libera um bloco de memória que foi alocado previamente por malloc, calloc ou realloc, tornando-o disponível para reuso.
#include <stdio.h>
#include <stdlib.h> // Para malloc e free
int main() {
int* ptr_inteiro = NULL; // É uma boa prática inicializar ponteiros como NULL
// Aloca memória para um único inteiro
ptr_inteiro = (int*)malloc(sizeof(int));
if (ptr_inteiro == NULL) {
fprintf(stderr, "Erro ao alocar memória!\n");
return EXIT_FAILURE; // Retorna código de erro
}
// Atribui um valor à memória alocada
*ptr_inteiro = 256;
printf("Valor armazenado na memória alocada: %d\n", *ptr_inteiro);
// Libera a memória alocada
free(ptr_inteiro);
ptr_inteiro = NULL; // Evita ponteiros "pendurados" (dangling pointers)
printf("Memória liberada.\n");
// Exemplo de alocação para um array de 5 doubles
double* array_doubles = (double*)malloc(5 * sizeof(double));
if (array_doubles == NULL) {
fprintf(stderr, "Erro ao alocar memória para array!\n");
return EXIT_FAILURE;
}
for (int i = 0; i < 5; ++i) {
array_doubles[i] = i * 10.5;
printf("array_doubles[%d] = %.1f\n", i, array_doubles[i]);
}
free(array_doubles);
array_doubles = NULL;
return 0;
}
Saída:
Valor armazenado na memória alocada: 256
Memória liberada.
array_doubles[0] = 0.0
array_doubles[1] = 10.5
array_doubles[2] = 21.0
array_doubles[3] = 31.5
array_doubles[4] = 42.0
A alocação e liberação de memória são passos críticos para evitar vazamentos de memória e garantir o uso eficiente dos recursos do sistema.