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"







Comentários Recentes