You are currently browsing the category archive for the 'C' category.

Um mesmo aplicativo pode se comportar de forma diferente dependendo da forma como é invocado. No exemplo a seguir, o mesmo programa é usado para calcular a raiz quadrada e potência de dois de um dado argumento.

#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    double res;
    char *program = basename(argv[0]);

    if (argc != 2)
        return 1;

    if (!strcmp(program, "sqrt")) {
        res = sqrt(atof(argv[1]));
        printf("%f\n", res);
        return 0;
    }

    if (!strcmp(program, "pow2")) {
        res = pow(atof(argv[1]), 2);
        printf("%f\n", res);
        return 0;
    }

    printf("Invalid program: %s\n", program);

    return 1;
}

O truque consiste em analisar o primeiro argumento que corresponde ao caminho do binário do programa. No exemplo, decidimos qual ação tomar comparando o nome do programa com “sqrt” e “pow2″. Para não fazer múltiplas cópias do binário gerado, fazemos links simbólicos.

$ gcc -Wall -lm -o test test.c
$ ln -s test sqrt
$ ln -s test pow2

$ ./test 5
Invalid program: test

$ ./pow2 5
25.000000

$ ./sqrt 5
2.236068

A técnica é extensamente utilizada no BusyBox e pode facilmente ser implementada em qualquer outra linguagem de programação.

Em C não existe um tipo nativo booleano, temos 0 como falso e tudo diferente de 0 é verdadeiro. Em C++ existe o tipo booleano, onde verdadeiro e falso correspondem respectivamente a 0 e 1.

Uma forma de fazer um “cast” em C para um tipo booleano compatível com C++ seria usando a dupla negação, como no exemplo a seguir.

int value1 = 0;
int value2 = 1;
int value3 = 50;
int value4 = -1;

printf("value1 = %d\n", !!value1); // Imprime value1 = 0
printf("value2 = %d\n", !!value2); // Imprime value2 = 1
printf("value3 = %d\n", !!value3); // Imprime value3 = 1
printf("value4 = %d\n", !!value4); // Imprime value4 = 1

No Linux é muito comum um daemon, durante sua execução, criar um arquivo .pid dentro de /var/run. Dentro do arquivo syslogd.pid, por exemplo, contém o PID da instância do syslogd em execução. Usa-se este mecanismo para impedir que duas instâncias do mesmo processo rodem simultaneamente e conflitem na obtenção de recursos.

Porém, a simples existência do arquivo .pid não garante que o processo esteja em execução. Ele pode ter sido fechado de uma forma inesperada e não o apagou. Então, temos que ler o conteúdo do arquivo .pid e certificar que o processo com aquele número de PID está em execução.

A forma mais fácil de fazer isso é enviando o sinal 0 para o processo. O sinal 0 é especial e não é de fato enviado, caso contrário o processo destinatário poderia ser fechado se não tratasse o sinal, mas o retorno da função indica se um sinal real seria enviado com sucesso. O código a seguir exemplifica como aplicar a técnica em C:

int main(int argc, char **argv)
{
    unsigned int pid;

    if (argc != 2) {
        printf("Uso: %s PID\n", argv[0]);
        return 1;
    }

    pid = (unsigned int) atoi(argv[1]);

    if (!kill(pid, 0))
        printf("%d esta em execucao.\n", pid);
    else
        printf("%d nao esta em execucao.\n", pid);

    return 0;
}

A mesma técnica pode ser aplicada no terminal:

$ kill -0 123
$ echo $? # Imprime 0 se o PID 123 existir, 1 caso contrario

Note que você precisa ter permissão para enviar um sinal para um determinado processo. Para um usuário sem privilégios especiais, a dica só vai funcionar para verificar a existência de processos iniciados pelo próprio usuário.

Um recurso bastante útil do GCC (apenas) são vetores de tamanho zero. O uso é permitido apenas como último elemento de uma estrutura de dados.

struct pessoa {
    int idade;
    char nome[0];
};
printf("%d\n", sizeof(struct pessoa)); // Imprime "4"

Note que a estrutura tem o mesmo tamanho de um inteiro (na minha máquina). Então podemos concluir que o vetor de tamanho zero não ocupa espaço, ele apenas serve como um identificador para a primeira posição de memória após a estrutura. Isto nos permite fazer algo assim:

struct pessoa * cria_pessoa(int idade, char* nome)
{
    int tamanho = sizeof(struct pessoa) + strlen(nome) + 1;
    struct pessoa *novo = malloc(tamanho);

    novo->idade = idade;
    strcpy(novo->nome, nome);

    return novo;
}
int main(void)
{
    struct pessoa *teste = cria_pessoa(18, "Jose");

    /* Imprime "Jose tem 18 anos." */
    printf("%s tem %d anos.\n", teste->nome, teste->idade);

    free(teste);

    return 0;
}

Note que conseguimos alocar toda a memória para a estrutura em apenas uma operação, o que não seria possível caso “nome” fosse um ponteiro, o que nos custaria mais uma chamada de malloc. Da mesma forma a desalocação de toda a estrutura foi feita usando um comando.

Esta técnica permite maior flexibilidade e um melhor aproveitamento de memória do que construir a estrutura com o atributo nome sendo “char nome[200]” (tamanho fixo) supondo que um nome nunca seria maior que 200 caracteres.

A forma tradicional de um programa ir para background é fazendo um fork(), terminar o processo pai e criar uma nova sessão com setsid(). Outra forma mais simples, que de fato encapsula tudo isso e mais um pouco, é apenas usar a função daemon, como o Ademar sugeriu neste comentário.

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    if (daemon(0, 0)) {
        printf("Erro indo para background.\n");
        return 1;
    }

    printf("Esta mensagem nao deve ser impressa.\n");

    sleep(10); // Durma por dez segundos antes de sair

    return 0;
}

Nada será impresso porque ao passar zero para o segundo parâmetro da função, faz com a saída padrão seja redirecionada para /dev/null, assim como o Cláudio ensinou (e agora vi que dupliquei a dica dele).

Uma vez bem sucedido o comando, nosso processo estará rodando em background desconectado do terminal que o invocou. Você poderá observar que mesmo após o retorno instantâneo do terminal, o nosso programa ainda aparecerá na lista de processos por 10 segundos.

Imagine uma função definida em uma macro que faça a potência de dois de um determinado número. Ela poderia ser definida da seguinte forma:

#define quadrado(n) \
    n * n

Acontece que o comportamento, dependendo dos parâmetros, nem sempre é o esperado. Veja o que acontece no exemplo a seguir:

int main(void)
{
    int a = 2;

    printf("%d\n", quadrado(a)); // Imprime 4

    printf("%d\n", quadrado(a + 1)); // Imprime 5

    return 0;
}

A expansão da macro quadrado(a + 1), resultou a seguinte expressão:

a + 1 * a + 1
2 + 1 * 2 + 1 // Substituindo os valores de a
2 + 2 + 1 // Obedecendo a precedência dos operadores
5

Neste caso, para resolver o problema a sugestão seria usar uma função inline, que assim como a macro, poupa uma chamada de função mas se comporta como uma função convencional.

inline int quadrado(int n) {
    return n * n;
}

As variáveis da pilha de execução, são automaticamentes desalocadas quando a mesma retorna. No caso do exemplo a seguir, o retorno da função teste() é o endereço de memória da string “Teste123″ (ou seja, para onde str aponta e não str propriamente dito).

char *teste(void)
{
    char *str = "Teste123";

    return str;
}

Esta string está alocada na seção de dados do programa e existirá durante toda sua execução, não se restringindo ao escopo da pilha, o que torna segura sua utilização.

Vale lembrar que esta string é protegida somente para leitura, qualquer tentativa de escrita em uma de suas posições, fatalmente irá gerar uma falha de segmentação.

Quem usa o GCC pode tirar vantagem do pré-processador para banir de vez do código algumas funções consideradas maléficas como gets() e sprintf(), por exemplo. Basta acrescentar no seu código a seguinte linha:

#pragma GCC poison gets sprintf

Um erro semelhante a este irá surgir em tempo de compilação:

teste.c:6:12: error: attempt to use poisoned "gets"
Add to Technorati Favorites

Assuntos