C/C++

Dois IDEs que gosto e dois que odeio

No princípio você descobre o Dev-C++ começa a programar e os bugs te atormentam. Não lembro quais são eles mas me fez procurar alternativas e encontrei o caso de amor:

Code::Blocks – O profissional

Feito para ser uma alternativa ao Dev-C++ já deixou de ser alternativa e se tornou regra. Evoluiu muito e tem cada vez mais agregado funcionalidades úteis.

Captura de tela de 2016-07-01 19-47-20 - 1.png

Como se pode ver na figura os Templates são variados e nos ultimos releases há uma busca pelo desenvolvimento embarcado se aproveitando das diversas plataformas suportadas pelo GCC e o SDCC.

O Code::Blocks suporta desde o começo muitos compiladores. No Windows o download já vem embutido o Mingw ou TDM-GCC.

Eu uso ele profissionalmente para desenvolvimento C/C++ para as plataformas ARM (STM32 ou Kinetis) e AVR bem como x86. Deu um pouco de trabalho a configuração inicial dele para STM32 mas foi gratificante e muito instrutivo.

Captura de tela de 2016-07-01 19-59-01.png

O que mais gosto nele é facilidade de configuração e início médio. Eu odeio programas lentos como se verá nos que odeio. Mas ainda há uma opção veloz …

Geany – O pequeno poderoso

Eu adoro o Geany. É o IDE perfeito para testes rápidos e desenvolvimentos descompromissados. Diferente do Code:Blocks ele suporta diversas linguagens e tenho usado muito ultimante para Python.

Apesar de ter a opção de criar projetos o que me atrai nele é justamente você poder criar um arquivo, digitar o código e botar para rodar diretamente sem maiores burocracias. Por isso é perfeito para testes.E ao fechá-lo e abri-lo posteriormente as abas estarão lá abertas novamente.  Na verdade um projeto é nada mais que organizar seus arquivos em pastas que são navegáveis no painel “Aquivos” ao lado. Se o projeto cresceu então junte tudo em uma pasta e trabalhe com ela.

Captura de tela de 2016-07-01 20-03-43.png

E o inicio é rapido!

Como eu gosto disto! Clicou abriu! E o terminal embaixo? Quebra um galhão. Com um suporte a debug  ele pode até substituir o Code::Blocks.

linux_dark_1-24_weblinux_dark_1-24

As caras são muitas ao Geany para todos os gostos. Acho bonito programar em escuro mas não consigo me acostumar.

Os que odeio

Eclipse – Lerdeza

Pelo amor de Deus … como demora esta coisa para iniciar. Fiquei injuriado quando a Freescale mudou sua IDE para variantes Eclipse. Clicava em debug e aquela coisa ficava pensando … pensando … indexando. Que horror. Por isso fiz um esforço e aprendi a configurar o Code::Blocks para ARMs.

Essa tendência de se fazer IDEs baseadas no Eclipse para mim foi um desastre. A Texas fez isso, a Freescale e a ST esta indo no caminho. Sim, sim sei que o Eclipse é muito versátil mas convenhamos muito lento.  Certeira foi a Atmel que partiu para uma solução Visual Studio.

plc2-2015-your-own-ide-4-638

E aquela burocracia para se criar um projeto???? O sujeito que programa para sistemas embarcados em Eclipse se aprender diretamente nele se torna dependente do mesmo; porque não terá a menor noção de como se programa para uma plataforma: que comandos, que bibliotecas, que caminhos, como usar os comandos de debug. Tudo isso fica escondido em trezentos cliques que da medo e preguiça de mexer.

Se você usa o Eclipse e não tem a menor idéia de como as coisas funcionam por favor veja neste post como se programa “na mão”  um STM32.

Descobri neste site http://www.ihateeclipse.com  que não sou o único!

Vim/VI – O Geek

Fui atrás de um screenshot deles no Google e o que acho? Curiosamente mais destas imagens que do próprio em questão. Isto já diz tudo:

vi-vim-cheat-sheet

Ai vem aquele moleque que quer ser diferente e se acha o maior por usar um desses.

A primeira instalação que fazia em algum Linux novo  – antigamente pois depois do ArchLinux nunca mais fiz instalação nova – era o MCEdit para evitar ter que editar arquivos de configurações nesta !@#$!. Aliás como se sai mesmo dele?  Era algo como ESC-Q!.

Eu não odeio o Terminal. Eu adoro o modo texto! Uso demais o Guake (terminal suspenso), mas perder meu tempo aprendendo esses atalhos se posso usar um IDE mais robusto não é comigo.

Fazer um esforço para aprender estes comandos para mim é uma perda de tempo se posso aprender muitas outras coisas mais úteis. De fato no momento estou fazendo um esforço para escrever robôs investidores em Forex e Bolsa e estou adorando o tema em torno de processos estocásticos. O Geany e o Metatrader estão sendo perfeitos para isso.

main-qimg-cf12b776c0e69ec6d30a47b0d90792d9

Geração dodói

Se você programa nestas coisas parabéns te admiro, mas não me pertube.

 

 

 

Ponteiros em 7 arquiteturas – de z80 a ARM

Eu admito que somente entendi ponteiros quando fiz a ponte entre código em C e Assembly. Ponteiros são complicados mesmo. É um disfarce de alto nível para o baixo nível da máquina. Ponteiros devem ser usados com cuidado em muitos aspectos; como no problema de alocação dinâmica e liberação da memória alocada, múltiplas instâncias do mesmo endereço, problemas de endianess, acessos não autorizados ou em áreas indesejadas …

Por curiosidade gerei o código assembly  do código em C abaixo para 7 processadores ou microcontroladores sendo eles: HCS08, STM8, Z80, X86-64,  ARM, ColdFire e AVR.

 int *a,*b;
 char *c;
 int i = 10;

void main(void)
 {

a = &i;
 b = a;
 *b = *b + 5;
 c = (char *) &i;
 *c = *c + 5;
 c = c + 5;

while (1);

}

E vamos aos códigos. É bom lembrar que diferenças irão ocorrer não somente pela arquitetura mas pelos compiladores usados. Um exemplo é o SDCC que para somar 5 não utilizou instrução de soma mas cinco incrementos para o Z80, provavelmente pela contagem de ciclos.

ColdFire – O mais limpo dos códigos

_main:
; main:
lea _i(a5),a0
move.l a0,_a(a5)
;
; 13: b = a;
;
move.l _a(a5),d0
move.l d0,_b(a5)
;
; 14: *b = *b + 5;
;
movea.l d0,a0
addq.l #5,(a0)
;
; 15: c = (char *) &i;
;
lea _i(a5),a0
move.l a0,_c(a5)
;
; 16: *c = *c + 5;
;
movea.l _c(a5),a0
mvs.b (a0),d0
addq.l #5,d0
move.b d0,(a0)
;
; 17: c = c + 5;
; 18:
; 19: while (1);
;
addq.l #5,_c(a5)
;
; 19: 1);
;
bra.l *+0</pre>

Z80 – Se aproveita de seus registradores de 16 Bits e acesso indireto

_main::
 61 ;ponteiros.c:9: a = &i;
ld de,#_i+0
ld (_a),de
 64 ;ponteiros.c:10: b = a;
ld hl,(_a)
ld (_b),hl
 67 ;ponteiros.c:11: *b = *b + 5;
ld hl,(_b)
push hl
ld c,(hl)
inc hl
ld b,(hl)
pop hl
inc bc
inc bc
inc bc
inc bc
inc bc
ld (hl),c
inc hl
ld (hl),b
 82 ;ponteiros.c:12: c = (char *) &i;
ld (_c),de
 84 ;ponteiros.c:13: *c = *c + 5;
ld hl,(_c)
ld a,(hl)
add a, #0x05
ld (hl),a
 89 ;ponteiros.c:14: c = c + 5;
ld hl,#_c
ld a,(hl)
add a, #0x05
ld (hl),a
inc hl
ld a,(hl)
adc a, #0x00
ld (hl),a
 98 ;ponteiros.c:16: while (1);
00102$:
jr 00102$

STM8 – O registrador x de 16 bits e as instruções de carga 16 bits ajudam muito


1 ; C Compiler for STM8 (COSMIC Software)
 2 ; Parser V4.10.2 - 02 Nov 2011
 3 ; Generator (Limited) V4.3.7 - 29 Nov 2011
 15 bsct
 16 0000 _i:
 17 0000 000a dc.w 10
 50 ; 7 main()
 50 ; 8 {
 52 switch .text
 53 0000 _main:
 57 ; 10 a = &i;
 59 ldw x,#_i
 60 ldw _a,x
 61 ; 11 b = a;
 63 ldw x,#_i
 64 ldw _b,x
 65 ; 12 *b = *b + 5;
 67 ldw x,_i
 68 addw x,#5
 69 ldw _i,x
 70 ; 13 c = (char *) &i;
 72 ldw x,#_i
 73 ldw _c,x
 74 ; 14 *c = *c + 5;
 76 ld a,_i
 77 add a,#5
 78 ld _i,a
 79 ; 15 c = c + 5;
 81 ldw x,#_i+5
 82 ldw _c,x
 83 L12:
 84 ; 17 while (1);
 86 jra L12
 140 xdef _main
 141 xdef _i
 142 switch .ubsct
 143 0000 _c:
 144 0000 0000 ds.b 2
 145 xdef _c
 146 0002 _b:
 147 0002 0000 ds.b 2
 148 xdef _b
 149 0004 _a:
 150 0004 0000 ds.b 2
 151 xdef _a
 171 end

HCS08 – Surpreso? Esse processador só tem um registrador index e um acumulador!


11: 
 12: a = &i;
 LDHX @i
 STHX a
 13: b = a;
 STHX b
 14: *b = *b + 5;
 LDHX ,X
 AIX #5
 TXA 
 PSHH 
 LDHX @i
 STA 1,X
 PULA 
 STA ,X
 15: c = (char *) &i;
 16: *c = *c + 5;
 ADD #5
 STA ,X
 17: c = c + 5;
 AIX #5
 STHX c
 L1E:
 19: while (1);
 BRA L1E
 21: }

X86-64 –

{
 push %rbp
 mov %rsp,%rbp

 a = &i;
 movq $0x0,0x0(%rip)  
 b = a;
 mov 0x0(%rip),%rax 
 mov %rax,0x0(%rip) 
 *b = *b + 5;
 mov 0x0(%rip),%rax 
  mov 0x0(%rip),%rdx 
 mov (%rdx),%edx
 add $0x5,%edx
 mov %edx,(%rax)
 c = (char *) &i;
 movq $0x0,0x0(%rip) 

 *c = *c + 5;
 mov 0x0(%rip),%rax 
 mov 0x0(%rip),%rdx 
 movzbl (%rdx),%edx
 add $0x5,%edx
 mov %dl,(%rax)
 c = c + 5;
 mov 0x0(%rip),%rax 
 add $0x5,%rax
 mov %rax,0x0(%rip) 

 while (1);
 jmp 65

ARM – É um RISC …


push {fp} ; (str fp, [sp, #-4]!)
add fp, sp, #0 
//a = &i;
ldr r3, [pc, #116] 
ldr r2, [pc, #116] 
str r2, [r3]
//b = a;
ldr r3, [pc, #104] 
ldr r3, [r3]
ldr r2, [pc, #104] 
str r3, [r2]
//*b = *b + 5;
ldr r3, [pc, #96] 
ldr r3, [r3]
ldr r2, [pc, #88] 
ldr r2, [r2]
ldr r2, [r2]
add r2, r2, #5
str r2, [r3]
//c = (char *) &i;
ldr r3, [pc, #72] 
ldr r2, [pc, #60] 
str r2, [r3]
//*c = *c + 5;
ldr r3, [pc, #60] 
ldr r3, [r3]
ldr r2, [pc, #52] 
ldr r2, [r2]
ldrb r2, [r2]
add r2, r2, #5
and r2, r2, #255 
strb r2, [r3]
//c = c + 5;
ldr r3, [pc, #28] 
ldr r3, [r3]
add r3, r3, #5
ldr r2, [pc, #16] 
str r3, [r2] 
//while (1);
b 80 

AVR – Infelizmente o processamento ficou todo em registradores


push r28
push r29
in r28, 0x3d ; 61
in r29, 0x3e ; 62 
// a = &i;
ldi r24, 0x00 ; 0
ldi r25, 0x00 ; 0
sts 0x0000, r25
sts 0x0000, r24
// b = a;
lds r24, 0x0000
lds r25, 0x0000
sts 0x0000, r25
sts 0x0000, r24
// *b = *b + 5;
lds r24, 0x0000
lds r25, 0x0000
lds r18, 0x0000
lds r19, 0x0000
mov r30, r18
mov r31, r19
ld r18, Z
ldd r19, Z+1 ; 0x01
subi r18, 0xFB ; 251
sbci r19, 0xFF ; 255
mov r30, r24
mov r31, r25
std Z+1, r19 ; 0x01
st Z, r18
// c = (char *) &i;
ldi r24, 0x00 ; 0
ldi r25, 0x00 ; 0
sts 0x0000, r25
sts 0x0000, r24
// *c = *c + 5;
lds r24, 0x0000
lds r25, 0x0000
lds r18, 0x0000
lds r19, 0x0000
mov r30, r18
mov r31, r19
ld r18, Z
subi r18, 0xFB ; 251
mov r30, r24
mov r31, r25
st Z, r18
// c = c + 5;
lds r24, 0x0000
lds r25, 0x0000
adiw r24, 0x05 ; 5
sts 0x0000, r25
sts 0x0000, r24

// while (1);
rjmp .+0

Afinal o que é um ponteiro? É simplesmente uma posição de memória que guarda um endereço que contém algum dado.

São dois endereços: Um é a variável que contém o dado, o outro contém o endereço desta variável.

Assim b = &i diz ao processador para guardar no endereço b o endereço de i. Logo b e i estão apontando para o mesmo dado. Mas b não é igual a i. A variável i é um endereço que contém um dado e o ponteiro b é um endereço que contém um endereço que contém um dado.

Veja em HCS08

// a = &i
LDHX @i
STHX a
Carrega o endereço de i em HX e posteriormente guarda o valor de HX em a

Veja que em STM8

// *b = *b + 5
67 ldw x,_i
68 addw x,#5
69 ldw _i,x

O compilador decidiu por não usar o ponteiro b para acessar i pois ele percebeu que b ainda contém o endereço de i e usou diretamente o endereço de i. Normalmente o correto seria carregar o valor de b em x e então indiretamente ‘(x)’ carregar o valor endereçado por x no acumulador e finalmente efetuar a soma.

Foi exatamente o que o Z80 iria fazer

ld hl,(_b)
push hl
ld c,(hl)
inc hl
ld b,(hl)
pop hl
inc bc
inc bc
inc bc
inc bc
inc bc
ld (hl),c
inc hl
ld (hl),b

Espero ter ajudado a compreender o que de fato é um ponteiro. Entretanto este post foi útil e agradável ao demonstrar as diferentes formas de acesso a memória em diferentes arquiteturas e principalmente as otimizações que compiladores diferentes fazem.

 

Rapido exemplo usando pthread.

A biblioteca pthread é um meio rápido e confiável de se criar aplicações multi-threads. Neste post será demonstrado um programa simples usando pthread.

  1. A biblioteca esta disponível no header <pthread.h> que deverá estar inclusa na compilação usando #include <pthread.h>.
  2. Criar a estrutura pthread_t para cada thread usado. Se for somente um pthread_t thread; será o suficiente caso contrário um ponteiro ou array de pthread_t será necessário como em pthread_t *threads;
  3. Criar a função que ficará separada do processo principal com tipo void e parametro *void. Como no exemplo void *Tarefa (void *param);
  4. Já criamos a estrutura e a função. Podemos então criar o thread usando pthread_create. Ela leva quatro parametros o primeiro a estrutura que criamos em 2; o segundo pode ser ignorado e passado NULL, o terceiro o ponteiro para a função (Tarefa) e finalmente o parametro (*void) para esta função (Tarefa (*void param)). Ao chamar pthread_create a tarefa (o thread) começa executando.
  5. Pronto já temos uma tarefa rodando em paralelo. Mas se esta tarefa tiver um fim precisamos esperar que ela termine antes que o processo principal termine antes e para isso usamos int pthread_join(pthread_t thread, void **value_ptr); que fica esperando pela tarefa identificada por pthread_t thread terminar e se esta retornou algum valor este estara guardado em void **value_ptr. Tenha em mente que esta função bloqueia a tarefa que a chamou até que a tarefa esperada termine.

Finalmente o código fica:


#include <pthread.h>
#include <stdio.h>

 void *Tarefa(void *param)
 {

 int *tx = (int *)param;
 int Fim = (*tx) + 20;

for (;(*tx) < Fim; (*tx)++;); // Incrementa X vinte vezes
 return NULL; // A tarefa não deverá retornar nada

}

int main()
 {

int x = 0, y = 0;

// Mostra antes
 printf("x: %d, y: %d\n ", x, y);

 pthread_t thread;

 if(pthread_create(&thread, NULL, Tarefa, &x)) {

fprintf(stderr, "Não deu certo a criação do thread\n");
 return 1;

}

 if(pthread_join(thread, NULL)) {

fprintf(stderr, "Falhou esperando fim da tarefa.\n");
 return 2;

}

// E finalmente o depois
 printf("x: %d, y: %d \n", x, y);

return 0;

}

Buffer Circular sem comparação e com um contador

Uma forma clássica de circular um buffer é o velho:

 if (contador < XXX) contador ++; else contador = 0; 

Mas se o buffer tiver um tamanho potência de 2 (2^X) podemos facilmente suprimir o if e usar a operação AND como em

contador = (contador + 1) AND (maximo - 1)

Em C++/C fica:

 contador = (contador + 1) & [1 3 7 15 ... 255 511 ... 1023 ...]; 

Também é possível usar um mesmo contador para vários buffers de tamanhos diferentes:

contador++;
 bufferA[contador & 7] = ....
bufferB[contador & 255] = ...

Mas resista a tentação de fazer contador = contador++ & (max -1);  use o velho (contador + 1)  para evitar um erro dificil de se achar (tente).