sexta-feira, 12 de dezembro de 2014
Videos Megaman - WIP
Alguns vídeos do jogo enquanto estou fazendo só para acompanhar andamento e evolução. Trata-se de um clone, o mais fiel possível(meu possível inclui minha burrice e também preguiça =D) do Megaman 1 do NES. Feito em C++ e Allegro, testo no windows 7 e no linux (ubuntu), mas deve rodar em qualquer lugar que suporte a lib Allegro.
quarta-feira, 27 de novembro de 2013
Cmd - Autorun reg key.
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\Autorun
Executa o comando em Autorun sempre que o cmd sobe. A menos que /d seja passado como parametro para o cmd.
quinta-feira, 15 de setembro de 2011
Um jeito diferente de indexar arrays - não façam isso em casa!
O que você diria se encontrasse a seguinte linha de código no meio de um grande sistema que vc está trabalhando:
3[array] = x;
Alguns vão pensar que se trata de um erro de digitação bem estranho e que o modulo não está compilando, ai vão checar o Makefile, depois vão checar se não existe algum cache de código ativo, depois vão checar o repositório e começar a culpar o CVS, os defensores do GIT vão mostrar como CVS é uma coisa capenga... todos vão checar forums e depois de uma flamewar saudável vão começar a culpar o compilador, o SO, os ETs e vão acabar consultando um padre novo e um padre velho... A questão é que esta linha é válida e funcional! A sintaxe usada para indexar arrays em C é uma "maquiagem" que esconde a aritmética de ponteiros que acontece. A nível de código assembly, ponteiros e inteiros são praticamente a mesma coisa. Sendo assim a[b] diz algo como: some a com b, o resultado deve ser visto como um endereço de memória. Se a expressão estiver do lado esquerdo então é para escrever neste local de memória, se estiver do lado direito deve ler desse endereço, ou seja, deve desreferenciar o resultado como se fosse um ponteiro. Essa expressão será vista como, *(a + b) ou *(b + a). Como a adição é comutativa, a + b == b + a, daí a[b] == b[a]. A unica restrição sintatica nesse caso é: a ou b deve ser um ponteiro e o outro um inteiro. Isso funciona mesmo se um dos operandos é uma constante inteira, como o 3[array]. É este mesmo mecanismo que nos permite usar índices negativos em um array sem nenhum problema, veja:
int main()
{
int * p;
int a[10];
int i,j;
a[4]=42;
a[5]=4;
p = &a[5];
i = *p;
j = p[-1];
printf("%d %d\n",i,j);
return 0;
}
Este código imprime "4 42". Note que p[-1] vai se transformar em *(p+(-1)) -> *(p-1). Lembre que *(p+3) não significa, necessáriamente, que o resultado é o byte de endereço P+3, o operando que não é ponteiro, no caso 3, vai ser escalado pelo tamanho do tipo que p aponta, então se p aponta para um int a conta, para criar o código asm, é (endereço(p) + (3*sizeof(int)). Só não tente isso no trabalho ;) Mas se for tentar, aproveite e use com arrays de mais de uma dimenção:
int main()
{
int i;
int a[2][2];
int x,y;
x = 0;
y = !x;
a[0][1]=42;
i = y[x[a]];
printf("%d\n",i);
return 0;
}
WhuHAUWhauwhauwhauw Se for fazer, faça direito, ahm?!?! Cya!!!
3[array] = x;
Alguns vão pensar que se trata de um erro de digitação bem estranho e que o modulo não está compilando, ai vão checar o Makefile, depois vão checar se não existe algum cache de código ativo, depois vão checar o repositório e começar a culpar o CVS, os defensores do GIT vão mostrar como CVS é uma coisa capenga... todos vão checar forums e depois de uma flamewar saudável vão começar a culpar o compilador, o SO, os ETs e vão acabar consultando um padre novo e um padre velho... A questão é que esta linha é válida e funcional! A sintaxe usada para indexar arrays em C é uma "maquiagem" que esconde a aritmética de ponteiros que acontece. A nível de código assembly, ponteiros e inteiros são praticamente a mesma coisa. Sendo assim a[b] diz algo como: some a com b, o resultado deve ser visto como um endereço de memória. Se a expressão estiver do lado esquerdo então é para escrever neste local de memória, se estiver do lado direito deve ler desse endereço, ou seja, deve desreferenciar o resultado como se fosse um ponteiro. Essa expressão será vista como, *(a + b) ou *(b + a). Como a adição é comutativa, a + b == b + a, daí a[b] == b[a]. A unica restrição sintatica nesse caso é: a ou b deve ser um ponteiro e o outro um inteiro. Isso funciona mesmo se um dos operandos é uma constante inteira, como o 3[array]. É este mesmo mecanismo que nos permite usar índices negativos em um array sem nenhum problema, veja:
int main()
{
int * p;
int a[10];
int i,j;
a[4]=42;
a[5]=4;
p = &a[5];
i = *p;
j = p[-1];
printf("%d %d\n",i,j);
return 0;
}
Este código imprime "4 42". Note que p[-1] vai se transformar em *(p+(-1)) -> *(p-1). Lembre que *(p+3) não significa, necessáriamente, que o resultado é o byte de endereço P+3, o operando que não é ponteiro, no caso 3, vai ser escalado pelo tamanho do tipo que p aponta, então se p aponta para um int a conta, para criar o código asm, é (endereço(p) + (3*sizeof(int)). Só não tente isso no trabalho ;) Mas se for tentar, aproveite e use com arrays de mais de uma dimenção:
int main()
{
int i;
int a[2][2];
int x,y;
x = 0;
y = !x;
a[0][1]=42;
i = y[x[a]];
printf("%d\n",i);
return 0;
}
WhuHAUWhauwhauwhauw Se for fazer, faça direito, ahm?!?! Cya!!!
terça-feira, 13 de setembro de 2011
A instrução LEA do x86
Um tempo atrás um doido aqui do trampo resolveu "melhorar" um pedaço do código escrevendo ele todo em assembly inline dentro dos modulos em c++ =D para completar, usou métodos pouco convencionais para executar algumas operações :) Isso rendeu muito papo para almoço e ele ganhou fama de ser maluco, se bem q ele já tinha essa fama :) Uma dos pulos do gato que ele implementou foi usar a instrução "lea" do x86 para multiplicar valores. Normalmente o manual da intel diz q LEA significa "load effective address", um pouco distante do famoso "MUL" :) Mas, devido a uma particularidade do modo de endereçamento dos x86, lea pode ser usada para muitas coisas alem de simplesmente funcionar como uma instrução para lidar com ponteiros. O assembly do x86 foi criado para "suportar" linguagens de alto-nível como C e Pascal de forma pouco dolorosa para os compiladores e nessas linguagens arrays de inteiros e estruturas pequenas são muito comuns. Vamos ver pra que LEA é utilizada "normalmente" :) Suponha q temos uma estrutura que guarda um valor de cor (RGB ou HSV):
struct Color
{
int rgb;
int hsv;
};
Para recuperar o valor do campo HSV escreveríamos algo como:
int hsvValue = colors[i].hsv;
Aqui colors[] é um array do tipo Color. Supondo que o compilador coloque o endereço base do array em EBX, e que a variavel i esteja em EAX, e que (rgb,hsv) são valores de 32 bits de forma que hsv esteja no offset 4 da estrutura (base+4), então a tradução para assembly poderia tomar a seguinte forma:
INTEL: MOV EDX, [EBX + 8*EAX + 4]
AT&T: mov 4(%ebx,%eax,8), %edx
Isso vai colocar o valor de "hsv" no registrador EDX. O valor de escala 8 é usado pq cada variável do tipo Color tem 8bytes, ou seja, soma o endereço base do array ao valor de "i" multiplicado pelo tamanho da estrutura então adiciona 4 que é o offset de "hsv". Mas é se quissemos o endereço de "hsv"? Vamos analisar esta expressão usando o operador & para recuperar o endereço de hsv:
int * pColor = &color[i].hsv;
Neste caso não queremos o conteúdo contido no endereço de memória, queremos o endereço de memória em si. Neste caso vamos usar a instrução LEA:
LEA ESI, [EBX + 8*EAX + 4]
Pq usar LEA e não MOV? Pq neste caso se você trocar por MOV o que vai acontecer é: o cpu vai efetuar o calculo EBX+8*EAX+4 e vai encontrar um valor, este valor será encarado como um endereço de memória então o cpu vai neste endereço buscar o conteúdo que está armazenado lá. O que claramente não é nossa intenção. Podemos dizer que LEA é uma instrução que executa cálculos envolvendo memória, mas sem acessar estes endereços. De certa forma, é mais uma operação de soma e shift só que com um nome estranho :) Até daria para utilizar MOV, veja esse programinha em C:
#include
int main()
{
int * p;
int a = 2;
p = &a;
printf("%d\n",*p);
return 0;
}
Se olharmos o código assembly gerado pelo compilador(no meu caso o gcc) o código será parecido com:
main:
// Setup da pilha
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp) // Move 2 para a região da pilha onde está o 'a'
leal 24(%esp), %eax // Carrega eax com o endereço de 'a'
movl %eax, 28(%esp) // Move para onde fica 'p'
movl 28(%esp), %eax // Move o endereço de 'a'(valor de 'p') para eax
movl (%eax), %edx // Carrega edx com o valor de 'a'
// Setup para chamar printf
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Obvio que o código não é otimizado, mas veja que ele usa lea para carregar o endereço de 'a' em 'p', poderíamos ter chegado ao mesmo resultado usando MOV, da seguinte forma:
main:
// Setup da pilha
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp) // Move 2 para a região da pilha onde está o 'a'
// Faz o que o LEA faz usando um MOV e um ADD
movl %esp, %eax
addl $24, %eax
// O resto é igual! :)
movl %eax, 28(%esp)
movl 28(%esp), %eax
movl (%eax), %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Dá para notar que usamos 2 instruções ao invés de uma, não muito bom :) E esse é o principal sentido de usar LEA, poupar cálculos e resolver o endereço de memória que queremos e depois usar, num loop, por exemplo, onde seria custoso ficar calculando offsets o tempo todo. Se você mudar o LEA para MOV no primeiro caso provavelmente vai quebrar o programa, pq MOV vai tentar acessar o endereço calculado para recuperar o valor. Um outro exemplo é como recuperar o valor de um ponteiro, vejamos, dado o seguinte programinha em C:
#include
int main()
{
int * p;
int a = 2;
p = &a;
printf("%d\n",a);
return 0;
}
Esse programa mostra 2 na saída padrão. O código assembly gerado pelo compilador(Gcc) será parecido com:
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp)
leal 24(%esp), %eax
movl %eax, 28(%esp)
movl 24(%esp), %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Vamos supor que alguem apagou nosso código em C e vc, por alguma razão maluca qualquer, tem que trocar a linha printf("%d\n",a); para printf("%d\n",*p); fariamos o seguinte:
Já carregamos o endereço de 'a' em 'p' com a instrução movl %eax, 28(%esp). Podemos notar que antes de colocar o valor dos parâmetros do printf na pilha o compilador moveu 'a' para edx e o endereço da string para eax. O que vamos fazer é trocar a instrução movl 24(%esp), %edx por:
movl 28(%esp), %edx
movl (%edx), %edx
Já que 'p' está no endereço (esp+28) nós temos que reaver o que ele contem (o endereço de 'a') e fazemos isso com o movl 28(%esp), %edx ai temos que acessar esse ponteiro movl (%edx), %edx e armazenar em edx o valor contido no endereço que estava armazenado em 'p' (sendo q 'p' contem o endereço de 'a'). Agora que já vimos bastante o LEA, vamos traduzir algumas utilizações do LEA para português:
leal (%ebx), %eax
Esta instrução copia o endereço da expressão (%ebx) para eax. Como o "endereço efetivo" é o valor de ebx, essa instrução vai copiar ebx em eax como se fosse um MOV. Nada muito útil :)
leal 3(%ebx), %ebx
Copia o valor de ebx + 3 para ebx. Não muito útil já que um ADD faria o mesmo, possivelmente mais rápido.
leal 3(%ebx), %eax
Aqui temos uma utilização mais sensata de LEA, ela vai copiar o valor de ebx somado a 3 em eax. Isso significa que fizemos um MOV + ADD em uma única instrução! Ok, e o que LEA tem com MUL!?!? Se usarmos a capacidade de LEA operar cálculos com endereços sem acessar a memória podemos usar isso para multiplicar por 2,4,8 ou até por 3,5,9, ou até executar essa multiplicação e somar um inteiro. Usando LEA para multiplicar:
leal (%eax,%eax,2), %eax // EAX = EAX * 2 + EAX = EAX*3
leal (%eax,%eax,4), %eax // EAX = EAX * 4 + EAX = EAX*5
leal (%eax,%eax,8), %eax // EAX = EAX * 8 + EAX = EAX*9
Outros exemplo:
leal 8(,%eax,4), %eax // Multiplica eax por 4 e adiciona 8
No Asm AT&T a sintaxe displacement(base register, offset register, scalar multiplier) é equivalente ao [base register + displacement + offset register * scalar multiplier] na sintaxe da Intel. Um ou ambos os parâmetros numéricos e um dos registradores podem ser omitidos. Sendo que o fator de escala deve ser 1,2,4 ou 8. Ok! Viram! Com LEA vc pode realizar alguns calculos (e sem modificar os flags! isso consta na documentação da Intel). Se você consultar qualquer guia de otimização de código (principalmente para guiar compiladores) verá que LEA pode fornecer métodos bem úteis de realizar operações matemáticas! É isso pessoal! Meu parecer é esse tal gato que meu brother fez não é tanto um gato, é muito mais uma técnica muito boa!!!!
struct Color
{
int rgb;
int hsv;
};
Para recuperar o valor do campo HSV escreveríamos algo como:
int hsvValue = colors[i].hsv;
Aqui colors[] é um array do tipo Color. Supondo que o compilador coloque o endereço base do array em EBX, e que a variavel i esteja em EAX, e que (rgb,hsv) são valores de 32 bits de forma que hsv esteja no offset 4 da estrutura (base+4), então a tradução para assembly poderia tomar a seguinte forma:
INTEL: MOV EDX, [EBX + 8*EAX + 4]
AT&T: mov 4(%ebx,%eax,8), %edx
Isso vai colocar o valor de "hsv" no registrador EDX. O valor de escala 8 é usado pq cada variável do tipo Color tem 8bytes, ou seja, soma o endereço base do array ao valor de "i" multiplicado pelo tamanho da estrutura então adiciona 4 que é o offset de "hsv". Mas é se quissemos o endereço de "hsv"? Vamos analisar esta expressão usando o operador & para recuperar o endereço de hsv:
int * pColor = &color[i].hsv;
Neste caso não queremos o conteúdo contido no endereço de memória, queremos o endereço de memória em si. Neste caso vamos usar a instrução LEA:
LEA ESI, [EBX + 8*EAX + 4]
Pq usar LEA e não MOV? Pq neste caso se você trocar por MOV o que vai acontecer é: o cpu vai efetuar o calculo EBX+8*EAX+4 e vai encontrar um valor, este valor será encarado como um endereço de memória então o cpu vai neste endereço buscar o conteúdo que está armazenado lá. O que claramente não é nossa intenção. Podemos dizer que LEA é uma instrução que executa cálculos envolvendo memória, mas sem acessar estes endereços. De certa forma, é mais uma operação de soma e shift só que com um nome estranho :) Até daria para utilizar MOV, veja esse programinha em C:
#include
int main()
{
int * p;
int a = 2;
p = &a;
printf("%d\n",*p);
return 0;
}
Se olharmos o código assembly gerado pelo compilador(no meu caso o gcc) o código será parecido com:
main:
// Setup da pilha
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp) // Move 2 para a região da pilha onde está o 'a'
leal 24(%esp), %eax // Carrega eax com o endereço de 'a'
movl %eax, 28(%esp) // Move para onde fica 'p'
movl 28(%esp), %eax // Move o endereço de 'a'(valor de 'p') para eax
movl (%eax), %edx // Carrega edx com o valor de 'a'
// Setup para chamar printf
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Obvio que o código não é otimizado, mas veja que ele usa lea para carregar o endereço de 'a' em 'p', poderíamos ter chegado ao mesmo resultado usando MOV, da seguinte forma:
main:
// Setup da pilha
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp) // Move 2 para a região da pilha onde está o 'a'
// Faz o que o LEA faz usando um MOV e um ADD
movl %esp, %eax
addl $24, %eax
// O resto é igual! :)
movl %eax, 28(%esp)
movl 28(%esp), %eax
movl (%eax), %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Dá para notar que usamos 2 instruções ao invés de uma, não muito bom :) E esse é o principal sentido de usar LEA, poupar cálculos e resolver o endereço de memória que queremos e depois usar, num loop, por exemplo, onde seria custoso ficar calculando offsets o tempo todo. Se você mudar o LEA para MOV no primeiro caso provavelmente vai quebrar o programa, pq MOV vai tentar acessar o endereço calculado para recuperar o valor. Um outro exemplo é como recuperar o valor de um ponteiro, vejamos, dado o seguinte programinha em C:
#include
int main()
{
int * p;
int a = 2;
p = &a;
printf("%d\n",a);
return 0;
}
Esse programa mostra 2 na saída padrão. O código assembly gerado pelo compilador(Gcc) será parecido com:
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $2, 24(%esp)
leal 24(%esp), %eax
movl %eax, 28(%esp)
movl 24(%esp), %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Vamos supor que alguem apagou nosso código em C e vc, por alguma razão maluca qualquer, tem que trocar a linha printf("%d\n",a); para printf("%d\n",*p); fariamos o seguinte:
Já carregamos o endereço de 'a' em 'p' com a instrução movl %eax, 28(%esp). Podemos notar que antes de colocar o valor dos parâmetros do printf na pilha o compilador moveu 'a' para edx e o endereço da string para eax. O que vamos fazer é trocar a instrução movl 24(%esp), %edx por:
movl 28(%esp), %edx
movl (%edx), %edx
Já que 'p' está no endereço (esp+28) nós temos que reaver o que ele contem (o endereço de 'a') e fazemos isso com o movl 28(%esp), %edx ai temos que acessar esse ponteiro movl (%edx), %edx e armazenar em edx o valor contido no endereço que estava armazenado em 'p' (sendo q 'p' contem o endereço de 'a'). Agora que já vimos bastante o LEA, vamos traduzir algumas utilizações do LEA para português:
leal (%ebx), %eax
Esta instrução copia o endereço da expressão (%ebx) para eax. Como o "endereço efetivo" é o valor de ebx, essa instrução vai copiar ebx em eax como se fosse um MOV. Nada muito útil :)
leal 3(%ebx), %ebx
Copia o valor de ebx + 3 para ebx. Não muito útil já que um ADD faria o mesmo, possivelmente mais rápido.
leal 3(%ebx), %eax
Aqui temos uma utilização mais sensata de LEA, ela vai copiar o valor de ebx somado a 3 em eax. Isso significa que fizemos um MOV + ADD em uma única instrução! Ok, e o que LEA tem com MUL!?!? Se usarmos a capacidade de LEA operar cálculos com endereços sem acessar a memória podemos usar isso para multiplicar por 2,4,8 ou até por 3,5,9, ou até executar essa multiplicação e somar um inteiro. Usando LEA para multiplicar:
leal (%eax,%eax,2), %eax // EAX = EAX * 2 + EAX = EAX*3
leal (%eax,%eax,4), %eax // EAX = EAX * 4 + EAX = EAX*5
leal (%eax,%eax,8), %eax // EAX = EAX * 8 + EAX = EAX*9
Outros exemplo:
leal 8(,%eax,4), %eax // Multiplica eax por 4 e adiciona 8
No Asm AT&T a sintaxe displacement(base register, offset register, scalar multiplier) é equivalente ao [base register + displacement + offset register * scalar multiplier] na sintaxe da Intel. Um ou ambos os parâmetros numéricos e um dos registradores podem ser omitidos. Sendo que o fator de escala deve ser 1,2,4 ou 8. Ok! Viram! Com LEA vc pode realizar alguns calculos (e sem modificar os flags! isso consta na documentação da Intel). Se você consultar qualquer guia de otimização de código (principalmente para guiar compiladores) verá que LEA pode fornecer métodos bem úteis de realizar operações matemáticas! É isso pessoal! Meu parecer é esse tal gato que meu brother fez não é tanto um gato, é muito mais uma técnica muito boa!!!!
quinta-feira, 3 de fevereiro de 2011
'\n' ou std::endl - Qual newline usar? (C++)
Seja por economia de espaço ou por pura preguiçite aguda é comum que uma boa parte dos programadores escolha usar '\n' para quebrar um linha, principalmente para logar informações de debug no console. No entanto, qual é a diferença? Bem a diferença principal é que o std::endl faz um flush no buffer (para streams que suportam buffer) então se você não quer que o buffer receba um flush com muita frequencia é melhor usar o \n, mas se você quer evitar o flush automatico do buffer e deseja ter um controle melhor de quando o buffer será comitado use std::endl.
De um ponto de vista simplista:
std::cout << std::endl;
// é equivalente a
std::cout << "\n" << std::flush;
Claro que quem estiver usando o operador '<<' deve saber que operações de I/O, mesmo bufferizadas, são operações lentas. Então não leve o assunto performace TÃO a sério nesse cenário. Em contra partida lembre que o flush pode criar algumas chamadas a mais e se o seu código é uma classe de log em arquivo no disco, por exemplo, então você pode ter uma perda de performace SIM. Vale lembrar que isso acontece pois, no geral, escrever um bloco de bytes é mais economico do que escrever varios bytes um de cada vez, os dispositivos são desenhados para efetuar operações com multiplos bytes então aproveite essa feature! E lembre-se que no geral, para arquivos abertos em modo texto, o newline será mapeado para o caracter correto de fim de linha(LF em Unix, CRLF no DOS(Windows), CR no Macintosh, e por ai vai), vale lembrar que cout e cin são arquivos especiais, ou uma especie de arquivo(se você prefere assim) e também vão se comportar como tal. Para não manter o artigo tão superficial vamos olhar a implementação do endl na libstdc++ da GNU:
/**
* @brief Write a newline and flush the stream.
*
* This manipulator is often mistakenly used when a simple newline is
* desired, leading to poor buffering performance. See
* http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#2 for more
* on this subject.
*/
template
basic_ostream<_CharT, _Traits>& endl(basic_ostream<_CharT, _Traits>& __os)
{
return flush(__os.put(__os.widen('\n')));
}
Veja que a documentação é bem clara quanto ao comportamento do endl, mas olhando o código fica claro o que acontece. O endl recebe o stream que vai operar como parâmetro e chama o put para inserir um '\n'. O retorno do put será enviado para o flush causando, obviamente, um flush na stream.
That's all!!!
De um ponto de vista simplista:
std::cout << std::endl;
// é equivalente a
std::cout << "\n" << std::flush;
Claro que quem estiver usando o operador '<<' deve saber que operações de I/O, mesmo bufferizadas, são operações lentas. Então não leve o assunto performace TÃO a sério nesse cenário. Em contra partida lembre que o flush pode criar algumas chamadas a mais e se o seu código é uma classe de log em arquivo no disco, por exemplo, então você pode ter uma perda de performace SIM. Vale lembrar que isso acontece pois, no geral, escrever um bloco de bytes é mais economico do que escrever varios bytes um de cada vez, os dispositivos são desenhados para efetuar operações com multiplos bytes então aproveite essa feature! E lembre-se que no geral, para arquivos abertos em modo texto, o newline será mapeado para o caracter correto de fim de linha(LF em Unix, CRLF no DOS(Windows), CR no Macintosh, e por ai vai), vale lembrar que cout e cin são arquivos especiais, ou uma especie de arquivo(se você prefere assim) e também vão se comportar como tal. Para não manter o artigo tão superficial vamos olhar a implementação do endl na libstdc++ da GNU:
/**
* @brief Write a newline and flush the stream.
*
* This manipulator is often mistakenly used when a simple newline is
* desired, leading to poor buffering performance. See
* http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#2 for more
* on this subject.
*/
template
basic_ostream<_CharT, _Traits>& endl(basic_ostream<_CharT, _Traits>& __os)
{
return flush(__os.put(__os.widen('\n')));
}
Veja que a documentação é bem clara quanto ao comportamento do endl, mas olhando o código fica claro o que acontece. O endl recebe o stream que vai operar como parâmetro e chama o put para inserir um '\n'. O retorno do put será enviado para o flush causando, obviamente, um flush na stream.
That's all!!!
terça-feira, 2 de março de 2010
Ponteiros para funções – C
Ponteiros são os grandes bichos-papões de quem está querendo aprender C, e ponteiro para função é o über monstro para a maioria dos programadores novatos. Mas, basta um pouco de paciência e um olhar destemido para perceber que não existe nada de tão assustador sobre eles. Assumindo que você conhece ponteiros vou me concentrar no tópico ponteiro para funções. É importante saber distinguir uma declaração de função de um ponteiro para função. Nos exemplos abaixo:
- int * func();
- int (*func)();
O primeiro caso define uma função que retorna um ponteiro para um inteiro, já o segundo caso é um ponteiro para uma função que retorna int. Lembre-se ( ) tem precedência ao * logo o primeiro caso será avaliado como uma função e o segundo como um ponteiro (*func) para uma função int (). Ponteiros para função não são somente um jeito complicado de programar, são extremamente úteis, pois podem substituir longas cadeias de if/else ou um switch gigante, são úteis para criar callbacks, e, de certa forma, sem eles não existiria como implementar mecanismos de ligação dinâmica de funções como DLL's já que você solicita um nome na tabela de símbolos do dll, e a partir de um ponteiro de função você consegue chamar a função cujo código foi carregado na memória possivelmente após a imagem do seu processo ter sido carregado. Um ponteiro para função funciona pois, no final das contas, o código das funções também estão na memória e se o controle da cpu for transferido para esse endereço o código será executado, claro que o endereço tem que ser um endereço aonde seu processo tem privilégios de execução. Para declarar um ponteiro para uma função utilizo mentalmente um jeito bem simples, e que funciona bem na maioria dos casos. Esse método consiste em escrever como se você estivesse declarando a função, por exemplo:
int f(int a);
Então coloque parênteses ao redor do nome da função e um asterisco na frente do nome:
int (*f)(int a);
Note que não é necessário colocar o nome da variável que é recebida como parâmetro, mas talvez seja uma boa para fim de entendimento do código. Ok, agora vamos continuar a brincadeira e pensar grande, quem sabe um ponteiro para uma função que receba um inteiro retorne um ponteiro para um float? Simples, primeiro escreva como se estivesse declarando:
float * f(int i);
Depois, faça a jogada que aprendemos anteriormente:
float * (*f)(int i);
Pronto! Viu como a coisa nem é tão complicada assim! Antes de demonstrar aspectos práticos vamos declarar mais uns ponteiros para funções que pareceriam intragáveis antes. Digamos, um ponteiro para uma função que retorna um array de ponteiros para funções que retornam um ponteiro void e não recebem parâmetros. Somando a isso, a função recebe como parâmetro dois inteiros e um ponteiro para int, hehehe bem até a declaração disso é meio bizarro, mas vamos lá:
void * (**f(int a, int b, int * pi))(void)
Ok, isso merece uma explicação hehehe, f é o nome da função, ela recebe dois inteiros e um ponteiro para inteiro, o retorno dela é um ponteiro para ponteiro **f e esse ponteiro é para um array de ponteiros para função do tipo void * (void), ou seja, que recebe void e retorna um ponteiro void. Como é mais comum retornar o endereço do primeiro item do array, então temos nossa função declarada. Criar um ponteiro, neste caso consiste em:
void * (**(*fp)(int a,int b,int* pi)) (void);
Lindo não?! Removendo o nome dos parâmetros fica ainda mais bonito:
void * (**(*fp)(int,int,int*)) (void);
Mas, acredite, é bem pouco provável que você tenha que criar uma loucura dessas, o propósito do meu exemplo é só para demonstrar que até as funções mais loucas podem ter um ponteiro apontando pra elas sem muito trabalho, pelo menos não é tão absurdo como dizem. Que tal um exemplo disso em funcionamento hehehe:
#include <stdio.h>
void * fvoid1(void)
{
fprintf(stderr,"fvoid1\n");
return NULL;
}
void * fvoid2(void)
{
fprintf(stderr,"fvoid2\n");
return NULL;
}
void * (*farray[]) () = {fvoid1,fvoid2};
void * (**f(int a, int b, int * pi))(void)
{
void *(**fp)() = &farray[0];
return fp;
}
int main()
{
void * (**(*fp)(int,int,int*)) (void);
void * (**a)(void);
fp = f;
a = (*fp)(10,20,NULL);
(*a)();
a++;
(*a)();
return 0;
}
Atribuir o endereço de uma função para um ponteiro de função segue o mesmo padrão do resto das coisas em C, digo, se você criar um ponteiro de função que aponte para uma função cuja definição combine com a definição da função printf, pro exemplo, atribuir o endereço é tão simples quanto:
pf = printf;
Ou você pode utilizar a forma mais “canônica” e utilizar o operador & para recuperar o endereço da função. Vale lembrar que alguns compiladores vão reclamar da forma direta, então já que o jeito certo é o canônico, utilize o jeito certo! Como em:
pf = &printf;
Para chamar, você tem duas opções, você pode utilizar a sintaxe:
pf(“Alo mundo”);
Desta forma o compilador vai desreferenciar o ponteiro para você, ou você pode fazer na mão:
(*pf)(“Alo mundo”);
Não tenho nenhum argumento contra ou a favor a utilização de um método ou do outro, no máximo eu diria que desreferenciar na mão deixa um pouco mais claro que você está utilizando um ponteiro e não chamando uma função chamada pf.
Agora vamos tratar os assuntos mais comuns relativos ao uso de ponteiros para função. Evitar uma cadeia de if/else gigante ou um switch e funções que são passadas para funções*, de certo modo, para implementar uma técnica conhecida como callback. Pretendo matar dois coelhos com uma cajadada só, e explicar ambos no mesmo exemplo. O problema com as cadeias de if/else ou switch, geralmente, surge quando é necessário fazer a chamada para uma função apropriada para algum caso especifico quando a situação não pode ser determinada em tempo de compilação, ou seja, não há como saber antes de executar qual função deve ser chamada. Vou utilizar o caso mais clássico que é o das quatro operações básicas (+/-/*/div), o código utilizando um switch seria parecido com:
Utilizando uma função que utiliza ponteiros de função como parâmetro, ou seja, recebe uma função como parâmetro, ficaria parecido com:
E obvio que este caso é um caso trivial e não existe necessidade real de utilizar um ponteiro para função. A coisa muda de figura em casos aonde algum trabalho extra é pré-requisito para o funcionamento das operações, imagine receber os operandos via RPC ou que seja necessário converter a entrada do usuário em uma GUI para valores float a fim de obter os operandos, ou qualquer coisa do tipo. O código para executar esta tarefa não teria que ser replicado em todos os casos do switch bastaria codificar uma vez e baseado na necessidade chamar passando o ponteiro para a função correta. Essa técnica de passar uma função para outra é chamada de callback, e a função sendo chamada geralmente recebe o nome de callback function.
Normalmente callbacks são utilizadas quando é necessário que duas camadas diferentes de um sistema se comuniquem como, por exemplo, você passa uma função para o SO de forma que, quando um evento qualquer ocorrer o sistema chame essa função pra você.
Note que fiz um apontamento * quando falei sobre passar uma função para outra, no caso de C/C++ não é isso que ocorre de fato. Em linguagens imperativas como é o caso de C/C++, normalmente não permitem tal operação o que estamos fazendo é meramente passar o endereço de onde a função está na memória. Funções que recebem outra função como parâmetro, e de fato implementam funções de alta ordem gerais só são possíveis em linguagens funcionais e não vou tratar disso neste tutorial e possivelmente não neste blog heheheh. Então é isso, da próxima vez que pensar em ponteiros para função ao invés de medo, sorria, pois é um recurso muito poderoso da linguagem e que não é tão complicado quanto dizem por ai!
That's all folks!
- int * func();
- int (*func)();
O primeiro caso define uma função que retorna um ponteiro para um inteiro, já o segundo caso é um ponteiro para uma função que retorna int. Lembre-se ( ) tem precedência ao * logo o primeiro caso será avaliado como uma função e o segundo como um ponteiro (*func) para uma função int (). Ponteiros para função não são somente um jeito complicado de programar, são extremamente úteis, pois podem substituir longas cadeias de if/else ou um switch gigante, são úteis para criar callbacks, e, de certa forma, sem eles não existiria como implementar mecanismos de ligação dinâmica de funções como DLL's já que você solicita um nome na tabela de símbolos do dll, e a partir de um ponteiro de função você consegue chamar a função cujo código foi carregado na memória possivelmente após a imagem do seu processo ter sido carregado. Um ponteiro para função funciona pois, no final das contas, o código das funções também estão na memória e se o controle da cpu for transferido para esse endereço o código será executado, claro que o endereço tem que ser um endereço aonde seu processo tem privilégios de execução. Para declarar um ponteiro para uma função utilizo mentalmente um jeito bem simples, e que funciona bem na maioria dos casos. Esse método consiste em escrever como se você estivesse declarando a função, por exemplo:
int f(int a);
Então coloque parênteses ao redor do nome da função e um asterisco na frente do nome:
int (*f)(int a);
Note que não é necessário colocar o nome da variável que é recebida como parâmetro, mas talvez seja uma boa para fim de entendimento do código. Ok, agora vamos continuar a brincadeira e pensar grande, quem sabe um ponteiro para uma função que receba um inteiro retorne um ponteiro para um float? Simples, primeiro escreva como se estivesse declarando:
float * f(int i);
Depois, faça a jogada que aprendemos anteriormente:
float * (*f)(int i);
Pronto! Viu como a coisa nem é tão complicada assim! Antes de demonstrar aspectos práticos vamos declarar mais uns ponteiros para funções que pareceriam intragáveis antes. Digamos, um ponteiro para uma função que retorna um array de ponteiros para funções que retornam um ponteiro void e não recebem parâmetros. Somando a isso, a função recebe como parâmetro dois inteiros e um ponteiro para int, hehehe bem até a declaração disso é meio bizarro, mas vamos lá:
void * (**f(int a, int b, int * pi))(void)
Ok, isso merece uma explicação hehehe, f é o nome da função, ela recebe dois inteiros e um ponteiro para inteiro, o retorno dela é um ponteiro para ponteiro **f e esse ponteiro é para um array de ponteiros para função do tipo void * (void), ou seja, que recebe void e retorna um ponteiro void. Como é mais comum retornar o endereço do primeiro item do array, então temos nossa função declarada. Criar um ponteiro, neste caso consiste em:
void * (**(*fp)(int a,int b,int* pi)) (void);
Lindo não?! Removendo o nome dos parâmetros fica ainda mais bonito:
void * (**(*fp)(int,int,int*)) (void);
Mas, acredite, é bem pouco provável que você tenha que criar uma loucura dessas, o propósito do meu exemplo é só para demonstrar que até as funções mais loucas podem ter um ponteiro apontando pra elas sem muito trabalho, pelo menos não é tão absurdo como dizem. Que tal um exemplo disso em funcionamento hehehe:
#include <stdio.h>
void * fvoid1(void)
{
fprintf(stderr,"fvoid1\n");
return NULL;
}
void * fvoid2(void)
{
fprintf(stderr,"fvoid2\n");
return NULL;
}
void * (*farray[]) () = {fvoid1,fvoid2};
void * (**f(int a, int b, int * pi))(void)
{
void *(**fp)() = &farray[0];
return fp;
}
int main()
{
void * (**(*fp)(int,int,int*)) (void);
void * (**a)(void);
fp = f;
a = (*fp)(10,20,NULL);
(*a)();
a++;
(*a)();
return 0;
}
Atribuir o endereço de uma função para um ponteiro de função segue o mesmo padrão do resto das coisas em C, digo, se você criar um ponteiro de função que aponte para uma função cuja definição combine com a definição da função printf, pro exemplo, atribuir o endereço é tão simples quanto:
pf = printf;
Ou você pode utilizar a forma mais “canônica” e utilizar o operador & para recuperar o endereço da função. Vale lembrar que alguns compiladores vão reclamar da forma direta, então já que o jeito certo é o canônico, utilize o jeito certo! Como em:
pf = &printf;
Para chamar, você tem duas opções, você pode utilizar a sintaxe:
pf(“Alo mundo”);
Desta forma o compilador vai desreferenciar o ponteiro para você, ou você pode fazer na mão:
(*pf)(“Alo mundo”);
Não tenho nenhum argumento contra ou a favor a utilização de um método ou do outro, no máximo eu diria que desreferenciar na mão deixa um pouco mais claro que você está utilizando um ponteiro e não chamando uma função chamada pf.
Agora vamos tratar os assuntos mais comuns relativos ao uso de ponteiros para função. Evitar uma cadeia de if/else gigante ou um switch e funções que são passadas para funções*, de certo modo, para implementar uma técnica conhecida como callback. Pretendo matar dois coelhos com uma cajadada só, e explicar ambos no mesmo exemplo. O problema com as cadeias de if/else ou switch, geralmente, surge quando é necessário fazer a chamada para uma função apropriada para algum caso especifico quando a situação não pode ser determinada em tempo de compilação, ou seja, não há como saber antes de executar qual função deve ser chamada. Vou utilizar o caso mais clássico que é o das quatro operações básicas (+/-/*/div), o código utilizando um switch seria parecido com:
#include <stdio.h>
float Plus (float a, float b) { return a+b; }
float Minus (float a, float b) { return a-b; }
float Multiply (float a, float b) { return a*b; }
float Divide (float a, float b) { return a/b; }
void Solve(float oper1, float oper2, char opCode)
{
float result=0.0f;
switch(opCode)
{
case '+': result = Plus (oper1, oper2);break;
case '-': result = Minus (oper1, oper2);break;
case '*': result = Multiply(oper1, oper2);break;
case '/': result = Divide (oper1, oper2);break;
}
printf("Resultado: %f %c %f = %f\n",oper1,opCode,oper2,result);
}
int main()
{
Solve(2,3,'+');
Solve(7,3,'-');
Solve(2,7,'*');
Solve(25,5,'/');
getchar();
return 0;
}
Utilizando uma função que utiliza ponteiros de função como parâmetro, ou seja, recebe uma função como parâmetro, ficaria parecido com:
#include <stdio.h>
float Plus (float a, float b) { return a+b; }
float Minus (float a, float b) { return a-b; }
float Multiply (float a, float b) { return a*b; }
float Divide (float a, float b) { return a/b; }
float Solve(float oper1, float oper2, float (*f)(float,float))
{
return ((*f)(oper1,oper2));
}
int main()
{
printf("Resultado: 2 + 3 = %f\n",Solve(2,3,Plus));
printf("Resultado: 7 - 3 = %f\n",Solve(7,3,Minus));
printf("Resultado: 2 * 7 = %f\n",Solve(2,7,Multiply));
printf("Resultado: 25/5 = %f\n",Solve(25,5,Divide));
getchar();
return 0;
}
E obvio que este caso é um caso trivial e não existe necessidade real de utilizar um ponteiro para função. A coisa muda de figura em casos aonde algum trabalho extra é pré-requisito para o funcionamento das operações, imagine receber os operandos via RPC ou que seja necessário converter a entrada do usuário em uma GUI para valores float a fim de obter os operandos, ou qualquer coisa do tipo. O código para executar esta tarefa não teria que ser replicado em todos os casos do switch bastaria codificar uma vez e baseado na necessidade chamar passando o ponteiro para a função correta. Essa técnica de passar uma função para outra é chamada de callback, e a função sendo chamada geralmente recebe o nome de callback function.
Normalmente callbacks são utilizadas quando é necessário que duas camadas diferentes de um sistema se comuniquem como, por exemplo, você passa uma função para o SO de forma que, quando um evento qualquer ocorrer o sistema chame essa função pra você.
Note que fiz um apontamento * quando falei sobre passar uma função para outra, no caso de C/C++ não é isso que ocorre de fato. Em linguagens imperativas como é o caso de C/C++, normalmente não permitem tal operação o que estamos fazendo é meramente passar o endereço de onde a função está na memória. Funções que recebem outra função como parâmetro, e de fato implementam funções de alta ordem gerais só são possíveis em linguagens funcionais e não vou tratar disso neste tutorial e possivelmente não neste blog heheheh. Então é isso, da próxima vez que pensar em ponteiros para função ao invés de medo, sorria, pois é um recurso muito poderoso da linguagem e que não é tão complicado quanto dizem por ai!
That's all folks!
Assinar:
Postagens (Atom)