segunda-feira, 10 de outubro de 2022

Como fazer algo de forma errada, e aprendendo a certo

Algum tempo atrás eu vi um código muito errado, mas muito errado mesmo, e de muitas formas diferentes, que me deixou chocado.

Não vou colocar o código original aqui, mas vou refazer usando o mesmo algoritmo usando outra situação.

Situação nova: Em alguns países o ponto é usado para separar as decimais e a vírgula é usada a cada 3 casas não decimais, mas em outros, como o Brasil, é ao contrário.

Por exemplo, nos EUA a velocidade da luz seria escrita como 1,079,252,848.8 Km/h (Eu sei, eles não usam Km/h, e sim aquela medida obsoleta de milhas por hora.), e no Brasil este número seria representado assim: 1.079.252.848,8 Km/h.

E no caso seria necessária uma função que fizesse esta troca e colocasse o "c = " na frente. A função seria escrita deste modo abaixo com o algoritmo que vi:

void Troca( char *texto )
{
    char temp[50] = "c = " ;
    int i ;

    for( i = 0 ; i < 50 ; i++ )
        if( texto[i] == ',')
            texto[i] = '.' ;
        else if( texto[i] == '.')
            texto[i] = ',' ;
 
    strcat(temp,texto) ;
    strcpy(texto,temp) ;
}

Agora analise o código. Veja quantos erros tem nele. Imagine as consequências destes erros. Imagine como resolvê-los. Depois continue a ler.

quarta-feira, 14 de julho de 2021

Um pouco de a, b, c e main da programação modular.

Este é um mais um caso no qual uma explicação para uma pessoa nas redes sociais fica tão boa que vira um artigo no blog. O texto vai abaixo:
 
Um header, um arquivo .h, NUNCA deverá gerar código ou dados. Pode definir variáveis, mas somente como extern. Pode ter protótipos de funções, definição de tipos, enum, macros, mas NUNCA deve definir uma variável alocando espaço em memória ou ter o código de uma função.
 
Os .c e .cpp são onde estão as definições de variáveis e código.
 
Digamos que vamos fazer um programa que é dividido em partes. A parte "a" que é usada pela "b" e "c", e a parte "main" seria o programa principal e depende diretamente das partes "b" e "c".
 
Então teríamos os arquivos a.h, a.c, b.h, b.c, c.h, c.c e main.c.
 
a.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <> e a.h entre aspas.
 
a.h
 
Contém os protótipos das funções escritas em a.c, e em extern as variáveis de a.c que precisam ser vistas por outros módulos.
 
b.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <>, e a.h e b.h entre aspas.
 
b.h
 
Contém os protótipos das funções escritas em b.c, e em extern as variáveis de b.c que precisam ser vistas por outros módulos.
 
c.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <>, e a.h e c.h entre aspas.
 
c.h
 
Contém os protótipos das funções escritas em c.c, e em extern as variáveis de c.c que precisam ser vistas por outros módulos.
 
main.c
 
Já que este depende diretamente somente de b.c e c.c, então inclui alguns headers padrão entre <>, e b.h e c.h entre aspas. A dependência do módulo "a" se faz indiretamente.
 
O Makefile deste projeto fica aproximadamente assim.
 
programa: main.c b.o c.o
    cc -s -O3 -o programa main.c a.o b.o c.o
 
a.o: a.c a.h
    cc -O3 -c a.c
 
b.o: b.h b.c a.o
    cc -O3 -c b.c
 
c.o: c.h c.c a.o
    cc -O3 -c c.c
 
E assim só os módulos necessários é que serão compilados. Se editar o main.c, só ele será compilado. Se editar o a.h ou o a.c, tudo será compilado, pois tudo depende diretamente ou indiretamente deles. Se editar b.c ou b.h, só b.c e main.c serão compilados.
 
Acho que dá para fazer um Makefile mais detalhado, de modo que nem main.c seja compilado do zero se os headers não forem modificados.

Aqui já terminou o texto da rede social, mas tenho este texto falando que vale para ler, pois justamente é sobre como variáveis são alocadas em memória, sobre extern, e sobre funções e variáveis locais a módulos.

sábado, 26 de setembro de 2020

Rodando vários navegadores e programas isolados por UIDs diferentes

O que vou ensinar aqui se aplica para todos (ou quase) os sistemas que rodam X Windows, ou seja, sistemas Unix-Like (FreeBSD, Linux etc), e VMS (Não uso um faz décadas.), e talvez mais alguns outros sistemas (Ouvi falar de X Windows em Mac OS X, que é um Unix-Like derivado do FreeBSD).

O X Windows é um sistema em rede, isto é, um programa em um computador pode se conectar ao ambiente gráfico de outro computador (ou terminal X Windows).

Como na época em que ele surgiu o poder computacional era muito menor do que o atual, surgiram terminais que rodavam o X Windows. Assim os programas rodariam em um grande computador e a interface gráfica dele estaria no terminal em separado.

Também existiam programas que transformavam PCs em terminais X Windows.

Aqui tem um ponto que dá nó na cabeça de muita gente. Quem roda o X Windows é o servidor, pois serve janelas, teclado, mouse etc, em suma, fornece uma interface com o usuário, para os programas que os requisitam, é que são clientes. Assim os computadores grandes, os mainframes, eram os clientes de X Windows e os terminais eram os servidores de X Windows

Assim o navegador (por exemplo) é um cliente de X Windows que se conecta a um servidor de X Windows, e como atualmente se tem muito mais poder computacional do que se tinha antigamente, ambos normalmente rodam no mesmo computador.

Quando eu fiz um curso de programação em X Windows em 1991 usávamos uma única DEC VAX Station que tínhamos disponível para toda a turma, e um DEC VAX por vários terminais seriais (acho que compatíveis com o DEC VT-100 conectados por RS232). Rodávamos os programas neste DEC VAX, mas pedíamos que as janelas fossem feitas an DEC VAX Station. Assim a turma toda poderia usar a estação gráfica, mas sem ninguém realmente sentar diante dela.

Aliás, uma das máquinas rodava DEC Ultrix como sistema operacional (acho que era a VAX Station, mas posso estar errado) e o outro rodava DEC VMS como sistema operacional. Ou seja, cliente e servidor não precisam usar o mesmo sistema operacional.

Mesmo o mundo tendo mudado muito, este recurso de rede do X Windows ainda existe, e ainda é usado, mas bem menos. E por questões de segurança, ele normalmente fica desativado.

Eu me lembro do X Windows suportar HDR naquela época (1991), mesmo com este termo ainda não existindo. Ele suportava 16 bits por cor, 48 bits por pixel, mesmo com quase todos os hardwares atuais (2020) não suportando isso. (Descobri o suporte a 16 bits por cor por pixel quando o meu degradê de cinza só mostrava preto. rsrs)

Eu uso este recurso do X Windows no meu computador, e separo um usuário para o jogo do Minetest. Aliás, agora tenho vários usuários para este jogo, já que criei o canal Não É Minecraft. Mas eu já fazia antes. Eu tenho um usuário que só uso uma vez por ano, pois é o usuário para fazer Imposto de Renda. Eu tenho um com o login do meu antigo trabalho, e assim mantinha um navegador com o Trello, o e-mail do trabalho etc, abertos completamente isolados dos pessoais.

Os usos para isto são muito amplos.

Outro uso que fiz foi quase 20 anos atrás. Eu pensava no reaproveitamento dos antigos 486 no tempo que eu trabalhei na Biblioteca Nacional. Eu rodei um Netscape (navegador web) no Pentium 4, que era a minha estação de trabalho, mas a janela era apresentada no 486 antigo que não tinha condições de rodar um navegador. A ideia era rodar os navegadores em um Pentium 4, e cada um deles fazendo a sua janela num 486 diferente. Em suma, eu estava transformando os 486 em terminais X Windows.

 

Agora vamos ao que interessa a muita gente

Para começar, o X Windows tem que ser iniciado com suporte à conexão remota. No meu sistema eu faço assim:

startx -- -listen tcp

Isto diz ao X Windows, em seu momento de inicialização, que ele tem que escutar a porta TCP dele, caso contrário ele não a abrirá (creio eu).

No seu sistema (se não for um FreeBSD) pode ser diferente. Se isto não funcionar no seu sistema pesquise é nele, e pode colocar nos comentários qual foi o sistema e qual foi a solução adotada.

E ainda tem que autorizar quem pode fazer requisições a ele, e isto é com o comando xhost, como abaixo:

xhost +localhost

Aqui estamos dizendo que é para aceitar todas as conexões da máquina local. Então qualquer usuário da máquina local pode fazer janelas nela, e não só o usuário que iniciou o X Windows na máquina.

Em uma janela shell, de linha de comando, pode-se fazer login num outro usuário, como o exemplo abaixo:

exec login fulano

E depois fornecer a devida senha. (Nota: fulano é o usuário no qual vai se fazer login neste exemplo.)

Podem consultar o manual do exec e do login para saber mais detalhes.

Agora tem que avisar aos programas que este usuário rodar qual é o display dele, onde tem que ser feitas as janelas e a interface com o usuário.

Se usa uma csh como shell de linha de comando, isto pode ser feito da forma abaixo.

setenv DISPLAY localhost:0.0

É basicamente colocar a informação numa variável do ambiente que os programas receberão.

Se sempre vai usar este usuário desta forma, sem ele realmente fazer login diretamente no computador, coloque a linha acima no arquivo .login que fará com que esta configuração sempre seja feita quando fizer o exec login citado acima.

Se usa uma Bourne Shell (sh) ou uma derivada (como a bash), faça da seguinte forma (Nota: Não testei, mas acho que deve funcionar.).

set DISPLAY='localhost:0.0'

export DISPLAY

E o arquivo de configuração será o .profile.

Podem perguntar e fazer acréscimos nos comentários.


terça-feira, 14 de janeiro de 2020

Preparando um novo HD no FreeBSD

Neste final de semana preparei um novo HD externo para funcionar no FreeBSD. Mas, mesmo sendo externo, quase tudo que vou falar aqui funciona para internos também.

Esta preparação incluiu testar o HD. Eu acho que antes de começar a usar um HD deve-se fazer testes nele. Eu falo por experiência, pois vi HDs novos com problemas, e que não passaram nos testes.

Aliás, parte do que vou falar aqui, especialmente o início, também vale para outros sistemas operacionais, especialmente os Unix Like. E estes testes podem ser feitos num sistema diferente do sistema final onde vai ser instalado o HD.

segunda-feira, 26 de agosto de 2019

Uma pequena vingança

Isto aconteceu por volta de 1991 na faculdade.

Um determinado computador num laboratório que eu frequentava tinha duas placas de vídeo, sendo que uma era especializada para tratamento de imagens. O monitor dela funcionava na mesma frequência das TVs, ou seja, 60 Hz na vertical e 15750 Hz na horizontal.

Em um dia uma garota que não sabia nada de eletrônica, de como um monitor funcionava etc, (não era culpa dela, pois era no Departamento de Geografia) estava usando este computador, mas eu sabia bem devido meus estudos de eletrônica.

Meu ouvido é (ou era) muito sensível a agudos, exceto o 15750 Hz e vizinhança próxima, pois toda TV faz este apito agudo. Eu escutava, mas não me incomodava.

Ela toda hora resetava o computador, o que fazia o segundo monitor sair de sincronismo na frequência horizontal. Isto me incomodava muito. Eu ficava escutando o barulho agudo fora de frequência certa.

Ela dava boot no computador, chamava o programa, carregava uma imagem, e aí tentava ajustar o monitor, e eu escutando o assovio por este tempo todo.

Aí começava outro martírio meu. Ela não virava o ajuste devagar. Ela ia rapidamente de ponta a ponta, fazendo toda a gama de assovios que aquele oscilador, com a alta tensão do monitor, eram capazes de fazer. E depois de várias tentativas, o comparador de fase do monitor conseguia travar na frequência certa. Mas o oscilador estava ajustado para operar naturalmente em outra frequência, pois dependia de onde ela parasse durante o ajuste.

Eu ainda tinha a certeza, devido aos meus conhecimentos de eletrônica, que ela estava fazendo errado. Isto também me incomodava.

Depois de um tempo de uso ela resetava o computador de novo. E começava tudo de novo. E de novo. E de novo. Etc.

Foram vários ciclos assim. Sei que tem gente que teria surtado, que teria passado bronca nela etc, mas usei outro método.

Em um dos resets que ela deu eu me levantei, fui até o monitor, e comecei a ajustar o monitor de ouvido até que chegasse o mais perto possível da frequência que deveria oscilar, 15750 Hz. Enquanto eu fazia isto ela falou:

- Espere eu colocar uma imagem.

Quando ela colocou a imagem o monitor funcionou direitinho. Em sincronismo. Ela ficou me olhando.

Ela devia estar pensando "Como eu tenho dificuldade de fazer isto com imagem, e ele fez sem imagem no monitor?".

Fiz um sinal do tipo "Ok" com a cabeça, e voltei para o meu canto da sala, onde eu estava trabalhando.

Como vingança, não contei como fiz isto.

Acho que eu tive a vantagem de perceber o que me incomodava, como me incomodava, porque me incomodava, e sabia como resolver. Nem todo mundo tem esta vantagem em muitas situações de incômodo. Acredito que inclusive eu em alguns casos.

domingo, 2 de junho de 2019

Ordem de testes

No outro dia me deparei com um código mais ou menos assim:

int f( bool a, int b, int c, int d )
{
  if( b > c && a == false )
    return 1 ;
  else if( c < d && a == false )
    return 2 ;
  else if( a == true )
    return 3 ;
  else
    return 4 ;
}


Olhem para ele e digam o que está errado, o que pode ser melhorado. Depois de pensar nele, cliquem em "Mais informações" para ver o resto da discussão.

quinta-feira, 7 de fevereiro de 2019

LEDs queimam? Normalmente não, mas...

Apesar de ter criado este blog para falar principalmente de computação, ele não é restrito a isto. Ele é para falar de nerdices, e como sou um nerd antigo, comecei pela eletrônica na minha adolescência, tal como muitos nerds da minha geração. Na década de 1980 ainda era caro ter um computador em casa.

Este texto é sobre LED (Light Emitting Diode - Diodo Emissor de Luz). Ele é um semicondutor que emite luz quando devidamente polarizado, com uma corrente passando por ele da forma direta. Como retificador ele é muito ruim, apesar de ser um diodo semicondutor. Mas é muito bom, eficiente e durável para pequenas fontes de luz. E nos últimos anos, com as melhorias tecnológicas, passaram a ser usados em sinais de trânsito (Uso que eu imaginava desde a década de 1980.), iluminação etc.

Uma das características dos LEDs é uma luz de espectro estreito, com uma cor bem definida, o que é bom para luzes indicadoras, sinais de trânsito, iluminações coloridas etc. LEDs brancos na realidade não existem. Já tratei sobre luz branca usando LEDs aqui.

Mas o assunto aqui é "LEDs queimam?".

quarta-feira, 22 de agosto de 2018

C: Imprimindo percentual

É muito comum uma saída com percentual, e com o símbolo '%' depois do número, mas a printf() usa este caractere para formatação de saída. Qual é a solução?

Já vi muitas vezes sendo resolvido assim:

printf( "%f %s\n",pecent,"%" );

Isto está errado, realmente muito errado. Mas por que está errado, e qual é a solução?

C: Ponteiros para função

Em C existe um recurso muito incomum. Ponteiros para funções. Não sei de outra linguagem que tenha isto, além de C e C++. Não sei se outras linguagens derivadas do C, como Java e C# tem isto (Podem responder nos comentários). Dá para fazer em Assembly, mas isto não conta.

Qual é o uso disto? Depende do caso. É possível passar uma função, aliás, o endereço de uma função, como parâmetro para outra função, para que ela a chame dentro dela, por exemplo.

Então vamos a um exemplo prático:

terça-feira, 17 de abril de 2018

Testando os quadrados por somas de ímpares de Pitágoras e otimização

Eu vi a seguinte publicação no Facebook e pensei em testar computacionalmente até um limite grande. Para quem não quer, ou não tem como, acessar o Facebook, reproduzo a imagem abaixo:


Quando escrevia o programa notei que estava fazendo errado, que o código iria gastar tempo demais inutilmente, então reescrevi o código resolvendo isto.

A primeira versão do programa foi:

#include        <stdio.h>

int     main()
{
        register unsigned long long numero, impar, soma ;

        for( numero = 1 ; numero <= 1000000000 ; numero++ )
        {
                register int    i ;

                for( i = numero, impar = 1, soma = 0 ; i ; i--, impar +=2 )
                        soma += impar ;

                if( soma != numero*numero )
                        printf( "Falhou para o numero %llu\n",numero );
        }
}


Esta é a descrição literal do problema, mas nem sempre a descrição literal é a melhor a ser implementada. O que está errado neste programa?