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

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;

Palavras-chave: C, pré-processador, macros, macro, define, gcc

O pré-processador do GCC define uma série de macros por default. Muitas delas podem lhe ser útil, caso se deseje que um determinado bloco de código seja compilado para apenas uma determinada arquitetura, por exemplo. Para conhecer estas macros, basta perguntar ao pré-processador:

$ echo foo |gcc -dM -E -
(...)
#define __VERSION__ "4.1.2 20061115 (prerelease) (Debian 4.1.1-21)"
#define i386 1
#define __linux__ 1
#define __gnu_linux__ 1
#define __i486__ 1
#define unix 1
#define __i386__ 1
#define __SIZE_TYPE__ unsigned int
#define __ELF__ 1
#define __FLT_HAS_QUIET_NAN__ 1
#define __FLT_MAX_10_EXP__ 38
#define __LONG_MAX__ 2147483647L
#define __FLT_HAS_INFINITY__ 1
#define linux 1
(...)
$

A saída acima, mostra algumas das macros definidas pelo pré-processador i386-linux do Debian Etch. No pré-processador para a arquitetura arm-palmos temos as seguintes macros:

$ echo foo |arm-palmos-gcc -dM -E -
(...)
#define __palmos__ 1
#define __FLT_MIN__ 1.17549435e-38F
#define __CHAR_BIT__ 8
#define __WCHAR_MAX__ 2147483647
#define __DBL_DENORM_MIN__ 4.9406564584124654e-324
#define __FLT_EVAL_METHOD__ 0
#define __SIZE_TYPE__ long unsigned int
#define __ELF__ 1
#define __DBL_MIN_10_EXP__ (-307)
#define __FINITE_MATH_ONLY__ 0
#define __ARMEL__ 1
#define __GNUC_PATCHLEVEL__ 1
#define __FLT_RADIX__ 2
(...)
$
Add to Technorati Favorites

Assuntos