DEV Community

Cover image for Recursos interessantes do C
Daniel Rocha
Daniel Rocha

Posted on • Updated on

Recursos interessantes do C

A linguagem C ainda é relevante nesse século, passou e ainda passa pelo teste do tempo, e mostra-se importante no desenvolvimento de software.

Porque aprender C?

C tem interoperabilidade com todas ou quase todas linguagens atualmente. OK! mas o quê isso significa? Significa que você pode turbinar sua linguagem favorita usando C, chamando funções escritas em uma linguagem de sistema em linguagens de alto nível como Python ou usando Webassembly com JavaScript.

Índice

Recursos

OBS: caso tenha interesse em executar os exemplos utilize um compilador online: GDB online Debugger

Ponteiros

Trabalhar livremente com a memória pode ser considera uma arma bem poderosa do C, mas segundo o tio Ben: "Com grandes poderes vêm grandes responsabilidades", ou seja, você pode torna-se um vilão caso não use direito essa arma.

Por qual motivo eu considero ponteiros interessante? Penso que entender esse recurso pode facilitar no entendimento de estrutura de dados, além de dá um overview ao desenvolvedor sobre como sua linguagem xodó fazem as mágicas que tanto ama.

Exemplo:

#include <stdio.h>


/*
 Algoritmo imprimi valor da variável
 antes e depois da modificação
*/ 

void change_age(int *age);

int main()
{  
    int age = 16;
    printf("Before: %d\n", age);
    change_age(&age);
    printf("After: %d\n", age);
    return 0;
}

void change_age(int *age)
{
    *age = 21;
}
Enter fullscreen mode Exit fullscreen mode

O seu sucesso na linguagem C vai depender se você vai ou não entender ponteiros.

Ver mais

Ponteiro pra função

O exemplo abaixo é uma implementação de forEach e map , funções que linguagens como JavaScript e Python possuem e que facilitam a manipulação de coleções de dados. O exemplo tem como base a sintaxe do JavaScript.

#include <stdio.h>
#include <stdlib.h>

/*
 O algoritmo analisa um array e retorna
 um novo array com os dados multiplicados
 por 2, e finaliza imprimindo os novos números.
*/

int *map(int ( *callback )(int value, int index, int *arr ), int *array, int length);
void forEach(void ( *callback )(int value, int index, int *arr ), int *array, int length);

int multiplyBy2(int value, int index, int *arr);
void show(int value, int index, int *arr);


int main()
{
    int numbers[length] = {5, 4, 3, 2, 1};
    int length = (int)(sizeof(numbers) / sizeof(int));

    int *newArray = map(multiplyBy2, numbers, length);

    printf("Array | Map\n");
    forEach(show, newArray, length);

    free(newArray);

    return 0;
}


int *map(int ( *callback )(int value, int index, int *arr ), int *array, int length)
{
    int *newArray = (int*) calloc(length, sizeof(int));

    for(int i = 0; i < length; i++)
        newArray[i] = callback(array[i], i, array);

    return newArray;
}


void forEach(void ( *callback )(int value, int index, int *arr ), int *array, int length)
{
   for(int i = 0; i < length; i++)
        callback(array[i], i, array);
}


int multiplyBy2(int value, int index, int *arr)
{
    return value * 2;
}


void show(int value, int index, int *arr)
{
    printf("Valor: %d\n", value);
}
Enter fullscreen mode Exit fullscreen mode

Ver mais

Ponteiro pra função com struct

#include <stdio.h>

/*
 O algoritmo imprimi com "intensidade"
 diferente a frase/palavra passada por
 parâmetro para a função com struct. 
*/

typedef struct {
    void (*shouting)(const char *message);
    void (*whispering)(const char *message);
} Speak;

Speak new();


int main()
{
    Speak speak = new();

    speak.shouting("Hi");
    speak.whispering("Hi");

    return 0;
}


void shouting(const char *message)
{
    printf("%s !!!!!!!!\n", message);
}


void whispering(const char *message)
{
    printf("... %s ...\n", message);    
}


Speak new()
{
    Speak speak;
    speak.shouting = shouting;
    speak.whispering = whispering;

    return speak;
}

Enter fullscreen mode Exit fullscreen mode

Mas como isso funciona? Simples, se você imaginar que a função está em algum lugar da memória, seria sensato pressupor que poderíamos "apontar" para a função, semelhante como fazemos com variáveis. Interessante é que a chamada das funções através de struct se parece com a chamada de métodos de linguagens OOP.

Ponteiro pra void

Esse recurso do C pode ser considerando um generics, caso não entenda o conceito de generics clique aqui.

#include <stdio.h>

/*
 Algoritmo imprimi o tipo e valor 
 do endereço passado pra função noType 
*/

void noType(const char* format, void* element);

int main()
{
    int  valueInt = 10;
    char valueChr = 'A';

    noType("%d", (void*) &valueInt );
    noType("%c", (void*) &valueChr );

    return 0;
}


void noType(const char* format, void* element)
{

    while(*format != '\0')
    {
        if(*format == '%')
        {
            char type = *(++format);

            switch(type)
            {
                case 'd':{

                    int value = *(int*) element;
                    printf("EH INT: %d\n", value); 
                    break;    
                }

                case 'c':{
                    char value = *(char*) element;
                    printf("EH CHAR: %c\n", value);
                    break;
                }
            }
        }

        ++format;
    } 
}

Enter fullscreen mode Exit fullscreen mode

Observe que a assinatura da função noType que recebe um const char e void*, a constante de caractere ( string ) auxilia a função a saber o tipo do endereço passado no segundo parâmetro, algo semelhante ao printf e scanf

Ponteiro pra void é recurso bastante usado, exemplo, olhe as assinaturas de malloc, calloc, realloc e free:

void* malloc( size_t size );
void* calloc( size_t num, size_t size );
void* realloc( void* ptr, size_t new_size );
void free( void* ptr );
Enter fullscreen mode Exit fullscreen mode

Ver mais

Argumentos variáveis

Caso tenha escrito no mínimo um "hello world" em C, saiba que já utilizou esse recurso, não de forma direta, já que não implementou a função printf . Veja a assinatura da função printf.

int printf( const char *format, ... );
Enter fullscreen mode Exit fullscreen mode

As reticências no final, simboliza que a função terá argumentos variáveis, ou seja, a função não sabe quantos parâmetros irá receber.

A função printf no primeiro argumento recebe uma constante de caracteres (string) com a formatação pra que seja feito o casting dos parâmetros, já que a função não conhece o tipo passado. Acesse aqui e veja como implementar o seu próprio printf.

Temos que inserir a biblioteca "stdarg.h" e podemos iniciar com o exemplo abaixo.

#include <stdio.h>
#include <stdarg.h>

/*
 O algoritmo soma os números passados
 por parâmetro, exceto o primeiro argumento, 
 pois indica a quantidade de números passados.
*/

int sum(int quantity, ...);


int main()
{
    printf("Sum of 2 + 3 + 6 = %i\n", sum(3, 2, 3, 6));
    return 0;
}


int sum(int quantity, ...)
{
    va_list numbers;
    int accumulator = 0;
    va_start(numbers, quantity);

    for(int i = 0; i < quantity; i++)
        accumulator += va_arg(numbers, int);

    va_end(numbers);

    return accumulator;
}
Enter fullscreen mode Exit fullscreen mode

Esse exemplo não serve pra basicamente nada, mas vou mostrar como usei esse recurso para um finalidade que particularmente gostei. Em um projetinho de teste, usei argumentos variaveis para controlar as sprites de exibição de um personagem em um "jogo".

void run_animation(Actor *character, double framesperseconds, int select_animation, AnimationControl control);
Enter fullscreen mode Exit fullscreen mode

Onde o parâmetro AnimationControl control é passada pra realizar o controle de exibição dos sprites usando a função abaixo.

Assinatura: Documentação

AnimationControl animation_control(int number_sprites, ...);
Enter fullscreen mode Exit fullscreen mode

Ver mais

Onde posso encontrar conteúdo sobre C?

Há bastante conteúdo pra aprender C/C++ na internet, uma simples "Googlada" e poderá encontrar matérias de estudo, mas fica a alguns links.

Discussion (0)