-
Notifications
You must be signed in to change notification settings - Fork 0
/
2008-02.html
1296 lines (1020 loc) · 72.2 KB
/
2008-02.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en-us" dir="ltr" itemscope itemtype="http://schema.org/Article">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Blogue do Caloni</title>
<meta name="author" content="Caloni" />
<meta name="generator" content="txt2blog 0.0.1">
<meta property="og:title" content="Blogue do Caloni"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="http://www.caloni.com.br"/>
<meta property="og:image" content="/img/about-brand.png"/>
<meta property="og:description" content="Write for computers, people and food."/>
<link href="/index.xml" rel="feed" type="application/rss+xml" title="Blogue do Caloni"/>
<link rel="stylesheet" type="text/css" href="/css/custom.css"/>
<link rel="stylesheet" type="text/css" href="/css/jquery-ui.css"/>
<script src="/js/jquery-1.12.4.js"></script>
<script src="/js/jquery-ui.js"></script>
<script src="/js/copy_clipboard.js"></script>
<script>
var quick_search_posts = [
];
</script>
<script src="/js/quick_search.js"></script>
<script src="/js/list.js"></script>
<link rel="icon" href="/img/favicon.ico"/>
</head>
<body style="min-height:100vh;display:flex;flex-direction:column">
<nav class="navbar has-shadow is-white"
role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item" href="months.html">
<div class="is-4"><b>caloni::2008-02</b></div>
</a>
</div>
</div>
</nav>
<div class="container">
<div class="column">
<div style="min-height:56vh">
<div style="padding-bottom: 1em;"></div>
<ul style="list-style: none;">
<li><small><a href="2008-02.html#compartilhando_variaveis_com_o_mundo_v2">Compartilhando variáveis com o mundo v2</a></small></li>
<li><small><a href="2008-02.html#process_monitor_e_o_monopolio_malcriado">Process Monitor e o monopólio malcriado</a></small></li>
<li><small><a href="2008-02.html#silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution">Silly regex trick</a></small></li>
<li><small><a href="2008-02.html#desconstruindo_ioccc">Desconstruindo IOCCC</a></small></li>
<li><small><a href="2008-02.html#funky_do_while">Funky do-while</a></small></li>
<li><small><a href="2008-02.html#os_diferentes_erros_na_linguagem_c">Os diferentes erros na linguagem C</a></small></li>
<li><small><a href="2008-02.html#configurando_seus_projetos_no_visual_studio">Configurando seus projetos no Visual Studio (Multi-threaded Debug DLL /MT,</a></small></li>
<li><small><a href="2008-02.html#codigos_de_entrevista_o_ponteiro_nulo">Códigos de entrevista - o ponteiro nulo</a></small></li>
<li><small><a href="2008-02.html#conversor_de_houaiss_para_babylon_parte_1">Conversor de Houaiss para Babylon - parte 1</a></small></li>
<li><small><a href="2008-02.html#quando_o_ponteiro_nulo_nao_e_invalido">Quando o ponteiro nulo não é inválido</a></small></li>
</ul>
<span id="compartilhando_variaveis_com_o_mundo_v2" title="Compartilhando variáveis com o mundo v2"/></span>
<section id="section_compartilhando_variaveis_com_o_mundo_v2">
<p class="title"><a href="2008-02.html#compartilhando_variaveis_com_o_mundo_v2">#</a> Compartilhando variáveis com o mundo v2</p>
<span class="title-heading">Caloni, 2008-02-01 <a href="coding.html">coding</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_compartilhando_variaveis_com_o_mundo_v2')"><sup>[copy]</sup></a></span>
<p>Nota de desempenho: esse artigo finaliza (finalmente) a republicação de todos os artigos do antigo blogue. Isso quer dizer que a partir de agora eu sou obrigado a trabalhar, e, se quiser manter meu ritmo atual, vou ter que fazer mais do que cinco cliques do mouse.</p>
<p>Como todas as coisas que fazemos e pensamos depois, descobrimos que sempre existe uma outra maneira de fazer a mesma coisa. Se é melhor ou não, pode ser uma questão de gosto, estética, objetivos de vida, etc. Com a <a href="2008-01.html#compartilhando_variaveis_com_o_mundo">implementação das variáveis mapeadas globais</a> não foi diferente. Bem, é isso que se espera fazer com código experimental: experimentos. E deu no que deu: SharedVar versão 2.0 alpha Enterprise Edition.</p>
<p>Quando comentei no final do artigo anterior que existem pessoas que só conseguem gerar código dentro de uma classe, não estava brincando. Existem linguagens, inclusive, que suportam apenas o paradigma de orientação a objetos, e levam isso muito a sério. C++ com certeza não é uma dessas linguagens, o que quer dizer que você tem a liberdade e a responsabilidade de tomar o melhor caminho para determinado problema.</p>
<p>Nessa segunda solução do nosso programa alocador de variáveis globais, pra variar, vamos utilizar uma classe. E pra entrar de vez no mundo POO vamos utilizar de quebra tratamento de erro orientado a exceções. Como vamos notar, aplicadas adequadamente, essas duas características da linguagem conseguirão um código mais simples de entender, embora não se possa dizer o mesmo da implementação "under the hood".</p>
<pre>
/** Classe helper para as nossas funções de alocação de variáveis
compartilhadas com o mundo. */
template<typename T>
class SharedVar
{
public:
// se conseguir, parabéns; senão, retorna BUM!
SharedVar(PCTSTR varName)
{
m_memPointer = 0;
m_memHandle = AllocSharedVariable(&m_memPointer, varName);
if( ! m_memHandle || ! m_memPointer )
throw GetLastError();
}
// libera recursos alocados para a variável
~SharedVar()
{
FreeSharedVariable(m_memHandle, m_memPointer);
}
T& operator * ()
{
return *m_memPointer;
}
private:
// não vamos nos preocupar com isso agora
SharedVar(const SharedVar& obj);
SharedVar& operator = (const SharedVar& obj);
T* m_memPointer;
HANDLE m_memHandle;
};
</pre>
<p>Como podemos notar, em programação "nada se cria, tudo se reutiliza". Reutilização é boa quando podemos acrescentar características adicionais ao código sem deturpar seu objetivo original. E isso é bom.</p>
<p>Note que nossa classe tenta fazer as coisas logo no construtor, já que seu único objetivo é representar uma variável da memória cachê. Se ela não for bem-sucedida em sua missão, ela explode, porque não há nada que ela possa fazer para garantir a integridade do objeto sendo criado e ela não tem como saber qual o melhor tratamento de erro para o usuário da classe. Geralmente o melhor - ou pelo menos o mais adequado - é o tratamento que o usuário dá ao seu código, porque o usuário da classe é que deve saber o contexto de execução do seu código.</p>
<p>Bem, como o código agora está em uma classe e o erro é baseado em exceção, o código cliente muda um pouco:</p>
<pre>
/** Exemplo de como usar as funções de alocação de memória compartilhada
AllocSharedVariable, OpenSharedVariable e FreeSharedVariable.
*/
int _tmain(int argc, PTSTR argv[])
{
try
{
// passou algum parâmetro: lê a variável compartilhada e exibe
if( argc > 1 )
{
system("pause");
// array de 100 TCHARs
SharedVar<TCHAR [100]> sharedVar(_T(SHARED_VAR));
_tprintf(_T("Frase secreta: \'%s\'\n"), *sharedVar);
_tprintf(_T("Pressione <enter> para retornar..."));
getchar();
}
else // não passou parâmetro: escreve na variável
// compartilhada e chama nova instância
{
// array de 100 TCHARs
SharedVar<TCHAR [100]> sharedVar(_T(SHARED_VAR));
PTSTR cmd = new TCHAR[ _tcslen(argv[0]) + 10 ];
_tcscpy(cmd, _T("\""));
_tcscat(cmd, argv[0]);
_tcscat(cmd, _T("\" 2"));
_tcscpy(*sharedVar,
_T("Vassora de sa, vassora de su, vassora de tuturuturutu!"));
_tsystem(cmd);
delete [] cmd;
}
}
catch(DWORD err)
{
_tprintf(_T("Erro %08X.\n"), err);
}
return 0;
}
</pre>
<p>Existem duas mudanças significativas: 1. a variável sozinha já representa a memória compartilhada; 2. o tratamento de erro agora é centralizado em apenas um ponto. Se pra melhor ou pior, eu não sei. Tratamento de exceções e classes são duas "modernisses" que podem ou não se encaixar em um projeto de desenvolvimento. Tudo vai depender de tudo. Por isso a melhor saída depende de como será a entrada.</p>
</section><hr/>
<span id="process_monitor_e_o_monopolio_malcriado" title="Process Monitor e o monopólio malcriado"/></span>
<section id="section_process_monitor_e_o_monopolio_malcriado">
<p class="title"><a href="2008-02.html#process_monitor_e_o_monopolio_malcriado">#</a> Process Monitor e o monopólio malcriado</p>
<span class="title-heading">Caloni, 2008-02-05 <a href="coding.html">coding</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_process_monitor_e_o_monopolio_malcriado')"><sup>[copy]</sup></a></span>
<p>Uma das primeiras regras que aprendemos para manter a integridade do Windows é utilizá-lo somente com a conta de usuários restritos, o que evitaria, por exemplo, que um programa mal-intencionado instale um serviço ou driver, que teriam acesso às partes íntimas do sistema operacional.</p>
<p>Essa é uma regra básica, mas não é fácil de cumpri-la. Só quem já tentou fazer isso sabe do que estou falando. Inúmeros programas mal-escritos vão tentar, de uma hora pra outra, acessar áreas do sistema de arquivos e registro que não possuem acesso, pois agora estão rodando em uma conta mais restrita. E não são programas de administração ou manutenção do sistema. Estou falando de programas de escritório e jogos. Aqui vai um singelo exemplo que tive que lidar esse fim-de-semana.</p>
<p>Primeiramente, quero deixar bem claro que jogamos Monopoly por mais ou menos dois meses sem ter qualquer tipo de problema, em três computadores diferentes. Até que resolvemos usar uma conta mais restrita. Foi o bastante para o programinha inocente começar a chiar.</p>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_crash.png"/>
<p>Mau garoto. Bons tempos em que quando um jogo travava o máximo que tínhamos que fazer era apertar um botão.</p>
<p>Para encontrar problemas desse tipo, sempre uso o Process Monitor, que tem virado minha ferramenta básica para muitas coisas. Para os que não conhecem, o Process Monitor é uma ferramenta de auditoria de operações no sistema operacional, ou seja, tudo que alguém ler e escrever em arquivos e no registro será logado.</p>
<p>Sua função é mostrar tudo, absolutamente tudo que o sistema está fazendo em um determinado espaço no tempo. Isso pode ser ruim por um lado, já que será bem difícil encontrar alguma informação útil no meio de tanto lixo que pode ser gerado em um log de poucos momentos. Para ter uma idéia do que eu estou falando, tente abrir o Procmon sem qualquer filtro e deixá-lo rodando por 30 segundos sem fazer nada. No meu sistema isso deu aproximadamente 20 mil linhas de eventos de log. Nada mau para um sistema ocioso.</p>
<p>É por isso que ele vem "de fábrica" já com uma série de filtros, que evitam lotar o log de eventos com informação sempre gerada pelo sistema, mas quase sempre inútil. Além dos filtros-padrão, podemos inserir nossos próprios filtros. É isso que faremos aqui para pegar o monopólio malcriado (sem trocadilhos).</p>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_procmon.png"/>
<p>Como podemos ver, iremos mostrar em nosso log todos os eventos cujo nome do processo seja monopolyclassic.exe (o nosso amigo faltoso) e iremos excluir do log qualquer evento cujo resultado tenha sido sucesso (se deu certo, provavelmente não é um erro).</p>
<p>Executamos novamente o jogo, dessa vez com o Process Monitor capturando todos seus movimentos.</p>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_crash.png"/>
<p>Agora, uma pequena ressalva: eu estou cansado de ver isso, mas para quem nunca viu, pode não ser tão óbvio. Como eu disse no início do artigo, programas mal-escritos costumam tentar acessar áreas do sistema que não são acessíveis para usuários comuns. Isso quer dizer que, se o problema que está acontecendo com o jogo tem a ver com essa peculiaridade, a primeira coisa a procurar é por erros de acesso negado.</p>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_procmon_access_denied1.png"/>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_procmon_access_denied2.png"/>
<p>A primeira busca retorna uma chave no registro referente às propriedades de joystick. Como não estou usando joysticks, podemos ignorar este erro por enquanto e passar adiante.</p>
<pre>
MonopolyClassic.exe CreateFile C:\Documents and ...\TikGames\Monopoly NAME COLLISION
MonopolyClassic.exe CreateFile C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log ACCESS DENIED
MonopolyClassic.exe QueryOpen C:\Arquivos de programas\GameHouse\Monopoly Classic\DBGHELP.DLL NAME NOT FOUND
MonopolyClassic.exe RegOpenKey HKLM\Software\Microsoft\...\DBGHELP.DLL NAME NOT FOUND
</pre>
<p>O próximo erro diz respeito a uma tentativa de acesso ao arquivo Monopoly.log localizado no diretório de instalação do jogo, o que já é mais sugestivo. Podemos fazer um pequeno teste alterando o acesso desse arquivo.</p>
<pre>
C:\Arquivos de programas\GameHouse\Monopoly Classic>cacls Monopoly.log
C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log BUILTIN\Usuários:R
BUILTIN\Administradores:F
AUTORIDADE NT\SYSTEM:F
MITY\Caloni:F
C:\Arquivos de programas\GameHouse\Monopoly Classic>
</pre>
<p>Como podemos ver, o que é muito natural, um arquivo dentro da pasta de instalação de programas permite acesso de somente leitura para usuários comuns a seus arquivos, inclusive o Monopoly.log. Para fazer o teste, podemos simplesmente adicionar controle total a apenas esse arquivo, e rodar novamente o jogo.</p>
<pre>
>cacls Monopoly.log /E /G Usuários:F
arquivo processado: C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log
C:\Arquivos de programas\GameHouse\Monopoly Classic>cacls Monopoly.log
C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log BUILTIN\Usuários:F
BUILTIN\Administradores:F
AUTORIDADE NT\SYSTEM:F
MITY\Caloni:F
C:\Arquivos de programas\GameHouse\Monopoly Classic>start monopolyclassic.exe
</pre>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_screenshot.jpg"/>
<p>Ora essa, estou conseguindo rodar o jogo! Isso quer dizer que nosso único problema, o acesso a esse arquivo, foi resolvido. Sabendo que um arquivo de log provavelmente não será executado por nenhuma conta privilegiada, podemos deixá-lo com acesso irrestrito para todos.</p>
<p>Para ter certeza que isso resolveu o problema, uma segunda auditoria de execução executada pelo Process Monitor pode nos revelar mais detalhes.</p>
<img src="img/process_monitor_e_o_monopolio_malcriado_monopoly_procmon_find2.png"/>
<pre>
MonopolyClassic.exe QueryStandardInformationFile C:\Documents ...\Monopoly\save.gcf SUCCESS
MonopolyClassic.exe ReadFile C:\Documents ...\Monopoly\save.gcf SUCCESS
MonopolyClassic.exe CloseFile C:\Documents ...\Monopoly\save.gcf SUCCESS
MonopolyClassic.exe CreateFile C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log SUCCESS
MonopolyClassic.exe CreateFile C:\Arquivos de programas\GameHouse\Monopoly Classic SUCCESS
MonopolyClassic.exe CloseFile C:\Arquivos de programas\GameHouse\Monopoly Classic SUCCESS
MonopolyClassic.exe WriteFile C:\Arquivos de programas\GameHouse\Monopoly Classic\Monopoly.log SUCCESS
</pre>
<p>Moral da história: se algum dia você vier a escrever um programa inocente, deixe que pessoas inocentes consigam utilizá-lo.</p>
</section><hr/>
<span id="silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution" title="Silly regex trick"/></span>
<section id="section_silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution">
<p class="title"><a href="2008-02.html#silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution">#</a> Silly regex trick</p>
<span class="title-heading">Caloni, 2008-02-07 <a href="coding.html">coding</a> <a href="english.html">english</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution')"><sup>[copy]</sup></a></span>
<p>I know what you going to think about this one: "silly trick". That's why I just put it in the title. Anyway, that is something I use everyday, so I thought it might be useful to who cares about productivity.</p>
<p>Let's say you have to manage a big solution in Visual Studio made of more than 30 projects, and needs to rebuild all them. Suddenly, something goes wrong. The question is: how to discover, in a heartbeat, what project has failed?</p>
<img src="img/silly_regex_trick_finding_the_project_who_failed_inside_a_vs_big_solution_find_error_regex2.png"/>
<p>Note that you need to enable "Regular Expressions" option in the Find Dialog (not shown here).</p>
<p>What I'm saying inside this regex is "find the first number different from zero followed by a space and the letters err". This lead us to the first project who has at least one error:</p>
<pre>
------ Build started: Project: FailedProj, Configuration: Release Win32 ------
Compiling...
stdafx.cpp
Compiling...
FailedProj.cpp
FailedProj.cpp(2477) : error C2039: 'Blablabla' : is not a member of 'IBlabla'
Build log was saved at "file://c:Projects...ReleaseBuildLog.htm"
FailedProj - 2 error(s), 0 warning(s)
</pre>
<p>If you think "what about when a project generates more than 9 errors? the regex wouldn't be able to catch this case", well, you're right. Anyway, that's the quicker form to search for the unsuccessful project inside a big solution. A more complex yet complete regex would be:</p>
<pre>
[1-9][0-9]* err
</pre>
<p>For me, the first version is enough. It is faster to type, simpler to catch and solves my problem. I hope it can solve yours =)</p>
</section><hr/>
<span id="desconstruindo_ioccc" title="Desconstruindo IOCCC"/></span>
<section id="section_desconstruindo_ioccc">
<p class="title"><a href="2008-02.html#desconstruindo_ioccc">#</a> Desconstruindo IOCCC</p>
<span class="title-heading">Caloni, 2008-02-11 <a href="coding.html">coding</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_desconstruindo_ioccc')"><sup>[copy]</sup></a></span>
<p>Como alguns devem saber, e outros não (ou não deveriam), existe uma competição internacional para escolher quem escreve o código em C mais ofuscado. Isso mesmo. O evento se chama <a href="http://www.ioccc.org">The International Obfuscated C Code Contest</a> (IOCCC resumidamente) e costuma premiar anualmente os melhores "do ramo" com a chamada "menção desonrosa".</p>
<p>Acredito que a real valia de um campeonato desse porte é fazer as pessoas pensarem mais a fundo sobre as regras da linguagem. Isso faz com que erros mais obscuros que encontramos no dia-a-dia se tornem mais fáceis. Claro que ninguém deveria programar como os caras desse torneio, mas a título de aprendizagem, é uma grande aula sobre C.</p>
<p>Publico aqui a interpretação do primeiro programa a ganhar a tal <a href="http://www.ioccc.org/1984/anonymous/anonymous.c">menção desonrosa</a>, em 1984. Se trata do batidíssimo "Hello World", só que um pouco compactado e confuso. Vejamos o fonte original:</p>
<pre>
int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}
</pre>
<p>Aparentemente o fonte é bem confuso, apesar de podermos já ver a famosa string escondida no meio do código. Depois de aplicar uma formatação mais adequada para nossa tarefa de desfazer o feito, o resultado é bem mais legível:</p>
<pre>
int i;
main()
{
for( ; i["] < i; ++i ){--i;}"]; read('-' - '-', i++ + "hello, world!\n", '/' / '/') )
;
}
read(j, i, p)
{
write(j / p + p, i-- - j, i / i);
}
</pre>
<p>Algumas construções são óbvias. Vamos então partir para as não-tão-óbvias.</p>
<pre>
int i;
</pre>
<p>Como toda variável global inteira, é inicializada com zero. Logo, a linha acima é equivalente a "int i =0".</p>
<pre>
main() { }
read() { }
</pre>
<p>Aos programadores C++ desavisados de plantão, em C o valor de retorno padrão é int, e, caso não seja retornado nada, isso não constitui um erro, porém o comportamento é não-definido. Nada de mal, porém, pode ocorrer, a não ser o retorno de lixo da pilha.</p>
<pre>
for( ; <censurado>; <censurado> )
;
</pre>
<p>Outra coisa óbvia, mas não tanto, é um laço for sem corpo. Ele possui apenas um ponto-e-vírgula, que identifica uma instrução nula. Não faz nada no corpo, mas pode fazer coisas interessantes no cabeçalho, ou seja, na inicialização, no teste e no incremento. Como podemos ver, a inicialização também está vazia, contendo esse laço apenas o teste e o incremento. No teste temos a seguinte comparação:</p>
<pre>
i["] < i; ++i ){--i;}"]
</pre>
<p>Ora, sabendo que a variável "i" inicialmente tem o valor zero, o que estamos vendo aqui é a mesma coisa que</p>
<pre>
0["] < i; ++i ){--i;}"]
</pre>
<p>E uma vez que aprendemos algumas <a href="2007-12.html#curiosidades_inuteis_o_operador_de_subscrito_em_c">peculiaridades sobre o operador de subscrito</a> em C, sabemos que a linha acima é equivalente a essa linha abaixo:</p>
<pre>
"] < i; ++i ){--i;}"[0]
</pre>
<p>Agora ficou mais fácil de entender. Se trocarmos a nossa string literal por uma variável (forma mais usual), temos um acesso típico a um dos caracteres de uma string:</p>
<pre>
char* str = "] < i; ++i ){--i;}";
str[0];
</pre>
<p>Só precisamos lembrar que a variável i é que define a posição, e por ser uma variável, pode mudar durante a execução:</p>
<pre>
int i = 0;
char* str = "] < i; ++i ){--i;}";
str[i];
</pre>
<p>Pois bem. Agora sabemos que o laço irá ser testado pelo menos uma vez, o que quer dizer que a parte do incremento vai executar pelo menos uma vez. E essa parte é a seguinte:</p>
<pre>
read('-' - '-', i++ + "hello, world!\n", '/' / '/')
</pre>
<p>Uma chamada de função. Nada mais simples. Podemos anular algumas coisas por aqui. Por exemplo, se subtraímos um número dele mesmo encontramos zero, e se dividirmos um número por ele mesmo o resultado é um:</p>
<pre>
'-' - '-' == 0
'/' / '/' == 1
</pre>
<p>Lembre-se de que um caractere em C é um tipo inteiro, e portanto, pode fazer parte de cálculos matemáticos. Depois dessa simplificação, temos</p>
<pre>
read(0, i++ + "hello, world!\n", 1)
</pre>
<p>Agora você deveria estar se perguntando (se ainda não encontrou a resposta) do porquê de eu ter dividido os três sinais de + dessa forma. Existem duas opções para a divisão:</p>
<pre>
i++ + "hello, world!\n" /* ou */
i+ ++"hello, world"\n" /* ?? */
</pre>
<p>A primeira forma é a resposta correta devido à regra de precedência (deferida pela gramática). Antes os operadores unários, depois os binários. Dessa forma, um "i+" não quer dizer nada, mas "i++" é um operando com um operador unário.</p>
<p>Voltando à expressão, imagino que a essa altura você já deva ter decifrado que i++ + "hello, world!\n" é o mesmo que:</p>
<pre>
char* str = "hello, world"\n";
&str[i++];
</pre>
<p>Ou seja, obtemos o endereço do primeiro caractere da string e incrementamos nossa variável "i" que, como sabemos, é usada no teste do laço for. Na primeira vez, testamos se o primeiro caractere de "] < i; ++i ){--i;}" é diferente de zero. Na segunda iteração, portanto, iremos testar se o segundo caractere será zero. Sabendo disso, podemos deduzir que o laço irá correr por todos os caracteres da string de teste, até encontrar o zero finalizador de string. Ao mesmo tempo, iremos enviar para a função read sempre o endereço do i'ésimo caractere da string "hello, world!\n", pois essa string também é indexada pela variável "i".</p>
<p>Isso quer dizer que nosso laço irá terminar exatamente no final de ambas as strings! (Note, que para comparar as strings, usamos as strings originais do programa, sem melhorar a formatação).</p>
<pre>
"] < i ; + + i ) { - - i ; }"
1 2 3 4 5 6 7 8 9 0 1 2 3 4
"h e l l o , w o r l d ! \n"
</pre>
<p>Também devemos lembrar que o caractere de controle '\n' é representado apenas por um byte, apesar de no fonte parecer dois.</p>
<p>Em um passado bem longínquo, o padrão ANSI C não existia, e outras funções dominavam o ambiente UNIX. Muitas dessas funções foram adaptadas, e outras completamente copiadas para a formação do padrão. No entanto, ainda que o padrão não tenha colocado algumas funções clássicas, elas continuaram sendo usadas e suportadas. Um bom exemplo disso são as funções read e write, que, apesar de não estarem no padrão, estão no <a href="posts.html?q=livro de K&R">livro de K&R</a>, no capítulo sobre fluxos (streams) em UNIX, provando que são bem populares.</p>
<p>Dentro desse mundo paralelo, existem identificadores de fluxos padrões para a entrada e a saída padrão. Melhor ainda, esses identificadores são inteiros que estão especificados da seguinte maneira (tirado da <a href="http://www.gnu.org/software/libc/manual/html_node/Descriptors-and-Streams.html#index-STDIN_005fFILENO-1235">referência GNU</a> da linguagem C, meu grifo):</p>
<blockquote>There are also symbolic constants defined in unistd.h for the file descriptors belonging to the standard streams stdin, stdout, and stderr; see Standard Streams.</blockquote>
<blockquote></blockquote>
<blockquote>STDIN_FILENO</blockquote>
<blockquote>This macro has value 0, which is the file descriptor for standard input.</blockquote>
<blockquote></blockquote>
<blockquote>STDOUT_FILENO</blockquote>
<blockquote>This macro has value 1, which is the file descriptor for standard output.</blockquote>
<blockquote></blockquote>
<blockquote>STDERR_FILENO</blockquote>
<blockquote>This macro has value 2, which is the file descriptor for standard error output.</blockquote>
<p>Agora podemos voltar ao fonte. Vejamos como é implementada a função read, chamada dentro do laço for. Como todos sabem, se uma função já é definida em sua própria unidade, não haverá uma busca por referências externas, o que quer dizer que a implementação padrão de read não atrapalha a implementação local.</p>
<pre>
read(j, i, p)
{
write(j / p + p, i-- - j, i / i);
}
</pre>
<p>Ótimo. A função read chama a função (essa sim, padrão) write. Sabemos que tanto o primeiro quanto o último parâmetro da função será sempre constante no laço for:</p>
<pre>
read(0, i++ + "hello, world!\n", 1)
</pre>
<p>O que quer dizer que o primeiro argumento passado para write será sempre o mesmo:</p>
<pre>
j == 0;
p == 1;
j / p + p == 1;
</pre>
<p>Além da constante óbvia passada no último argumento:</p>
<pre>
i / i = 1; /* independente de i */
</pre>
<p>Isso quer dizer que a chamada para write pode ser resumida para:</p>
<pre>
write(1, i-- - j, 1);
</pre>
<p>O decremento da variável "i" (dentro de read) nunca é usado, uma vez que é uma variável local. E subtrair "j" é inócuo, uma vez que o valor de "j" será sempre zero. Logo, o argumento do meio é sempre o parâmetro do meio, por mais idiota que isso possa parecer =)</p>
<pre>
write(1, i, 1);
</pre>
<p>Pronto, já temos condições de interpretar o significado dessa chamada à write. Como já vimos, o número 1 identifica a saída padrão, o que quer dizer que estamos escrevendo algo na saída padrão. Esse algo é o parâmetro "i" que, como vimos, é o endereço do i'ésimo caractere da string "hello, word!\n". O último argumento é o número de bytes a serem escritos, que será sempre um. O que quer dizer que o laço em for chamada a função read strlen("hello, world!\n") vezes passando o endereço do próximo caractere de cada vez. A função read, por sua vez, escreve este caractere na saída padrão. O resultado, como todos que compilarem o fonte e rodarem poderão comprovar, é a impressão da mensagem mais famosa do mundo da computação:</p>
<pre>
hello, world!
</pre>
<p>E voilà =)</p>
<p>Abaixo um código-fonte equivalente, devidamente desencriptado:</p>
<pre>
int i = 0;
main()
{
char* indexString = "]<i;++i){--i;}";
char* outputString = "hello, world!\n";
for( ; indexString[i] != 0; read(&outputString[i++]) )
;
}
read(outStr)
{
write(1, outStr, 1);
}
</pre>
</section><hr/>
<span id="funky_do_while" title="Funky do-while"/></span>
<section id="section_funky_do_while">
<p class="title"><a href="2008-02.html#funky_do_while">#</a> Funky do-while</p>
<span class="title-heading">Caloni, 2008-02-13 <a href="coding.html">coding</a> <a href="english.html">english</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_funky_do_while')"><sup>[copy]</sup></a></span>
<p>It's a known habit to use do-while constructions when there's a need to define a macro that has more than one command instead of using the { simple multicommand brackets }. What was never clear is why this is so.</p>
<p>Let's imagine a trace macro that's enabled in debug mode, whilst kept in silence in release builds:</p>
<pre>
#ifdef NDEBUG
#define MYTRACE( message ) /* nothing */
#else
#define MYTRACE( message ) \
{ \
char buffer[500]; \
sprintf(buffer, \
"MYTRACE: %s(%d) %s\n", \
__FILE__, \
__LINE__, \
message); \
OutputDebugString(buffer); \
}
#endif /* NDEBUG */
</pre>
<p>Nothing much, but it seems to work. But, as we going to see in the following lines, it is really a buggy piece of code, since a call inside an if-else construction simply doesn't work.</p>
<pre>
if( exploded() )
MYTRACE("Oh, my God");
else
MYTRACE("That's right");
error C2181: illegal else without matching if
</pre>
<p>Why's that? In order to answer this question, we need to look closer into the result code from the preprocessor, just replacing the macro for its piece of code:</p>
<pre>
if( exploded() )
{
char buffer[500];
sprintf(buffer,
"MYTRACE: %s(%d) %s\n",
__FILE__,
__LINE__,
"Oh, my God");
OutputDebugString(buffer);
};
else
{
char buffer[500];
sprintf(buffer,
"MYTRACE: %s(%d) %s\n",
__FILE__,
__LINE__,
"That's right");
OutputDebugString(buffer);
};
</pre>
<p>So, that's why. When we call a macro, generally we use the funcion-call syntax, putting a semicolon in the end. This is the right way to call a function, but in the macro case, it's a disaster, because it creates two commands instead of one (an empty semicolon, despite doing nothing, it's a valid command). So that's what the compiler does:</p>
<pre>
if( instruction )
{
/* a lot of comands */
} /* here I would expect an else or new instruction */
; /* a new command! okay, no else this time */
else /* wait! what this else is doing here without an if?!?! */
{
/* more commands */
}
</pre>
<p>Think about the empty command as if it was a real command, what is the easier way to realize the compiler error:</p>
<pre>
if( error() )
{
printf("error");
}
printf("here we go");
else /* llegal else without matching if! */
{
printf("okay");
}
</pre>
<p>For this reason, the tradicional way to skip this common error is to use a valid construction who asks for a semicolon in the end. Fortunately, language C has such construction, and it is... right, the **do-while**!</p>
<pre>
do
{
/* multiple commands here */
}
while( expression )
;
/* I expect a semicolon here, in order
to end the do-while instruction */
</pre>
<p>So we can rewrite our trace macro the right way, even being a funcky one:</p>
<pre>
#ifdef NDEBUG
#define MYTRACE( message ) /* nothing */
#else
#define MYTRACE( message ) \
do \
{ \
char buffer[500]; \
sprintf(buffer, \
"MYTRACE: %s(%d) %s\n", \
__FILE__, \
__LINE__, \
message); \
printf(buffer); \
} \
while( 0 )
#endif /* NDEBUG */
</pre>
<p>Using a do-while (with a false expression inside the test to execute the block just once) the if-else construction is allowed and working properly:</p>
<pre>
if( exploded() )
do
{
char buffer[500];
sprintf(buffer,
"MYTRACE: %s(%d) %s\n",
__FILE__,
__LINE__,
"Oh, my God");
OutputDebugString(buffer);
}
while( 0 );
else
do
{
char buffer[500];
sprintf(buffer,
"MYTRACE: %s(%d) %s\n",
__FILE__,
__LINE__,
"That's right");
OutputDebugString(buffer);
}
while( 0 );
</pre>
</section><hr/>
<span id="os_diferentes_erros_na_linguagem_c" title="Os diferentes erros na linguagem C"/></span>
<section id="section_os_diferentes_erros_na_linguagem_c">
<p class="title"><a href="2008-02.html#os_diferentes_erros_na_linguagem_c">#</a> Os diferentes erros na linguagem C</p>
<span class="title-heading">Caloni, 2008-02-15 <a href="coding.html">coding</a> <a href="ccpp.html">ccpp</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_os_diferentes_erros_na_linguagem_c')"><sup>[copy]</sup></a></span>
<p>Uma coisa que me espanta de vez em quando é o total desconhecimento por programadores mais ou menos experientes dos níveis de erros que podem ocorrer em um fonte escrito em C ou C++. Desconheço o motivo, mas desconfio que o fato de outras linguagens não terem essa divisão de processos pode causar alguma nivelação entre as linguagens e fazer pensar que o processo de compilação em C é como em qualquer outra linguagem.</p>
<p>Porém, para começar, só de falarmos em compilação já estamos pegando apenas um pedaço do todo, que é a geração de um programa executável em C. Tradicionalmente, dividimos esse processo em três passos:</p>
<p> 1. Preprocessamento</p>
<p> 2. Compilação</p>
<p> 3. Linkedição</p>
<p>Vamos dar uma olhada mais de perto em cada um deles e descobrir erros típicos de cada processo.</p>
<p>O preprocessamento é especificado pelos padrões C e C++, mas, tecnicamente, não faz parte da linguagem. Ou seja, antes que qualquer regra de sintaxe seja verificada no código-fonte, o preprocessamento já terá terminado.</p>
<p>Essa parte do processo lida com substituição de texto e diretivas baseadas em arquivos e símbolos. Por exemplo, a diretiva de preprocessamento mais conhecida</p>
<pre>
#include <stdio.h>
</pre>
<p>faz com que todo o conteúdo do arquivo especificado seja incluído exatamente no ponto onde for colocada essa diretiva. Isso quer dizer que, antes sequer do código-fonte ser compilado, todo o conteúdo desse header padrão estará no corpo do arquivo C.</p>
<p>Para evitar que o mesmo header seja incluído inúmeras vezes dentro da mesma unidade em C, causando assim erros de redefinição, existe outra diretiva muito usada para cercar esses arquivos públicos:</p>
<pre>
#ifndef __MEUHEADER__ // se já estiver definido, caio fora até endif
#define __MEUHEADER__
// conteúdo do header
#endif // __MEUHEADER__
</pre>
<p>Esse conjunto de duas diretivas, por si só, é capaz de gerar os mais criativos e bizarros erros de compilação em C. E estamos falando de erros que ocorrem antes que sequer seja iniciado o processo de compilação propriamente dito. Obviamente que os erros serão capturados durante a compilação, mas o motivo deles terem ocorrido foi um erro decorrente do processo de preprocessamento. Por exemplo, vamos supor que um determinado fonte necessita de uma declaração de função contida em meuheader.h:</p>
<pre>
#include "header-do-mal.h"
#include "meuheader.h"
int func()
{
meuheaderFunc();
}
</pre>
<p>Porém, num daqueles acasos da natureza, o header-do-mal.h define justamente o que não poderia definir jamais (obs.: e isso pode muito bem acontecer na vida real, se usamos definições muito comuns):</p>
<pre>
#ifndef __HEADERDOMAL__
#define __HEADERDOMAL__
// tirei header da jogada, huahuahua (risos maléficos)
#define __MEUHEADER__
#endif // __HEADERDOMAL__
</pre>
<p>Na hora do preprocessamento, o preprocessador não irá mais incluir o conteúdo dentro de header.h:</p>
<pre>
#ifndef __MEUHEADER__ // se já estiver definido, caio fora até endif
#define __MEUHEADER__
int meuheaderFunc(); // talvez alguém precise disso
#endif // __MEUHEADER__
</pre>
<p>Conseqüentemente, durante a compilação do código-fonte já preprocessado, sem a declaração da função meuheaderFunc, irá ocorrer o seguinte erro:</p>
<pre>
error C3861: 'meuheaderFunc': identifier not found
</pre>
<p>Isso em fontes pequenos é facilmente identificável. Em fontes maiores, é preciso ter um pouco mais de cuidado.</p>
<p>Após o processo de preprocessamento, de todos os arquivos indicados terem sido incluídos, de todas as macros terem sido substituídas, todas as constantes colocadas literalmente no código-fonte, temos o que é chamado unidade de compilação, que será entregue ao compilador, que, por sua vez, irá começar a análise sintática de fato, descobrindo novos erros que podem ou não (como vimos) ter a ver com a fase anterior. A figura abaixo ilustra esse processo, com algumas trocas conhecidas:</p>
<img src="img/os_diferentes_erros_na_linguagem_c_preprocessor.gif"/>
<p>Dica: quando o bicho estiver pegando, e tudo o que você sabe sobre linguagem C não estiver te ajudando a resolver um problema, tente gerar uma unidade de compilação em C e analisar sua saída. Às vezes o que é claro no código pode se tornar obscuro após o preprocessamento. Para fazer isso no VC++ em linha de comando, use o parâmetro /E.</p>
<p>Se você conseguir passar ileso para a fase de compilação, pode se considerar um mestre do preprocessamento. Por experiência própria, posso afirmar que a maior parte do tempo gasto corrigindo erros de compilação, por ironia do destino, não terá origem na compilação em si, mas no preprocessamento e linkedição. Isso porque o preprocessamento confunde muito o que vimos no nosso editor preferido, e a linkedição ocorre em uma fase onde não importa mais o que está dentro das funções, mas sim o escopo de nomes, um assunto um pouco mais vago do que a linguagem C.</p>
<p>Na compilação você irá encontrar geralmente erros bem comportados, como conversão entre tipos, else sem if e esquecimento de pontuação ou parênteses.</p>
<pre>
int cannotConvertError(const char* message)
{
int ret = message[0];
return ret;
}
int ret = cannotConvertError(3);
error C2664: 'cannotConvertError' : cannot convert parameter 1 from 'int' to 'const char *'
if( test() )
something;
something-else;
else
else-something;
error C2181: illegal else without matching if
while( (x < z) && func(x, func2(y) != 2 )
{
something;
}
error C2143: syntax error : missing ')' before '{'
</pre>
<p>Claro, não estamos falando de erros relacionados a templates, que são um pesadelo à parte.</p>
<p>Dica: nunca subestime o poder de informação do compilador e da sua documentação. Se o erro tem um código (geralmente tem), procure a documentação sobre o código de erro específico, para ter uma idéia de por que esse erro costuma ocorrer, exemplos de código com esse erro e possíveis soluções. Ficar batendo a cabeça não vai ajudar em nada, e com o tempo, você irá ficar sabendo rapidamente o que aconteceu.</p>
<p>Chegando na linkedição, onde a esperança reside, tudo pode vir por água abaixo. Isso porque você já espera confiante que tudo dê certo, quando, na verdade, um erro bem colocado pode fazer você desistir pra sempre desse negócio de programar em C.</p>
<p>As características mais desejadas para corrigir erros nessa fase são:</p>
<p> 1. Total conhecimento da fase do preprocessamento</p>
<p> 2. Total conhecimento da fase da compilação</p>
<p> 3. Total conhecimento de níveis de escopo e assinatura de funções</p>
<p>Os dois primeiros itens são uma maldição previsível que deve-se carregar para todo o sempre. Se você não consegue entender o que aconteceu nas duas primeiras fases, dificilmente irá conseguir seguir adiante com essa empreitada. O terceiro item significa que deve-se levar em conta as bibliotecas que estamos usando, headers externos (com dependências externas), conflitos entre nomes, etc.</p>
<p>Alguns erros mais encontrados aqui são as funções não encontradas por falta da LIB certa ou por LIBs desatualizadas que não se encaixam mais com o projeto, fruto de muitas dores de cabeça de manutenção de código. Essa é a parte em que mais vale a pena saber organizar e definir uma interface clara entre os componentes de um projeto.</p>
<p>Do ponto de vista técnico, é a fase onde o linker junta todos os arquivos-objeto especificados, encontra as funções, métodos e classes necessárias e monta uma unidade executável, como ilustrado pela figura abaixo.</p>
<img src="img/os_diferentes_erros_na_linguagem_c_linker.gif"/>
<p>Dica: uma LIB, ou biblioteca, nada mais é que uma coleção de arquivos-objeto que já foram compilados, ou seja, já passaram pelas duas primeiras fases, mas ainda não foram linkeditados. Muitas vezes é importante manter compatibilidade entre LIBs e os projetos que as usam, de forma que o processo de linkedição ocorra da maneira menos dolorosa possível.</p>
<p>É óbvio que, por ter passado pelas três fases de transformação de um código-fonte em um programa executável, não quer dizer que este programa está livre de erros. Os famigerados erros de lógica podem se disfarçar até o último momento da compilação e só se mostrarem quando o código estiver rodando (de preferência, no cliente).</p>
<p>Entre esses erros, os mais comuns costumam se aproveitar de macros, como max, que usa mais de uma vez o parâmetro, que pode ser uma chamada com uma função. A função será chamada duas vezes, mesmo que aparentemente no código a chamada seja feita uma única vez:</p>
<pre>
#define max(a, b) ( a > b ? a : b )
int z = max( func(10), 30 );
</pre>
<p>Um outro erro que já encontrei algumas vezes é quando a definição de uma classe tem um sizeof diferente do compilado em sua LIB, pela exclusão ou adição de novos membros. Isso pode (vai) fazer com que, durante a execução, a pilha seja corrompida, membros diferentes sejam acessados, entre outras traquinagens. Esses erros costumam acusar a falta de sincronismo entre os headers usados e suas reais implementações.</p>
<p>Enfim, na vida real, é impossível catalogar todos os erros que podem ocorrer em um fonte em C. Se isso fosse possível, ou não existiriam bugs, ou pelo menos existiria uma ferramenta para automaticamente procurar por esses erros e corrigi-los. Bom, existe o Lint.</p>
</section><hr/>
<span id="configurando_seus_projetos_no_visual_studio" title="Configurando seus projetos no Visual Studio (Multi-threaded Debug DLL /MT,"/></span>
<section id="section_configurando_seus_projetos_no_visual_studio">
<p class="title"><a href="2008-02.html#configurando_seus_projetos_no_visual_studio">#</a> Configurando seus projetos no Visual Studio (Multi-threaded Debug DLL /MT,</p>
<span class="title-heading">Caloni, 2008-02-21 <a href="coding.html">coding</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_configurando_seus_projetos_no_visual_studio')"><sup>[copy]</sup></a></span>
<p>Ao iniciar na arte da programação em C no Visual Studio eventualmente o programador irá querer testar seus programas rodando em outra máquina que não seja a de desenvolvimento, mandar uma versão beta para amigos, pra namorada e pro seu cachorro. Geralmente, por padrão, existem algumas dependências do programa compilado com uma DLL de runtime da versão do ambiente em que foi compilado o dito cujo, dificultando um pouco a distribuição do seu motherfucker-program.</p>
<p>Porém, seus "poroberemas se acabaram-se". Com o inovador configurador de projetos do Visual Studio, tudo o que você queria é possível, e ainda mais!</p>
<p>Nota do autor: isso não foi uma propaganda gratuita, apenas uma piada. Se fosse um verdadeiro anúncio das maravilhas do Visual Studio, eu agora estaria falando daquele tal código gerenciado e o tal do C++ CLI.</p>
<p>Inicialmente, se compilarmos um programa em Debug no Visual Studio 2005 teremos as seguintes dependências. kernel32 e msvcrx.dll (onde x é a versão do Visual Studio). A DLL kernel32 é nativa e sempre estará presente no Windows. Porém, a msvcr não. Ela veio junto com o pacote do Visual Studio, e se não for distribuída em outras máquinas, você não conseguirá rodar seu programa, pois isso gerará o erro de DLL não encontrada.</p>
<p>Bem, para resolver isso, a partir da IDE, temos que ir em Project, Properties, Configuration Properties, C/C++, Code Generation, Runtime Library.</p>
<p>Existem atualmente quatro tipos de runtime que você pode escolher:</p>
<ul><li>Multi-threaded (/MT). Versão Release que não depende de DLL.</li>
<li>Multi-threaded Debug (/MTd). Versão Debug que não depende de DLL.</li>
<li>Multi-threaded DLL (/MD). Versão Release que depende de DLL.</li>
<li>Multi-threaded Debug DLL (/MDd). Versão Debug que depende de DLL.</li>
</ul>
<p>Essas runtimes são chamada de multi-threaded porque antigamente existiam versões single-threaded dessas mesmas runtimes. Contudo, versões mais novas do Visual Studio só vêm com esse sabor mesmo.</p>
<p>Note que, por padrão, existem dois tipos de configuração em seu projeto: Debug (para testes) e Release (para distribuição). Convém não misturar configurações Debug com DLLs Release e vice-versa, a não ser que você tenha certeza do que está fazendo.</p>
<p>Pois bem. Para tirar a dependência da maldita DLL, tudo que temos que fazer é alterar a configuração, nesse caso Debug, de /MDd para /MTd. E recompilar.</p>
<p>E testar.</p>
<p>Além da dependência de DLLs, alguns casos especiais vão chiar por causa dos dados do manifesto embutidos no programa compilado. Por algum motivo que eu desconheço, o programa necessita que as DLLs estejam instaladas mesmo que no Dependency Walker não mostre nada. Nesses casos, uma arrancada do manifesto na versão Debug não fará mal algum.</p>
<p>Acho que esses são os únicos empecilhos iniciais para testar seu programa em outras máquinas. Sempre que ver o erro exibido no começo desse artigo, desconfie de alguma dependência que não está presente na máquina. Nessas horas, ter um Dependency Walker ou Dumpbin na mão vale ouro.</p>
</section><hr/>
<span id="codigos_de_entrevista_o_ponteiro_nulo" title="Códigos de entrevista - o ponteiro nulo"/></span>
<section id="section_codigos_de_entrevista_o_ponteiro_nulo">
<p class="title"><a href="2008-02.html#codigos_de_entrevista_o_ponteiro_nulo">#</a> Códigos de entrevista - o ponteiro nulo</p>
<span class="title-heading">Caloni, 2008-02-25 <a href="coding.html">coding</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_codigos_de_entrevista_o_ponteiro_nulo')"><sup>[copy]</sup></a></span>
<p>Bom, parece que o "mother-fucker" wordpress ferrou com meu artigo sobre o Houaiss. Enquanto eu choro as pitangas aqui vai um outro artigo um pouco mais simples, mas igualmente interessante.</p>
<blockquote>"Wanderley, tenho umas sugestões para teu blog.</blockquote>
<blockquote>A primeira:</blockquote>
<blockquote>Que tal analisar o código abaixo e dizer se compila ou não. Se não compilar, explicar porquê não compila. Se compilar, o que acontecerá e por quê."</blockquote>
<p>O código é o que veremos abaixo:</p>
<pre>
#include <stdio.h>
#include <stdlib.h>
void func()
{
*(int *)0 = 0;
return 0;
}
int main(int argc, char **argv)
{
func();
return 0;
}
</pre>
<p>Bem, para testar a compilação basta compilar. Porém, se estivermos em uma entrevista, geralmente não existe nenhum compilador em um raio de uma sala de reunião senão seu próprio cérebro.</p>
<p>E é nessas horas que os entrevistadores testam se você tem um bom cérebro ou um bom currículo.</p>
<p>Por isso, vamos analisar passo a passo cada bloco de código e entender o que pode estar errado. Se não encontrarmos, iremos supor que está tudo certo.</p>
<pre>
#include <stdio.h>
#include <stdlib.h>
</pre>
<p>Dois includes padrões, ultranormal, nada de errado aqui.</p>
<pre>
void func()
{
*(int *)0 = 0;
return 0;
}
</pre>
<p>Duas ressalvas aqui: a primeira quanto ao retorno da função é void, porém a função retorna um inteiro. Na linguagem C, isso funciona, no máximo um _warning_ do compilador. Em C++, isso é erro brabo de tipagem.</p>
<p>A segunda ressalva diz respeito à linha obscura, sintaticamente correta, mas cuja semântica iremos guardar para o final, já que ainda falta o main para analisar.a</p>
<pre>
int main(int argc, char **argv)
{
func();
return 0;
}
</pre>
<p>A clássica função inicial, nada de mais aqui. Retorna um int, e de fato retorn. Chama a função func, definida acima.</p>
<p>A linha que guardamos para analisar contém uma operação de casting, atribuição e deferência, sendo o casting executado primeiro, operador unário que é, seguido pelo segundo operador unário, a deferência. Como sempre, a atribuição é uma das últimas. Descomprimida a expressão dessa linha, ficamos com algo parecido com as duas linhas abaixo:</p>
<pre>
int* p = (int*) 0;
*p = 0;
</pre>
<p>Não tem nada de errado em atribuir o valor 0 a um ponteiro, que é equivalente ao define NULL da biblioteca C (e C++). De acordo com a <a href="http://www.gnu.org/software/libc/manual/html_node/Null-Pointer-Constant.html#Null-Pointer-Constant">referência GNU</a>, é recomendado o uso do define, mas nada impede utilizar o 0 "hardcoded".</p>
<p>Porém, estamos escrevendo em um ponteiro nulo, o que com certeza é um comportamento não-definido de conseqüências provavelmente funestas. O ponteiro nulo é um ponteiro inválido que serve apenas para marcar um ponteiro como inválido. Se escrevermos em um endereço inválido, bem, não é preciso ler o padrão para saber o que vai acontecer =)</p>
<p>Alguns amigos me avisaram sobre algo muito pertinente: dizer que acessar um ponteiro nulo, portanto inválido, é errado e nunca deve ser feito. Como um ponteiro nulo aponta para um endereço de memória inválido, acessá-lo irá gerar uma exceção no seu sistema operacional e fazer seu programa capotar. Um ponteiro nulo é uma maneira padrão e confiável de marcar o ponteiro como inválido, e testar isso facilmente através de um if. Mais uma vez: ponteiros nulos apontando para um endereço de memória inválido (o endereço 0) nunca devem ser acessados, apenas atribuído a ponteiros.</p>
<p>Em código. Isso pode:</p>
<pre>
int* p = 0; // atribuindo nulo a um ponteiro
int* p2 = p; // isso também pode
</pre>
<p>Isso não pode:</p>
<pre>
*p = 15; // nunca acessar ponteiros nulos
int x = *p; // isso também não pode, ler de um ponteiro nulo
</pre>
<p>Dito isso, me sinto melhor =)</p>
</section><hr/>
<span id="conversor_de_houaiss_para_babylon_parte_1" title="Conversor de Houaiss para Babylon - parte 1"/></span>
<section id="section_conversor_de_houaiss_para_babylon_parte_1">
<p class="title"><a href="2008-02.html#conversor_de_houaiss_para_babylon_parte_1">#</a> Conversor de Houaiss para Babylon - parte 1</p>
<span class="title-heading">Caloni, 2008-02-27 <a href="coding.html">coding</a> <a href="projects.html">projects</a><a href="2008-02.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_conversor_de_houaiss_para_babylon_parte_1')"><sup>[copy]</sup></a></span>
<p>Este artigo é sobre desmontar e montar novamente. Iremos descobrir como as entradas do dicionário Houaiss eletrônico estão gravadas em um primeiro momento, para depois remontarmos essa informação de maneira que ela possa ser usada em outro dicionário de uso mais flexível, o Babylon. Ou seja, este não é um guia de vandalismo. Estava apenas querendo usar um dicionário de qualidade excelente em outro dicionário cuja interface é muito boa.</p>
<p>Considero o Houaiss o melhor dicionário da atualidade, uso todo santo dia e tenho todo o respeito por ele. Possuo uma cópia legalizada exatamente por isso. Além, é óbvio, pelo escandaloso cinismo que seria se eu, desenvolvedor de software, pirateasse os que utilizo. Porém, acredito que tudo tenha um limite: respeito os direitos de quem desenvolve o programa se o programa se dá ao respeito de ser pago. Quer dizer, eu realmente uso muito esse dicionário, e ele é útil para mim. Logo, nada mais justo do que adquiri-lo como manda a lei.</p>
<p>Assim como adquiri o Houaiss, também comprei o Babylon, um programa-dicionário, cuja interface permite buscar o significado das palavras lidas no computador simplesmente clicando nelas. A qualidade de seu dicionário português embutido é medíocre, mas o que ele ganha mesmo é em sua interface fácil para acessar palavras. Exatamente por faltar um dicionário em português de peso no Babylon, e eu ter adquirido outro muito melhor, quis que ambos funcionassem juntos, ou seja, acesso o Babylon e tenho o resultado adicional desse meu dicionário tupiniquim.</p>
<p>O Babylon possui um mecanismo para criação de dicionários chamado Babylon Builder. É muito simples e fácil de usar (além de ser gratuito). Sabendo que possuo ambas as licenças desses dois programas me sinto mais aliviado em tentar desencriptar a base de dados do primeiro para construir um dicionário para o segundo, e assim realizar meu sonho de consumo: um Babylon com um dicionário de peso!</p>
<img src="img/conversor_de_houaiss_para_babylon_parte_1_houaiss_license.png"/>
<p>É necessário que, na hora da instalação, seja escolhida a opção de copiar os arquivos para o disco. Estarei utilizando o path padrão de um Windows em português, que é "C:\Arquivos de Programas\Houaiss".</p>
<img src="img/conversor_de_houaiss_para_babylon_parte_1_houaiss_install.png"/>
<p>A estrutura de diretórios interna da instalação é bem simples:</p>
<ul><li>Raiz. Arquivos de ajuda, desinstalador, executável principal, etc.</li>
<li>Quadros. Figuras com conhecimentos gerais, como calendários, signos, línguas mais faladas, etc.</li>
<li>Dicionario. Provavelmente onde está todo o dicionário, cerca de 120 MB.</li>
</ul>
<p>Se analisarmos o conteúdo dos arquivos dentro da pasta Dicionario vamos descobrir que ele se parece com "garbage nonsense", apesar de existir um certo padrão. O padrão revela que pode se tratar de uma criptografia muito simples, talvez até um simples XOR.</p>
<pre>
for %i in (*.*) do type %i | less
</pre>
<img src="img/conversor_de_houaiss_para_babylon_parte_1_cmd.gif"/>
<p>Sabendo que o conteúdo do dicionário está em arquivos localizados no disco, e que teoricamente o programa não deve copiar todo o conteúdo para a memória, iremos depurar o processo do dicionário de olho nas chamadas da função ReadFile quando clicarmos em uma definição de palavra.</p>
<pre>
windbg -pn houaiss2.exe
0:001> bp kernel32!ReadFile "dd @$csp L6" $$ Dando uma olhada nos parâmetros
g
</pre>
<p>Ao clicar na definição de "programa-fonte", o breakpoint é ativado:</p>
<pre>
0012fa70 0040a7a9 00000200 08bbf1d0 00000200 0012fa80 0012fa88 00000000
eax=0012fa88 ebx=00000200 ecx=00000200 edx=08bbf1d0 esi=08bbf1d0 edi=00000200
eip=7c80180e esp=0012fa70 ebp=0012facc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
kernel32!ReadFile:
7c80180e 6a20 push 20h
$$ dados acima:
$$ - O buffer de saída é 08bbf1d0
$$ - O número de bytes lidos é 200
0:000> db 08bbf1d0 L80
08bbf1d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf1e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf1f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf200 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf210 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf220 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf230 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
08bbf240 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:000> bp /1 @$ra "db 08bbf1d0 L80"
0:000> g
08bbf1d0 1f 65 67 64 5c 67 56 62-56 22 5b 64 63 69 5a 02 .egd\gVbV"[dciZ.
08bbf1e0 ff 38 68 23 62 23 02 ff-59 70 51 5e 15 68 4d 4d .8h#b#..YpQ^.hMM
08bbf1f0 72 02 ff 49 5e 63 5b 02-ff 2f 65 67 64 5c 67 56 r..I^c[../egd\gV