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

As bibliotecas compartilhadas são carregadas no início da execução de um programa. No Linux, o dynamic loader procura pelas bibliotecas em /lib e /usr/lib. Caso a biblioteca não esteja presente neste caminho, recebemos uma mensagem de erro parecida com a mensagem a seguir:

error while loading shared libraries: libfoo.so:
  cannot open shared object file: No such file or directory

Imagine um ambiente de desenvolvimento, onde estamos codificando uma biblioteca. Não queremos instalar esta biblioteca no sistema só para testá-la. Uma alternativa é configurar a variável de ambiente LD_LIBRARY_PATH, apontando para o diretório onde se encontra o binário da biblioteca. Assim o dynamic loader vai procurar pela biblioteca também neste diretório.

$ export LD_LIBRARY_PATH=/home/user/libfoo/

Uma segunda opção seria no momento em que seu programa é “linkado”, passar o caminho da biblioteca para a opção -rpath do linker. Isto coloca o caminho de busca pela biblioteca dentro da estrutura do executável (ELF). A opção -Wl do gcc serve para passar parâmetros para o linker (usando “,” no lugar de espaço) que é chamado automaticamente após a compilação.

$ gcc -shared -Wall -o libfoo.so foo.c
$ gcc -Wall -o test test.c -L/home/user/libfoo/ -lfoo
$ ./test # Erro! Não acha a biblioteca libfoo.so
$ gcc -Wall -Wl,-rpath,/home/user/libfoo/ -o test test.c \
  -L/home/user/libfoo/ -lfoo
$ ./test # Funciona!

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

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.

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"

Apesar dos templates para hash_set e hash_map não serem padronizados, ambos são relativamente comuns, estando disponíveis tanto na implementação GNU (ie, no GCC) quanto no Visual C++ no Windows. Existem algumas diferenças, como o namespace um pouco mais escondido no GNUC (__gnu_cxx vs stdext) e alguns métodos que existem em um mas não em outro (p.ex.: reverse_iterator que não existe no GCC); mas a maioria das vezes servem para o serviço.

Um problema comum com essas classes no GCC é um erro meio criptico quando tentamos usar __gnu_cxx::hash_set<std::string> ou __gnu_cxx::hash_map<std::string>:

/usr/include/c++/4.2/ext/hashtable.h:595: error: no match for call to '(const __gnu_cxx::hash<std::basic_string<char, std::char_traits, std::allocator > >) (const std::basic_string<char, std::char_traits, std::allocator >&)'

Este erro ocorre porque a libstdc++ não define uma função de hash para std::string. A solução é definir uma (O RLY?). Felizmente a biblioteca padrão define uma função de hash para strings do tipo char*, então podemos aproveitá-la:

struct string_hash : public std::unary_function<std::string,size_t>
{
  size_t operator() (const std::string &v) const
  {
    return __gnu_cxx::hash()(v.c_str());
  }
};
typedef __gnu_cxx::hash_set<std::string,string_hash> string_hash_set;

Para obter ponteiros a métodos ou funções sobrecarregadas, como exemplo:

class Foo
{
public:
void bla(int a);
int bla() const;
};

Se tentarmos usar o nome da função diretamente, o compilador não saberá a qual dos métodos você se refere. Para sair da ambiguidade, necessitamos usar a “assinatura” (os tipos dos parâmetros e do valor retornado) da função indiretamente, possivelmente com o uso de variáveis temporárias:

void (Foo::*ptr1)(int)= &Foo::bla;
int (Foo::*ptr2)() const= &Foo::bla;
Add to Technorati Favorites

Assuntos