You are currently browsing Alfredo Kojima’s articles.

Palavras-chave: C, linkagem estática, undefined reference, referência indefinida

Ao linkar programas que usam bibliotecas estáticas que por usa vez usam outras bibliotecas estáticas, pode ocorrer de o linker reclamar da falta de símbolos de uma das bibliotecas, apesar de eles estarem comprovadamente definidos em uma delas. Isso pode ocorrer por causa da ordem errada em que as bibliotecas foram passadas ao linker.

Apesar de o linker se virar para encontrar símbolos em bibliotecas dinâmicas, para as estáticas é necessário passá-las em ordem de dependência. Por exemplo, se seu programa usa a libpng.a que usa a libz.a internamente, o comando correto para linká-lo é:

gcc programa.c -oprograma /usr/lib/libpng.a /usr/lib/libz.a

Mas se a ordem estivesse invertida:

$ gcc programa.c -oprograma /usr/lib/libz.a /usr/lib/libpng.a
/usr/lib/libpng.a(png.o): In function `png_reset_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_calculate_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_calculate_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_reset_zstream': undefined reference to `inflateReset'
collect2: ld returned 1 exit status

Palavras-chave: C, gcc, cpp, macros, …, varargs, número de parâmetros variável, variadic macros

Para escrever macros de função com número variável de argumentos:

#define DPRINT(fmt, ...)   printf("DEBUG: "fmt"\\n", ## __VA_ARGS__)

__VA_ARGS__ será substituído pelos parâmetros dados. E o ## entre a vírgula e __VA_ARGS__ se encarregará de remover a vírgula quando o número de argumentos for 0.

Exemplo:

#include <stdio.h>
#define DPRINT(fmt, ...) printf("DEBUG: "fmt"\\n", ##__VA_ARGS__)

int main()
{
  int i= 0, j= 0;
  DPRINT("chegou aqui");
  DPRINT("valor de i = %i, j = %i", i, j);
  return 0;
}

Nota: O Visual C++, a partir da versão 2005, passou a incluir o mesmo recurso.

Palavras-chave: C++, gcc, g++, erro, compilacão, linker, linking

Na maioria dos casos, o erro

undefined reference to `vtable for Class'

ocorre quando falta a definicão de algum método declarado na classe.

Certifique-se que seu Makefile contém o arquivo com a definicão da classe onde ocorreu o erro e que todos os métodos declarados estejam definidos.

Se você quiser mais detalhes sobre o quê causa este erro, leia http://gcc.gnu.org/faq.html#vtables.

Palavras-chave: exceções, gdb, depuração, debug exceptions, catch

Ao depurarmos programas C++ o GDB costuma interromper a execução quando a exceção é pega (catch) ou já quando um signal é lançado e o programa é abortado. O problema é que esse momento é tarde demais para saber o que ocorreu e o stacktrace é inútil. O que queremos é que o GDB faça um break quando a exceção é lançada e para isso usa-se o comando catch throw. O mesmo comando pode ser usado para interromper a execução na captura (catch catch).

Palavras-chave: for, iteração, índice

O for do Python é bastante conveniente para iterar listas, descartando a necessidade de variáveis de índice auxiliares. No entanto as vezes é necessário iterar uma lista e ter o índice de cada elemento além do elemento em si. A forma óbvia é algo como:

for i in range(len(lista)):
   elem= lista[i]
   print i, "=", elem

Mas algumas vezes isto pode ser inconveniente (na construção py:for do TurboGears, por exemplo). Nestes casos podemos usar o map() para criar uma lista de tuplas de cada elemento com seu índice:

>>> lista= ["maçã", "banana", "pêra"]
>>> for i, elem in map(None, range(len(lista)), lista):
>>>   print i, "=", elem
0 = maçã
1 = banana
2 = pêra

Atualização: Como comentado pelo Barbieri, a partir do Python 2.3 existe a função enumerate() que faz a mesma coisa:

>>> lista= ["maçã", "banana", "pêra"]
>>> for i, elem in enumerate(lista):
>>>   print i, "=", elem
0 = maçã
1 = banana
2 = pêra

Palavras-chave: C, memória, otimização, arrays, vetores, bitmap, pixmap

Ao se escrever código para manipulação de strings ou imagens, é comum percorrer grandes vetores de bytes. A maneira óbvia de fazer isto é algo no estilo de:

char *ptr= buffer;
for (i= 0; i < fim; i++)
      *ptr++= 0;

Mas percorrer memória byte a byte é bastante lento, por questões de alinhamento de memória, o fato dela ser endereçada por palavra. Por exemplo, um int em i386 e long em x86_64.

O exemplo abaixo inclui código que percorre por byte e por int, mas pode ser facilmente modificado para percorrer por longs:

#include <sys/types.h>
int main()
{
    char buffer[1000];
    uint32_t *iptr;
    char *ptr;
    int i, j;
    int max= 973;
    for (j= 0; j < 100000; j++)
    {
#ifdef lento
        ptr= buffer;
        for (i= max-1; i>=0; --i)
            *ptr++= 0;
#else
        iptr= (uint32_t*)buffer;
        // processa o vetor de 4 em 4 bytes,
        // arredondando o número de elementos
        for (i= max>>2-1; i>=0; --i)
            *iptr++= 0;
        ptr= iptr;
        // processa os últimos bytes restantes
        switch (max&((1<<2)-1))
        {
            case 3: *ptr++= 0;  // sem break
            case 2: *ptr++= 0;  // sem break
            case 1: *ptr++= 0;
              break;
        }
#endif
    }
    return 0;
}

Os tempos de execução para o código com percorrimento por palavras de 8, 32 e 64bits em uma máquina x86_64, são de: 0.190s, 0.100s e 0.025s, uma melhora de até 800%(!).

Obviamente, neste caso, um memset() seria mais rápido mas o ponto é que este conceito pode ser estendido para casos em que o memset() não serve. Ainda há espaço para mais otimizações, mas isto é deixado como exercício ;)

Obs.: lembre-se que o comprimento de long varia de acordo com a arquitetura e é prudente colocar trechos que dependem disso dentro de #ifdefs.

Leia também um artigo relacionado, sobre o aproveitamento da memória cache nesta dica do Eduardo.

Palavras-chave: OpenGL, Linux, multi-thread, drivers NVidia, crash, segmentation fault, segfault

Se seu programa explode em uma função OpenGL sem nenhum motivo aparente, verifique se a chamada está sendo feita desde a thread onde a OpenGL foi inicializada.

As bibliotecas que vêm com os drivers da NVidia (libGL.so) para X não são muito tolerantes com programas com multi-threading, enquanto a libGL.so da Mesa parece ser mais tolerante a isso. Isso pode levar à confusa situação de o programa rodar sem problemas em alguns sistemas e morrer em outros.

Nos casos em que tive esse problema, o segfault ocorreu na função glViewport(), mas é provável que ocorra com outras funções.

Não existe solucão mágica, mas um workaround — para ao menos testar a hipótese — é mudar o LD_LIBRARY_PATH para apontar ao libGL.so da xorg (se não a tem instalado, tente copiar de outro lugar etc). Verifique se a biblioteca correta está sendo usada com ldd e teste. Já a solucão definitiva é mover as chamadas OpenGL que acessam o mesmo contexto para uma única thread (ou protegê-las com mutexes).

Palavras-chave: C, C++, CPP, macro, string, identificador

Para transformar o nome de um macro ou identificador em string:

#define STR(s)  #s

Para transformar o valor de um macro ou identificador em string:

#define STRV(s)  STR(s)

O seguinte exemplo ilustra a diferença:

#define STRV(s) STR(s)
#define STR(s) #s

int main()
{
  puts("1: "STR(puts));
  puts("2: "STR(123));
  puts("3: "STR(__LINE__));
  puts("4: "STRV(__LINE__));
  return 0;
}

Resultado:

1: puts
2: 123
3: __LINE__
4: 9

Note como STR() retorna um string com o nome do macro, enquanto que STRV() retorna o valor.

Add to Technorati Favorites

Assuntos