paulo1205
(usa Ubuntu)
Enviado em 22/08/2019 - 15:51h
Outro aspecto, que vale no caso de
typedefs que envolvem
struct ou
union: de modo geral, o uso de apelidos por meio de
typedef ocorre quando você deseja esconder do usuário a existência da
struct ou
union, para que ele não tente manipular diretamente os campos que formam tais tipos estruturados. Por outro lado, quando se espera ou deseja que o usuário veja ou manipule os campos individualmente, evita-se o uso do
typedef, expondo-lhe que ele tem de lidar com uma estrutura ou união.
De modo mais geral, mais do que esconder apenas campos de tipos compostos,
typedefs também podem ser usandos para definir detalhes de implementação que usem até mesmo tipos nativos.
Exemplos:
• O tipo
FILE é declarado em <stdio.h> com algo parecido com “
typedef struct _IO_FILE FILE;”. Você, como programador, não é exposto em momento nenhum ao conteúdo da estrutura
_IO_FILE, e todas as interações do seu programa com objetos desse tipo serão feitas por meios de ponteiros para
FILE.
• O tipo
struct tm de <time.h> é exposto como estrutura para o usuário, porque é esperado que o usuário deseje examinar e manipular campos individuais da representação de data e hora que um dado desse tipo armazena.
• No mesmo <time.h>, o tipo
time_t é definido como um
typedef. Usuários de UNIX e outros sistemas compatíveis com POSIX estão acostumados a tratar
time_t como um apelido de
long ou
long long, mas o padrão do C não obriga a que seja assim, limitando-se a dizer que deve ser um tipo aritmético (i.e. uma outra implementação pode escolher usar números de ponto flutuante, também). Ser inteiro ou de ponto flutuante é um detalhe de implementação, que fica escondido do usuário final pelo uso do
typedef. Nós, usuários, deveríamos ter sempre o cuidado de não assumir que é um tipo ou outro, e usar funções da biblioteca ou conversões explícitas quando quisermos usá-los de uma maneira específica.
// Código inseguro.
time_t t1, t2;
t1=time(NULL);
/* Realiza ação demorada. */
t2=time(NULL);
printf("Início: %d. Fim: %ld. Duração: %lld\n", t1, t2, t2-t1);
// Essas conversões de printf() podem gerar alarmes durante a compilação e imprimir lixo durante a execução.
// Versão um pouco mais segura, com conversão explícita.
time_t t1, t2;
t1=time(NULL);
/* Realiza ação demorada. */
t2=time(NULL);
printf("Início: %d. Fim: %ld. Duração: %lld\n", (int)t1, (long)t2, (long long)t2-(long long)t1);
// Elimina alarmes durante a compilação, mas ainda pode imprimir lixo, se houver truncamento ou arredondamento em alguma conversão forçada de tipos.
// Versão mais robusta, usando difftime(), que devolve sempre um valor do tipo double, e uma referência de tempo (t0).
const time_t t0=(time_t)0;
time_t t1, t2;
t1=time(NULL);
/* Realiza ação demorada. */
t2=time(NULL);
printf("Início: %0.15g. Fim: %0.15g. Duração: %0.15g\n", difftime(t1, t0), difftime(t2, t0), diffime(t2, t1));
// Todos os valores relativos a outro time_t, e não haverá erros por conversão forçada, mas inadequada, de tipos.
... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)