Obra: Programar em C/Mais sobre variáveis
typedef instrução typedef serve para definir um novo nome para um certo tipo de dados intrínseco da linguagem ou definido pelo usuário por exemplo se fizéssemos seguinte declaração typedef unsigned int uint poderíamos declarar variáveis inteiras sem sinal unsigned int da seguinte maneira uint numero equivalente unsigned int numero como exemplo vamos dar nome de inteiro para tipo int typedef int inteiro como se vê typedef cria uma espécie de apelido para um tipo de dados permitindo que esse tipo seja referenciado através desse apelido em vez de seu identificador normal um dos usos mais comuns de typedef abreviar declaração de tipos complexos como structs ou estruturas veja este exemplo struct pessoa char nome int idade struct pessoa joao observe que para declarar variável joao precisamos escrever palavra struct podemos usar typedef para abreviar essa escrita typedef struct char nome int idade pessoa pessoa joao um apelido de tipo utilizado com bastante frequência embora não costumemos dar por isso tipo file usado nas funções de entrada saída de arquivos typedef struct char int char int int int int char file então quando declaramos algo como file fp na verdade estamos declarar um ponteiro para uma estrutura que será preenchida mais tarde pela função fopen atenção você não deve tentar manipular uma estrutura do tipo file sua composição foi apresentada apenas como exemplo ou ilustração sizeof operador sizeof usado para se saber tamanho de variáveis ou de tipos ele retorna tamanho do tipo ou variável em bytes como uma constante devemos usá lo para garantir portabilidade por exemplo tamanho de um inteiro pode depender do sistema para qual se está compilando sizeof um operador porque ele substituído pelo tamanho do tipo ou variável no momento da compilação ele não uma função sizeof admite duas formas sizeof sizeof nome_do_tipo se quisermos então saber tamanho de um float fazemos sizeof float se declararmos variável como float quisermos saber seu tamanho faremos sizeof operador sizeof também funciona com estruturas uniões enumerações outra aplicação importante do operador sizeof para se saber tamanho de tipos definidos pelo usuário seria por exemplo uma tarefa um tanto complicada de alocar memória para um ponteiro para estrutura ficha_pessoal criada na primeira página desta aula se não fosse uso de sizeof veja exemplo typedef struct const char nome const char sobrenome int idade pessoa int main void pessoa joaquim joaquim malloc sizeof pessoa joaquim nome joaquim joaquim sobrenome silva joaquim idade outro exemplo include include int main void char nome nome malloc sizeof char sprintf nome wikibooks printf site http pt org nome imprime site http pt wikibooks org sentença abaixo não funciona pois sizeof substituído pelo tamanho de um tipo em tempo de compilação const char frase frase wikibooks eh legal printf eu acho que tamanho da string frase sizeof frase conversão de tipos as atribuições no tem seguinte formato destino origem se destino origem são de t
conversão de tipos as atribuições no tem seguinte formato destino origem se destino origem são de tipos diferentes compilador faz uma conversão entre os tipos mas nem todas as conversões são possíveis primeiro ponto ser ressaltado que valor de origem convertido para valor de destino antes de ser atribuído não contrário em cada tipo básico ocupa uma determinada porção de bits na memória logo conversão entre tipos nem sempre algo nativo da linguagem por assim dizer há funções como atol atof que convertem string em inteiro longo long int string em double respectivamente mas em muitos casos possível usar casting importante lembrar que quando convertemos um tipo numérico para outro nós nunca ganhamos precisão nós podemos perder precisão ou no máximo manter precisão anterior isto pode ser entendido de uma outra forma quando convertemos um número não estamos introduzindo no sistema nenhuma informação adicional isto implica que nunca vamos ganhar precisão abaixo vemos uma tabela de conversões numéricas com perda de precisão para um compilador com palavra de bits de para informação perdida unsigned char char valores maiores que são alterados short int char os bits de mais alta ordem int char os bits de mais alta ordem long int char os bits de mais alta ordem long int short int os bits de mais alta ordem long int int os bits de mais alta ordem float int precisão resultado arredondado double float precisão resultado arredondado long double double precisão resultado arredondado casting conversão manual se declararmos sabemos que resultado ou seja divisão de dois números inteiros dá um número real porém resultado em será inteiro isso acontece porque as constantes são do tipo inteiro operações com inteiros tem resultado inteiro mesmo ocorreria em se forem inteiros se declararmos int resultado será mesmo que declarássemos float resultado continua ser mas desta vez para fazer divisão que resulte número real necessário fazer cast para um tipo de ponto flutuante float float nesse caso ou convertido para float outro número continua como inteiro mas ao entrar na divisão com um float ele convertido automaticamente para float divisão feita depois atribuída variável em poucas palavras casting colocar um tipo entre parênteses antes da atribuição de uma variável forma geral para cast tipo variável tipo expressão tipo variavel_origem mas existem umas conversões automáticas int void float f_var double d_var long double l_d_var f_var d_var l_d_var d_var d_var f_var float convertido em double l_d_var d_var f_var float double convertidos em long double return l_d_var repare que conversão feita de menor para maior possível fazer conversão ao contrário de um tipo com mais bits para um com menos bits isso truncar nesse caso cast explícito necessário assim um número float ao ser convertido para int deverá ser cortado ficando inteiro se converter long para short os bits mais significativos são perdidos na conversão operador cast também bastante utilizado para estruturar áreas de
icativos são perdidos na conversão operador cast também bastante utilizado para estruturar áreas de estoque temporários buffer seguir um pequeno exemplo include typedef struct estruturar char char int main char buffer estruturar struct estruturar buffer char char malloc printf getchar return atributos das variáveis estes modificadores como próprio nome indica mudam maneira com qual variável acessada modificada alguns dos exemplos usam conceitos que só serão abordados nas seções seguintes então você pode deixar esta seção para depois se assim desejar const modificador const faz com que variável não possa ser modificada no programa como nome já sugere útil para se declarar constantes poderíamos ter por exemplo const float pi podemos ver pelo exemplo que as variáveis com modificador const podem ser inicializadas mas pi não poderia ser alterado em qualquer outra parte do programa se programador tentar modificar pi compilador gerará um erro de compilação outro uso de const aliás muito comum que outro evitar que um parâmetro de uma função seja alterado pela função isto muito útil no caso de um ponteiro pois conteúdo de um ponteiro pode ser alterado por uma função para proteger ponteiro contra alterações basta declarar parâmetro como const include int sqr const int num int main void int int sqr int sqr const int num return num num no exemplo num está protegido contra alterações isto quer dizer que se tentássemos fazer num dentro da função sqr compilador daria uma mensagem de erro volatile modificador volatile diz ao compilador que variável em questão pode ser alterada sem que este seja avisado isto evita bugs que poderiam ocorrer se compilador tentasse fazer uma otimização no código que não segura quando memória modificada externamente digamos que por exemplo tenhamos uma variável que bios do computador altera de minuto em minuto um relógio por exemplo seria importante que declarássemos esta variável como volatile um uso importante de variáveis volatile em aplicações que acessam dados relativos ao hardware por exemplo enviam ou lêem dados de uma porta serial ou paralela não deve ser usado em aplicações com vários threads para essas padrão estipula um tipo próprio atomic extern modificador extern diz ao compilador que variável indicada foi declarada em outro arquivo que não podemos incluir diretamente por exemplo código de uma biblioteca padrão isso importante pois se não colocarmos modificador extern compilador irá declarar uma nova variável com nome especificado ocultando variável que realmente desejamos usar se simplesmente não declarássemos variável já sabemos que compilador não saberia tamanho da variável quando compilador encontra modificador extern ele marca variável como não resolvida montador se encarregará de substituir endereço correto da variável extern float sum extern int count float returnsum void count return sum neste exemplo compilador irá saber que count sum estão sendo usados no arquivo mas que foram declarados em outro uma variável e
á saber que count sum estão sendo usados no arquivo mas que foram declarados em outro uma variável externa frequentemente usada variável errno declarada no arquivo cabeçalho errno que indica último código de erro encontrado na execução de uma função da biblioteca padrão ou do sistema static funcionamento das variáveis declaradas como static depende de se estas são globais ou locais variáveis globais static funcionam como variáveis globais dentro de um módulo ou seja são variáveis globais que não são nem podem ser conhecidas em outros módulos arquivos isto util se quisermos isolar pedaços de um programa para evitar mudanças acidentais em variáveis globais isso um tipo de encapsulamento que ato de não permitir que uma variável seja modificada diretamente mas apenas por meio de uma função variáveis locais estáticas são variáveis cujo valor mantido de uma chamada da função para outra veja exemplo int count void static int num num return num função count retorna número de vezes que ela já foi chamada veja que variável local int inicializada esta inicialização só vale para primeira vez que função chamada pois num deve manter seu valor de uma chamada para outra que função faz incrementar num cada chamada retornar seu valor melhor maneira de se entender esta variável local static implementando veja por si mesmo executando seu próprio programa que use este conceito register computador pode guardar dados na memória ram nos registradores internos do processador as variáveis assim como programa como um todo costumam ser armazenadas na memória modificador register diz ao compilador que variável em questão deve ser se possível guardada em um registrador da cpu vamos agora ressaltar vários pontos importantes porque usar register variáveis nos registradores da cpu vão ser acessadas em um tempo muito menor pois os registradores são muito mais rápidos que memória no entanto maioria dos compiladores otimizantes atuais usa registradores da cpu para variáveis então uso de register freqüentemente desnecessário em que tipo de variável podemos usar register antes da criação do padrão ansi register aplicava se apenas aos tipos int char mas padrão atual permite uso de register para qualquer um dos quatro tipos fundamentais claro que seqüências de caracteres arrays estruturas também não podem ser guardadas nos registradores da cpu por serem grandes demais register um pedido que programador faz ao compilador este não precisa ser atendido necessariamente alguns compiladores até ignoram modificador register que permitido pelo padrão register não pode ser usado em variáveis globais pois isto implicaria em um registrador da cpu ficar tempo todo ocupado por essa variável um exemplo do uso do register dado seguir int main void register int count for count count count return loop acima em compiladores que não guardam variáveis em registradores por padrão deve ser executado mais rapidamente do que seria se não usássemos register este uso mais recomendável para register uma variável
damente do que seria se não usássemos register este uso mais recomendável para register uma variável que será usada muitas vezes em seguida