Introdução ao C: Difference between revisions

From Applied Science
No edit summary
Tag: wikieditor
No edit summary
Tag: wikieditor
 
(7 intermediate revisions by the same user not shown)
Line 5: Line 5:
* '''Palavras chave.''' São palavras reservadas, tem um significado fixo e inalterável. Por exemplo, uma função ou variável não pode ser chamada de ''''while'''', porque ''''while'''' já tem um significado fixo. A exceção é texto, podemos imprimir na tela a palavra ''''while'''' porque neste caso, a palavra não é tratada como comando na compilação do programa, apenas como uma sequência de caracteres. O mesmo acontece com símbolos como vírgulas e ponto e vírgula, o significado é fixo e o uso é pré-determinado pela sintaxe da linguagem.<br><br>
* '''Palavras chave.''' São palavras reservadas, tem um significado fixo e inalterável. Por exemplo, uma função ou variável não pode ser chamada de ''''while'''', porque ''''while'''' já tem um significado fixo. A exceção é texto, podemos imprimir na tela a palavra ''''while'''' porque neste caso, a palavra não é tratada como comando na compilação do programa, apenas como uma sequência de caracteres. O mesmo acontece com símbolos como vírgulas e ponto e vírgula, o significado é fixo e o uso é pré-determinado pela sintaxe da linguagem.<br><br>


* '''A operação de atribuição.''' Bem no começo, entender '''a = 2''' como ''"a é igual a dois"'', não tem problema porque a variável ''''a'''' tem o valor dois depois da atribuição. Mas quando fazemos operações aritméticas, aí essa leitura é errada, precisamos ler como ''"o valor dois é atribuído na variável a"''.
* '''A operação de atribuição.''' Bem no começo, entender '''a = 2''' como ''"a é igual a dois"'', não tem problema porque a variável ''''a'''' tem o valor dois depois da atribuição. Mas quando fazemos operações aritméticas, aí essa leitura é errada, precisamos ler como ''"o valor dois é atribuído na variável a"''.<br><br>
<div class="code">
 
'''Por exemplo:'''<br>
<div class="bullet_margin">
a = b + c;<br><br>Isso é lido da direita para a esquerda, primeiro ocorre a soma ''''b + c'''', então o resultado é atribuído na variável ''''a''''.</div>
'''Por exemplo:'''
<source lang="c">
a = b + c;</source>
Isso é lido da direita para a esquerda, primeiro ocorre a soma ''''b + c'''', então o resultado é atribuído na variável ''''a''''.</div>
 


* '''Declaração da variável.''' Seria muito chato programar tendo que se referenciar a posições de memória o tempo todo, para isso a linguagem expõe a nós uma variável, um espaço na memória que referenciamos através de um nome, muito mais fácil de entender e memorizar do que um código numérico. A variável precisa ter um tipo, afinal, seria confuso se todas as variáveis fossem de um tipo só. Uma analogia: seria confuso guardar líquidos em caixas e seria um desperdício utilizar uma caixa muito grande para guardar um objeto muito pequeno. Consulte a documentação do C para saber todos os tipos existentes.
* '''Declaração da variável.''' Seria muito chato programar tendo que se referenciar a posições de memória o tempo todo, para isso a linguagem expõe a nós uma variável, um espaço na memória que referenciamos através de um nome, muito mais fácil de entender e memorizar do que um código numérico. A variável precisa ter um tipo, afinal, seria confuso se todas as variáveis fossem de um tipo só. Uma analogia: seria confuso guardar líquidos em caixas e seria um desperdício utilizar uma caixa muito grande para guardar um objeto muito pequeno. Consulte a documentação do C para saber todos os tipos existentes.
<div class="code">
<div class="bullet_margin"><source lang="c">
<span style="color: #408080; font-style: italic">/* Declaração de variáveis */</span><br>
/* Declaração de variáveis */
<span style="color: #B00040">int</span> var;<br>
int var;
<span style="color: #B00040">float</span> var2;<br>
float var2;
<span style="color: #B00040">double</span> var3;
double var3;


<span style="color: #408080; font-style: italic">/* Declaração de variável com atribuição de valor */</span><br>
/* Declaração de variável com atribuição de valor */
<span style="color: #B00040">int</span> var <span style="color: #666666">=</span> <span style="color: #666666">2</span>;<br>
int var = 2;
<span style="color: #B00040">float</span> var2 <span style="color: #666666">=</span> <span style="color: #666666">4.5</span>;
float var2 = 4.5;


<span style="color: #408080; font-style: italic">/* Perda de precisão, os dígitos da parte não inteira são perdidos */</span><br>
/* Perda de precisão, os dígitos da parte não inteira são perdidos */
<span style="color: #B00040">int</span> var <span style="color: #666666">=</span> <span style="color: #666666">2.5</span>;<br>
int var = 2.5;


<span style="color: #408080; font-style: italic">/* Mistura de tipos. O tipo com maior precisão domina a expressão. </span><br>
/* Mistura de tipos. O tipo com maior precisão domina a expressão.
<span style="color: #408080; font-style: italic">&nbsp;&nbsp;&nbsp;&nbsp;No exemplo, float tem preferência sobre o inteiro 2. O resultado</span><br>
    No exemplo, float tem preferência sobre o inteiro 2. O resultado
<span style="color: #408080; font-style: italic">&nbsp;&nbsp;&nbsp;&nbsp;da operação é 2.5 */</span><br>
    da operação é 2.5 */
<span style="color: #B00040">float</span> a;<br>
float a;
<span style="color: #B00040">float</span> b <span style="color: #666666">=</span> <span style="color: #666666">5</span>;<br>
float b = 5;
<span style="color: #B00040">int</span> c <span style="color: #666666">=</span> <span style="color: #666666">2</span>;<br>
int c = 2;
a <span style="color: #666666">=</span> b<span style="color: #666666">/</span>c;
a = b/c;


<span style="color: #408080; font-style: italic">/* Caso especial de dominância: apesar da variável &quot;a&quot; ser float,</span><br>
/* Caso especial de dominância: apesar da variável "a" ser float,
<span style="color: #408080; font-style: italic">&nbsp;&nbsp;&nbsp;&nbsp;o resultado será 2.0 pois a operação de divisão é feita primeiro</span><br>
    o resultado será 2.0 pois a operação de divisão é feita primeiro
<span style="color: #408080; font-style: italic">&nbsp;&nbsp;&nbsp;&nbsp;e com inteiros, a operação de atribuição é feita depois. */</span><br>
    e com inteiros, a operação de atribuição é feita depois. */
<span style="color: #B00040">float</span> a;<br>
float a;
<span style="color: #B00040">int</span> b <span style="color: #666666">=</span> <span style="color: #666666">5</span>;<br>
int b = 5;
<span style="color: #B00040">int</span> c <span style="color: #666666">=</span> <span style="color: #666666">2</span>;<br>
int c = 2;
a <span style="color: #666666">=</span> b<span style="color: #666666">/</span>c;
a = b/c;
</source></div>


</div>
* '''A vírgula ','.''' Da mesma forma separamos exemplos numa lista com vírgulas, variáveis são separadas com vírgulas num programa. Tambem serve para separar parâmetros numa função e argumentos numa chamada de função. O compilador não distingue espaços em branco, então <source lang="c" inline>int a,b,c;</source> declara 3 variáveis do tipo '''int''', a ausência ou não de espaços antes ou depois das vírgulas é uma questão de legibilidade.<br><br>


* '''A vírgula ','.''' Da mesma forma separamos exemplos numa lista com vírgulas, variáveis são separadas com vírgulas num programa. Tambem serve para separar parâmetros numa função e argumentos numa chamada de função. O compilador não distingue espaços em branco, então <span id="var1">'''int'''</span> a<span id="var2">,</span>b<span id="var2">,</span>c<span id="var1">;</span> declara 3 variáveis do tipo '''int''', a ausência ou não de espaços antes ou depois das vírgulas é uma questão de legibilidade.<br><br>


* '''O ponto e vírgula ';'.''' É usado para terminar uma linha, um comando.
* '''O ponto e vírgula ';'.''' É usado para terminar uma linha, um comando.<br><br>
<div class="code">
<div class="bullet_margin">
'''Exemplo prático:'''<br>
'''Exemplo prático:'''<source lang="c">
a <span style="color: #666666">=</span> c <span style="color: #666666">+</span> <span style="color: #666666">3</span><br>
a = c + 3
<span style="color: #666666">+</span> d <span style="color: #666666">-</span> <span style="color: #666666">2</span>;
+ d - 2;
</source>


Visualmente, o comando tem duas linhas, mas para o compilador, a expressão aritmética ali só termina no ponto e vírgula da linha de baixo. Um erro não incomum é colocar um ponto e vírgula onde não se deve, interrompendo um comando. Exemplo:
Visualmente, o comando tem duas linhas, mas para o compilador, a expressão aritmética ali só termina no ponto e vírgula da linha de baixo. Um erro não incomum é colocar um ponto e vírgula onde não se deve, interrompendo um comando.
 
<span style="color: #008000; font-weight: bold">if</span> (condição);<br>
&nbsp;&nbsp;&nbsp;&nbsp;comando;


Exemplo:
<source lang="c">
if (condição);
    comando;
</source>
Outro exemplo:
Outro exemplo:
 
<source lang="c">
<span style="color: #408080; font-style: italic">/* é permitido escrever assim, mas a legibilidade fica horrível */</span><br>
/* é permitido escrever assim, mas a legibilidade fica horrível */
comando_1; comando_2; comando_3;
comando_1; comando_2; comando_3;
 
</source>
</div>
</div>


* '''Parêntesis '()'.''' Em funções e em expressões aritméticas é igual à matemática. Em comandos de decisão, como '''if''' e '''loops''', é necessário colocar uma condição entre parêntesis. Note que o compilador não diferencia espaços em branco, coloque para deixar o código mais legível, mas não colocar espaços em branco não atrapalha na execução ou na compilação.
* '''Parêntesis '()'.''' Em funções e em expressões aritméticas é igual à matemática. Em comandos de decisão, como '''if''' e '''loops''', é necessário colocar uma condição entre parêntesis. Note que o compilador não diferencia espaços em branco, coloque para deixar o código mais legível, mas não colocar espaços em branco não atrapalha na execução ou na compilação.


* '''Condicionais.''' Comandos como '''if''' e '''loops''' só são executados se uma condição for verdadeira. É natural confundir o operador '==' (igualdade lógica) com '=' (atribuição de valor) no começo. Consulte a documentação da linguagem C para saber todos os operadores disponíveis.
* '''Condicionais.''' Comandos como '''if''' e '''loops''' só são executados se uma condição for verdadeira. É natural confundir o operador '==' (igualdade lógica) com '=' (atribuição de valor) no começo. Consulte a documentação da linguagem C para saber todos os operadores disponíveis.
<div class="code">
<div class="bullet_margin">
Uma diferença aqui é que na operação de atribuição, o que está a direita do sinal é avaliado primeiro, enquanto que na operação de comparação, o que esta a esquerda é avaliado primeiro. Isso pode causar alguma confusão, mas nada muito grave, já que para os algoritmos estudados, isso acaba não alterando o funcionamento dos mesmos. Consulte a documentação da linguagem para saber a prioridade dos operadores. Note que a condicional não tem sentido se for colocada fora de um comando, '''a == 2;''' isoladamente não produz efeito nenhum.
Uma diferença aqui é que na operação de atribuição, o que está a direita do sinal é avaliado primeiro, enquanto que na operação de comparação, o que esta a esquerda é avaliado primeiro. Isso pode causar alguma confusão, mas nada muito grave, já que para os algoritmos estudados, isso acaba não alterando o funcionamento dos mesmos. Consulte a documentação da linguagem para saber a prioridade dos operadores. Note que a condicional não tem sentido se for colocada fora de um comando, '''a == 2;''' isoladamente não produz efeito nenhum.
</div>
</div>


* '''Operador ternário ou condicional '?:':''' algumas declarações 'if ... else' podem ser reescritas como se segue:
* '''Operador ternário ou condicional '?:':''' algumas declarações 'if ... else' podem ser reescritas como se segue:
<div class="code">
<div class="bullet_margin"><source lang="c">
(expressão condicional) ? (expressão 1) : (expressão 2)
(expressão condicional) ? (expressão 1) : (expressão 2)
 
</source>
Que é lida como "a condicional é verdadeira?". Se a resposta for positiva, a '''"expressão 1"''' é executada, caso contrário a '''"expressão 2"''' é executada. Os parêntesis não são necessários se a expressão for bastante simples, mas caso existiam outras operações (atribuição, relacional, lógica, aritmética) é recomendável usá-los para evitar confusões ou erros de compilação.
Que é lida como "a condicional é verdadeira?". Se a resposta for positiva, a '''"expressão 1"''' é executada, caso contrário a '''"expressão 2"''' é executada. Os parêntesis não são necessários se a expressão for bastante simples, mas caso existiam outras operações (atribuição, relacional, lógica, aritmética) é recomendável usá-los para evitar confusões ou erros de compilação.


'''Observação:''' toda condicional é uma expressão, mas nem toda expressão é uma condicional.
'''Observação:''' toda condicional é uma expressão, mas nem toda expressão é uma condicional.
</div>
</div>


* '''O par de abre e fecha chaves '{}'.''' Determinam blocos de comando. São necessários em funções e em comandos de decisão ou laços que tenham mais de um comando para executar. Por exemplo:
* '''O par de abre e fecha chaves '{}'.''' Determinam blocos de comando. São necessários em funções e em comandos de decisão ou laços que tenham mais de um comando para executar. Por exemplo:
<div class="code">
<div class="bullet_margin"><source lang="C">
if (condição) {
if (condição) {
     comando_1;
     comando_1;
     comando_2;
     comando_2;
}
}</source>
'''Outro exemplo:'''
'''Outro exemplo:'''
<source lang="C">
for (contador; expressão; incremento)
for (contador; expressão; incremento)
     if (condicional) {
     if (condicional) {
Line 90: Line 101:
         comando_2;
         comando_2;
     }
     }
</source>
Neste caso, veja que o ''''for'''' não precisou de chaves porque só tem um comando subordinado a ele, o ''''if''''. Os dois comandos, 1 e 2, são subordinados ao ''''if'''', não ao ''''for'''', daí precisa de chaves no bloco do ''''if''''. Mantendo o uso das chaves, todo o comando poderia ser escrito numa linha só.
Neste caso, veja que o ''''for'''' não precisou de chaves porque só tem um comando subordinado a ele, o ''''if''''. Os dois comandos, 1 e 2, são subordinados ao ''''if'''', não ao ''''for'''', daí precisa de chaves no bloco do ''''if''''. Mantendo o uso das chaves, todo o comando poderia ser escrito numa linha só.


'''Mais um exemplo:'''
'''Mais um exemplo:'''
 
<source lang="c">
for (contador; expressão; incremento) comando;
for (contador; expressão; incremento) comando;
 
</source>
Neste caso é até desejável omitir as chaves, pois as chaves atrapalham a leitura do único comando subordinado ao laço. Um laço escrito assim é semelhante à notação de somatória da matemática ''''∑''''. Compare a leitura de uma expressão com o símbolo da somatória na matemática com um laço escrito numa linha só.
Neste caso é até desejável omitir as chaves, pois as chaves atrapalham a leitura do único comando subordinado ao laço. Um laço escrito assim é semelhante à notação de somatória da matemática ''''∑''''. Compare a leitura de uma expressão com o símbolo da somatória na matemática com um laço escrito numa linha só.
</div>
</div>
* '''O símbolo '#' (sustenido).''' Ficam no topo dos programas #include <biblioteca.h> e #define. Funções como printf() e scanf() estão definidas nos arquivos cabeçalho, sem eles não poderemos utilizar as funções definidas nele a não ser que essas funções sejam definidas no nosso próprio programa. A função dos arquivos cabeçalho é organizar grupos de funções, principalmente as muito requisitadas. Como nem todas são utilizadas sempre, não temos um único arquivo cabeçalho para todas. Como os algoritmos tratados na introdução são simples e não são feitos programas grandes e complexos, esse tipo de gerenciamento de funções não é estudado.
 
<div class="code">
 
* '''O símbolo '#' (sustenido).''' Ficam no topo dos programas <source lang="c" inline>#include <biblioteca.h></source> e <source lang="c" inline>#define</source>. Funções como printf() e scanf() estão definidas nos arquivos cabeçalho, sem eles não poderemos utilizar as funções definidas nele a não ser que essas funções sejam definidas no nosso próprio programa. A função dos arquivos cabeçalho é organizar grupos de funções, principalmente as muito requisitadas. Como nem todas são utilizadas sempre, não temos um único arquivo cabeçalho para todas. Como os algoritmos tratados na introdução são simples e não são feitos programas grandes e complexos, esse tipo de gerenciamento de funções não é estudado.
<div class="bullet_margin">
<source lang="c">
#define SIM 1
#define SIM 1
#define NAO 0
#define NAO 0
 
</source>
Com isso, podemos substituir valores 1 e 0, respectivamente, por SIM e NAO onde houverem valores 1 ou 0 que representem uma resposta SIM ou NAO, assim fica mais fácil de ler o código.
Com isso, podemos substituir valores 1 e 0, respectivamente, por SIM e NAO onde houverem valores 1 ou 0 que representem uma resposta SIM ou NAO, assim fica mais fácil de ler o código.


Existem outros usos para ''''#'''' mas não são vistos na introdução.
Existem outros usos para ''''#'''' mas não são vistos na introdução.
</div>
</div>
* '''Código de formato.''' Em operações aritméticas ''''%'''' é a operação resto da divisão (o resto sempre é inteiro). Na função '''printf()''' é usado como código de formato. Exemplo:
* '''Código de formato.''' Em operações aritméticas ''''%'''' é a operação resto da divisão (o resto sempre é inteiro). Na função '''printf()''' é usado como código de formato. Exemplo:
<div class="code">
<div class="bullet_margin">
<source lang="c">
printf("variavel tem o valor %d", variavel);
printf("variavel tem o valor %d", variavel);
 
</source>
O código ''''%d'''' será substituído pelo valor da variável na impressão. É assim que se geram textos "dinâmicos", textos que variam o que será impresso na tela sem que o próprio código do programa tenha um texto fixo e inalterável. Consulte a documentação do C para saber todos os "marcadores" disponíveis. Um erro comum é a variável ter um tipo, mas na impressão o código de formato estar errado, daí o valor impresso sai errado, mesmo que a conta esteja certa; isso pode confundir e levar um bom tempo para corrigir...
O código ''''%d'''' será substituído pelo valor da variável na impressão. É assim que se geram textos "dinâmicos", textos que variam o que será impresso na tela sem que o próprio código do programa tenha um texto fixo e inalterável. Consulte a documentação do C para saber todos os "marcadores" disponíveis. Um erro comum é a variável ter um tipo, mas na impressão o código de formato estar errado, daí o valor impresso sai errado, mesmo que a conta esteja certa; isso pode confundir e levar um bom tempo para corrigir...
</div>
</div>
* '''Incrementos e decrementos.''' Existem "atalhos", expressões que podem ser simplificadas com o uso do sinal duplo ''''++'''' ou ''''--''''. Cuidado! Não pode haver espaço em branco entre o sinal duplo, ''''+ +'''' por exemplo, mas pode haver antes ou depois. Consulte a documentação da linguagem C para saber todas as simplificações disponíveis.
 
<div class="code">
 
* '''Incrementos e decrementos.''' Existem ''"atalhos"'', expressões que podem ser simplificadas com o uso do sinal duplo ''''++'''' ou ''''--''''. Cuidado! Não pode haver espaço em branco entre o sinal duplo, ''''+ +'''' por exemplo, mas pode haver antes ou depois. Consulte a documentação da linguagem C para saber todas as simplificações disponíveis.
<div class="bullet_margin">
'''Exemplo:'''
'''Exemplo:'''
 
<source lang="c">
/* Os pares significam a mesma coisa, versão simplificada, seguida
/* Os pares significam a mesma coisa, versão simplificada, seguida
   da versão por extenso */
   da versão por extenso */
Line 130: Line 150:
/* cuidado com este! */
/* cuidado com este! */
cont = cont + cont++;
cont = cont + cont++;
 
</source>
Quanto ao último exemplo: suponhamos que a variável ''''cont'''' tenha o valor 1, na leitura da expressão, a conta é 1 + 1 ou 1 + 2? Executando a operação de incremento primeiro, o resultado final é 3, mas poderia ser 2 se o compilador assim entendesse. Para evitar este tipo de ambiguidade, simplesmente dividimos a expressão em duas linhas separadas, dois comandos separados, assim teremos certeza sobre qual operação é executada primeiro. Exemplo prático para diferenciar incremento anterior e posterior:
Quanto ao último exemplo: suponhamos que a variável ''''cont'''' tenha o valor 1, na leitura da expressão, a conta é 1 + 1 ou 1 + 2? Executando a operação de incremento primeiro, o resultado final é 3, mas poderia ser 2 se o compilador assim entendesse. Para evitar este tipo de ambiguidade, simplesmente dividimos a expressão em duas linhas separadas, dois comandos separados, assim teremos certeza sobre qual operação é executada primeiro. Exemplo prático para diferenciar incremento anterior e posterior:
 
<source lang="c">
int a = 0, b;
int a = 0, b;
while (a != 10) {
while (a != 10) {
Line 138: Line 158:
       printf("\n%d", b);
       printf("\n%d", b);
}
}
 
</source>
Qual a diferença entre '''++a''' e '''a++'''? No primeiro caso, este simples laço contará de 1 a 10, no segundo caso, a contagem é de 0 a 9. No primeiro caso, a variável ''''a'''' primeiro é incrementada de um, depois atribuído o valor um à variável ''''b''''. No segundo caso, primeiro ''''b'''' recebe o valor zero, depois ''''a'''' é incrementada de um. Cuidado com a simulação disto! São duas operações em sequência e na mesma iteração do laço, não conte uma iteração e dois incrementos!
Qual a diferença entre '''++a''' e '''a++'''? No primeiro caso, este simples laço contará de 1 a 10, no segundo caso, a contagem é de 0 a 9. No primeiro caso, a variável ''''a'''' primeiro é incrementada de um, depois atribuído o valor um à variável ''''b''''. No segundo caso, primeiro ''''b'''' recebe o valor zero, depois ''''a'''' é incrementada de um. Cuidado com a simulação disto! São duas operações em sequência e na mesma iteração do laço, não conte uma iteração e dois incrementos!
</div>
</div>
* '''Vetores e matrizes.''' Uma matriz ou vetor é declarada como variável, mas precisa de um par ''''[]'''' para cada dimensão a ser usada.  
* '''Vetores e matrizes.''' Uma matriz ou vetor é declarada como variável, mas precisa de um par ''''[]'''' para cada dimensão a ser usada.  
<div class="code">
<div class="bullet_margin"><source lang="c">
int vetor[10] = {1,2,3,4,5,6,7,8,9,10};
int vetor[10] = {1,2,3,4,5,6,7,8,9,10};
int matriz[2][2] = {{1,2},
int matriz[2][2] = {{1,2},
Line 155: Line 177:
vetor[i++];
vetor[i++];
vetor[a[i]];
vetor[a[i]];
Note que o compilador não distingue espaços em branco, então não tem problema vetor [] ou matriz[] [ ] por exemplo. O índice da dimensão deve ser inteiro e deve ser constante, não pode ser uma variável. Não faz sentido um conjunto ter meias posições ou meios elementos. Vetores e matrizes de tamanho dinâmico, variável durante a execução do programa, não são estudados numa introdução. Não tem problema se a matriz ou vetor tiver muitas posições não usadas. Na atribuição de valores na matriz, não é obrigatório dispor os elementos em linhas e colunas no próprio código, isso é só para facilitar a identificação das posições. No exemplo do vetor de 10 posições, para não atribuir nada numa posição, basta apagar um dos números dali, deixando a vírgula que sobra do jeito que esta mesmo, duas vírgulas seguidas.
</source>
Note que o compilador não distingue espaços em branco, então não tem problema <source lang="c" inline>vetor []</source> ou <source lang="c" inline>matriz[] [ ]</source> por exemplo. O índice da dimensão deve ser inteiro e deve ser constante, não pode ser uma variável. Não faz sentido um conjunto ter meias posições ou meios elementos. Vetores e matrizes de tamanho dinâmico, variável durante a execução do programa, não são estudados numa introdução. Não tem problema se a matriz ou vetor tiver muitas posições não usadas. Na atribuição de valores na matriz, não é obrigatório dispor os elementos em linhas e colunas no próprio código, isso é só para facilitar a identificação das posições. No exemplo do vetor de 10 posições, para não atribuir nada numa posição, basta apagar um dos números dali, deixando a vírgula que sobra do jeito que esta mesmo, duas vírgulas seguidas.


Um erro bastante comum é confundir o elemento com a posição e vice-versa. Por exemplo: lista[10] = 123; Na posição 10 do vetor 'lista' é atribuído o valor 123. Cuidado! Quando se declara um tamanho de 10 elementos por exemplo, o índice começa sempre do zero, então a última posição é a 9 e não 10.
Um erro bastante comum é confundir o elemento com a posição e vice-versa. Por exemplo: <source lang="c" inline>lista[10] = 123;</source> Na posição 10 do vetor ''''lista'''' é atribuído o valor 123. Cuidado! Quando se declara um tamanho de 10 elementos por exemplo, o índice começa sempre do zero, então a última posição é a 9 e não 10.
Caracteres. Fundamentalmente são iguais a variáveis para valores simples e a vetores para sequências, a diferença é que caracteres utilizam uma tabela onde cada código numérico corresponde a um caractere.
Caracteres. Fundamentalmente são iguais a variáveis para valores simples e a vetores para sequências, a diferença é que caracteres utilizam uma tabela onde cada código numérico corresponde a um caractere.
<source lang="c">
char letra = 'a';
char letra = 'a';
char palavra[4] = {"casa"};
char palavra[4] = {"casa"};
</source>
Assim como acontece com vetores, somente na declaração da string é possível atribuir automaticamente valores a todos os índices, usando aspas e chaves. Os índices tambem começam do zero, não do um.
Assim como acontece com vetores, somente na declaração da string é possível atribuir automaticamente valores a todos os índices, usando aspas e chaves. Os índices tambem começam do zero, não do um.
Expressões lógicas. Em C, por convenção, verdadeiro esta associado a qualquer valor diferente de zero, enquanto o zero esta associado a falso. Assim:
Expressões lógicas. Em C, por convenção, verdadeiro esta associado a qualquer valor diferente de zero, enquanto o zero esta associado a falso. Assim:
<source lang="c">
/* condicional sempre verdadeira */
/* condicional sempre verdadeira */
if (5)
if (5)
Line 169: Line 195:
if (0)
if (0)
     comando;
     comando;
As operações de comparação, como (a > b) ou (a == b), tambem são associadas a verdadeiro ou falso. Assim, a = (1 > 2), 'a' recebe o valor 0.
</source>
As operações de comparação, como <source lang="c" inline>(a > b)</source> ou <source lang="c" inline>(a == b)</source>, também são associadas a verdadeiro ou falso. Assim, <source lang="c" inline>a = (1 > 2)</source>, 'a' recebe o valor 0.


Comparações compostas são otimizadas pelo compilador, no caso do operador '||' (condição_1 || condição_2), se 1 for verdadeira, então 2 não é avaliada pois uma condição já foi satisfeita. No caso do operador '&&' (condição_1 && condição_2), se 1 for verdadeira, é preciso avaliar 2, pois se 2 não for, então a condicional não será satisfeita. Se 1 for falsa, então toda a condicional já não é satisfeita e 2 não é avaliada. Em combinações de ambos (condição_1 || condição_2 && condição_3) o uso dos parêntesis é necessário para saber qual o par avaliado com '&&' ou com '||'.
Comparações compostas são otimizadas pelo compilador, no caso do operador ''''||'''' <source lang="c" inline>(condição_1 || condição_2)</source>, se 1 for verdadeira, então 2 não é avaliada pois uma condição já foi satisfeita. No caso do operador ''''&&'''' <source lang="c" inline>(condição_1 && condição_2)</source>, se 1 for verdadeira, é preciso avaliar 2, pois se 2 não for, então a condicional não será satisfeita. Se 1 for falsa, então toda a condicional já não é satisfeita e 2 não é avaliada. Em combinações de ambos <source lang="c" inline>(condição_1 || condição_2 && condição_3)</source> o uso dos parêntesis é necessário para saber qual o par avaliado com ''''&&'''' ou com ''''||''''.


Em condicionais, é possível trocar uma afirmação por uma negação. Por exemplo:
Em condicionais, é possível trocar uma afirmação por uma negação. Por exemplo:
<source lang="c">
while !(a == 2)
while !(a == 2)
         comando;
         comando;
No lugar do laço executar enquanto 'a' for igual a 2, o laço é executado enquanto 'a' não for igual a 2. Tem o mesmo efeito de trocar a comparação '==' por '!='. Em alguns casos pode ser mais prático trocar uma condicional afirmativa por uma negativa.
</source>
Declaração e uso de funções. Uma função, assim como uma variável, precisa ser declarada, pois ela também é um lugar na memória. A programação em C já se inicia com o uso de funções, mas a finalidade do 'return 0' e da função principal são completamente ignoradas até que se tenha o conhecimento sobre os principais comandos. Consulte a documentação do C para saber quais os tipos de função disponíveis na linguagem.
No lugar do laço executar enquanto ''''a'''' for igual a 2, o laço é executado enquanto ''''a'''' não for igual a 2. Tem o mesmo efeito de trocar a comparação ''''=='''' por ''''!=''''. Em alguns casos pode ser mais prático trocar uma condicional afirmativa por uma negativa.
</div>
 
 
* '''Declaração e uso de funções.''' Uma função, assim como uma variável, precisa ser declarada, pois ela também é um lugar na memória. A programação em C já se inicia com o uso de funções, mas a finalidade do ''''return 0'''' e da função principal são completamente ignoradas até que se tenha o conhecimento sobre os principais comandos. Consulte a documentação do C para saber quais os tipos de função disponíveis na linguagem.
<div class="bullet_margin">
<source lang="c">
/* isto é uma definição de função. Os parâmetros não tem nenhuma  
/* isto é uma definição de função. Os parâmetros não tem nenhuma  
   regra sobre a ordem de um e de outro */
   regra sobre a ordem de um e de outro */
Line 191: Line 225:
     return algum_valor;
     return algum_valor;
}
}
É entendendo a diferença entre variável local e "global" (aspas porque é no sentido de não local) que se entende porque variáveis podem ter o mesmo nome, mas cada uma numa função diferente.
</source>
É entendendo a diferença entre variável local e ''"global"'' (aspas porque é no sentido de não local) que se entende por que variáveis podem ter o mesmo nome, mas cada uma numa função diferente.
<source lang="c">
/* 'a' deixou de ser um parâmetro e agora é uma variável local */
/* 'a' deixou de ser um parâmetro e agora é uma variável local */
int func () {  
int func () {  
Line 199: Line 235:
/* isto não tem sentido, confusão entre definir e chamar a função */
/* isto não tem sentido, confusão entre definir e chamar a função */
int func (int a = 2) { }
int func (int a = 2) { }
</source>
Pensemos no seguinte: f(x) = x + 2. Escrever f(3) = x + 2 é errado. Escrever f() = x + 2 também é sem sentido.
Pensemos no seguinte: f(x) = x + 2. Escrever f(3) = x + 2 é errado. Escrever f() = x + 2 também é sem sentido.
<source lang="c">
/* isto é uma chamada de função. Os argumentos estão na mesma ordem  
/* isto é uma chamada de função. Os argumentos estão na mesma ordem  
   que os parâmetros na declaração da função. */
   que os parâmetros na declaração da função. */
func (argumento1, argumento2, ...);
func (argumento1, argumento2, ...);
</source>
Uma função precisa ser declara antes de ser chamada, ao contrário, não compila. Porém, há um modo de definir a função depois da chamada, esse modo é definindo um protótipo de função.
Uma função precisa ser declara antes de ser chamada, ao contrário, não compila. Porém, há um modo de definir a função depois da chamada, esse modo é definindo um protótipo de função.
<source lang="c">
/* protótipo da função, não precisa dar os nomes das variáveis parâmetro aqui */
/* protótipo da função, não precisa dar os nomes das variáveis parâmetro aqui */
int func (int, ...);
int func (int, ...);
Line 225: Line 265:
   sempre esta vazio */
   sempre esta vazio */
int func2 (int vetor[], int matriz[][indice_max]) { }
int func2 (int vetor[], int matriz[][indice_max]) { }
</source>
A função do protótipo é informar ao compilador qual o tipo da função e dos parâmetros, isso evita problemas com chamada de função com tipos incompatíveis de argumentos ou número incorreto destes. A outra função é permitir a organização do programa em diferentes arquivos, mas isso não é feito na introdução porque todos os algoritmos e programas feitos são muito simples.
A função do protótipo é informar ao compilador qual o tipo da função e dos parâmetros, isso evita problemas com chamada de função com tipos incompatíveis de argumentos ou número incorreto destes. A outra função é permitir a organização do programa em diferentes arquivos, mas isso não é feito na introdução porque todos os algoritmos e programas feitos são muito simples.


Observação: se uma função devolve um valor ela pode ser usada como parte ou até mesmo ser a própria condicional. Porém, uma função que não devolve nada não pode ser usada na condicional, uma vez que "nada" não pode ser associado nem a VERDADEIRO nem a FALSO.
'''Observação:''' se uma função devolve um valor ela pode ser usada como parte ou até mesmo ser a própria condicional. Porém, uma função que não devolve nada não pode ser usada na condicional, uma vez que ''"nada"'' não pode ser associado nem a VERDADEIRO nem a FALSO.
Ponteiros e funções. Ponteiros são declarados como variáveis, mas tem um asterisco para indicar que é um ponteiro e não uma variável qualquer. O operador 'e comercial' indica "endereço (de memória) de".  
</div>
 
 
* '''Ponteiros e funções.''' Ponteiros são declarados como variáveis, mas tem um asterisco para indicar que é um ponteiro e não uma variável qualquer. O operador ''''e comercial'''' indica ''"endereço (de memória) de"''.  
<div class="bullet_margin">
<source lang="c">
/* sintaxe para declarar ponteiros */
/* sintaxe para declarar ponteiros */
int *p = &var;
int *p = &var;
Line 235: Line 281:
/* O de cima atribui o valor 10 não no ponteiro, mas aloca o valor
/* O de cima atribui o valor 10 não no ponteiro, mas aloca o valor
   no local de memória apontado. Cuidado! E se o ponteiro foi
   no local de memória apontado. Cuidado! E se o ponteiro foi
   declarado mas sem um endereço de memória atribuído a ele? Aí
   declarado, mas sem um endereço de memória atribuído a ele? Aí
   o valor 10 foi alocado num local desconhecido da memória.
   o valor 10 foi alocado num local desconhecido da memória.
   O de baixo esta "ligando" o ponteiro a um endereço de memória.
   O de baixo está "ligando" o ponteiro a um endereço de memória.
   Cuidado! 'p' recebeu o endereço de 'var' e não o valor! */
   Cuidado! 'p' recebeu o endereço de 'var' e não o valor! */
*p = 10;
*p = 10;
p = &var;
p = &var;


/* assim esta errado */
/* assim está errado */
*p = &var;
*p = &var;


/* se p foi declarado como ponteiro, então a sintaxe esta errada.  
/* se p foi declarado como ponteiro, então a sintaxe está errada.  
   p não pode ser "transformado" numa variável qualquer */
   p não pode ser "transformado" numa variável qualquer */
p = 10;
p = 10;
Ponteiros são variáveis com propósito específico, precisam de um tipo como qualquer outra variável, mas não são utilizados como uma variável qualquer. Ponteiros só fazem sentido quando utilizados em conjunto com funções. A definição de um ponteiro esta atrelada ao funcionamento da memória do computador. Para entendê-los a melhor forma é resolvendo problemas que dependam de ponteiros.
</source>
Ponteiros são variáveis com propósito específico, precisam de um tipo como qualquer outra variável, mas não são utilizados como uma variável qualquer. Ponteiros só fazem sentido quando utilizados em conjunto com funções. A definição de um ponteiro está atrelada ao funcionamento da memória do computador. Para entendê-los a melhor forma é resolvendo problemas que dependam de ponteiros.


Vejamos como é a sintaxe de ponteiros, vetores, matrizes e funções quando usados em conjunto. Note que a diferenciação entre local e não local também se aplica a ponteiros. A variável não precisa ter o mesmo nome do ponteiro:
Vejamos como é a sintaxe de ponteiros, vetores, matrizes e funções quando usados em conjunto. Note que a diferenciação entre local e não local também se aplica a ponteiros. A variável não precisa ter o mesmo nome do ponteiro:
<source lang="c">
/* recebe apenas um endereço de memória de uma variável */
/* recebe apenas um endereço de memória de uma variável */
int func (int *p) { }
int func (int *p) { }
Line 269: Line 317:
      
      
     /* um ponteiro com atribuição de um endereço de memória do início do vetor
     /* um ponteiro com atribuição de um endereço de memória do início do vetor
       Quando chamamos uma função com o nome do vetor como parametro, tem o
       Quando chamamos uma função com o nome do vetor como parâmetro, tem o
       mesmo efeito de passar o endereço do índice zero do vetor. Isso é melhor
       mesmo efeito de passar o endereço do índice zero do vetor. Isso é melhor
       entendido com o estudo da aritmética dos ponteiros */
       entendido com o estudo da aritmética dos ponteiros */
Line 281: Line 329:
     func(&vetor[5]);
     func(&vetor[5]);


     /* errado! Isto esta passando um endereço de memória do próprio ponteiro p  
     /* errado! Isto está passando um endereço de memória do próprio ponteiro p  
       para a função. Um ponteiro não pode receber um endereço de outro ponteiro,
       para a função. Um ponteiro não pode receber um endereço de outro ponteiro,
       a menos que estejamos lidando com ponteiros de ponteiros, o que não é o caso */
       a menos que estejamos lidando com ponteiros de ponteiros, o que não é o caso */
Line 288: Line 336:
     /* o primeiro argumento é um endereço de memória, o início do vetor, daí só ter o nome
     /* o primeiro argumento é um endereço de memória, o início do vetor, daí só ter o nome
       do vetor sem colchetes. O segundo não é o valor 5 e nem um endereço de memória, é o
       do vetor sem colchetes. O segundo não é o valor 5 e nem um endereço de memória, é o
       valor que esta guardado no índice 5 do vetor */
       valor que está guardado no índice 5 do vetor */
     func2(vetor, vetor[5]);
     func2(vetor, vetor[5]);


Line 295: Line 343:
     func3(nome);
     func3(nome);
}
}
</source>
</div>
</div>

Latest revision as of 21:05, 21 January 2025

Como a disciplina é introdutória, os algoritmos não utilizam recursos mais avançados da linguagem C. Muita coisa não é vista neste curso, apenas o mais elementar para se entender a execução de um programa. Como alguns sinais são bastante similares à matemática e até ao idioma escrito, basta ler com atenção um código para entender a ordem das operações.

Um pequeno resumo da linguagem C
  • Palavras chave. São palavras reservadas, tem um significado fixo e inalterável. Por exemplo, uma função ou variável não pode ser chamada de 'while', porque 'while' já tem um significado fixo. A exceção é texto, podemos imprimir na tela a palavra 'while' porque neste caso, a palavra não é tratada como comando na compilação do programa, apenas como uma sequência de caracteres. O mesmo acontece com símbolos como vírgulas e ponto e vírgula, o significado é fixo e o uso é pré-determinado pela sintaxe da linguagem.

  • A operação de atribuição. Bem no começo, entender a = 2 como "a é igual a dois", não tem problema porque a variável 'a' tem o valor dois depois da atribuição. Mas quando fazemos operações aritméticas, aí essa leitura é errada, precisamos ler como "o valor dois é atribuído na variável a".

Por exemplo:

a = b + c;
Isso é lido da direita para a esquerda, primeiro ocorre a soma 'b + c', então o resultado é atribuído na variável 'a'.


  • Declaração da variável. Seria muito chato programar tendo que se referenciar a posições de memória o tempo todo, para isso a linguagem expõe a nós uma variável, um espaço na memória que referenciamos através de um nome, muito mais fácil de entender e memorizar do que um código numérico. A variável precisa ter um tipo, afinal, seria confuso se todas as variáveis fossem de um tipo só. Uma analogia: seria confuso guardar líquidos em caixas e seria um desperdício utilizar uma caixa muito grande para guardar um objeto muito pequeno. Consulte a documentação do C para saber todos os tipos existentes.
/* Declaração de variáveis */
int var;
float var2;
double var3;

/* Declaração de variável com atribuição de valor */
int var = 2;
float var2 = 4.5;

/* Perda de precisão, os dígitos da parte não inteira são perdidos */
int var = 2.5;

/* Mistura de tipos. O tipo com maior precisão domina a expressão.
    No exemplo, float tem preferência sobre o inteiro 2. O resultado
    da operação é 2.5 */
float a;
float b = 5;
int c = 2;
a = b/c;

/* Caso especial de dominância: apesar da variável "a" ser float,
    o resultado será 2.0 pois a operação de divisão é feita primeiro
    e com inteiros, a operação de atribuição é feita depois. */
float a;
int b = 5;
int c = 2;
a = b/c;
  • A vírgula ','. Da mesma forma separamos exemplos numa lista com vírgulas, variáveis são separadas com vírgulas num programa. Tambem serve para separar parâmetros numa função e argumentos numa chamada de função. O compilador não distingue espaços em branco, então int a,b,c; declara 3 variáveis do tipo int, a ausência ou não de espaços antes ou depois das vírgulas é uma questão de legibilidade.


  • O ponto e vírgula ';'. É usado para terminar uma linha, um comando.

Exemplo prático:
a = c + 3
+ d - 2;

Visualmente, o comando tem duas linhas, mas para o compilador, a expressão aritmética ali só termina no ponto e vírgula da linha de baixo. Um erro não incomum é colocar um ponto e vírgula onde não se deve, interrompendo um comando.

Exemplo:

if (condição);
    comando;

Outro exemplo:

/* é permitido escrever assim, mas a legibilidade fica horrível */
comando_1; comando_2; comando_3;
  • Parêntesis '()'. Em funções e em expressões aritméticas é igual à matemática. Em comandos de decisão, como if e loops, é necessário colocar uma condição entre parêntesis. Note que o compilador não diferencia espaços em branco, coloque para deixar o código mais legível, mas não colocar espaços em branco não atrapalha na execução ou na compilação.


  • Condicionais. Comandos como if e loops só são executados se uma condição for verdadeira. É natural confundir o operador '==' (igualdade lógica) com '=' (atribuição de valor) no começo. Consulte a documentação da linguagem C para saber todos os operadores disponíveis.

Uma diferença aqui é que na operação de atribuição, o que está a direita do sinal é avaliado primeiro, enquanto que na operação de comparação, o que esta a esquerda é avaliado primeiro. Isso pode causar alguma confusão, mas nada muito grave, já que para os algoritmos estudados, isso acaba não alterando o funcionamento dos mesmos. Consulte a documentação da linguagem para saber a prioridade dos operadores. Note que a condicional não tem sentido se for colocada fora de um comando, a == 2; isoladamente não produz efeito nenhum.


  • Operador ternário ou condicional '?:': algumas declarações 'if ... else' podem ser reescritas como se segue:
(expressão condicional) ? (expressão 1) : (expressão 2)

Que é lida como "a condicional é verdadeira?". Se a resposta for positiva, a "expressão 1" é executada, caso contrário a "expressão 2" é executada. Os parêntesis não são necessários se a expressão for bastante simples, mas caso existiam outras operações (atribuição, relacional, lógica, aritmética) é recomendável usá-los para evitar confusões ou erros de compilação.

Observação: toda condicional é uma expressão, mas nem toda expressão é uma condicional.


  • O par de abre e fecha chaves '{}'. Determinam blocos de comando. São necessários em funções e em comandos de decisão ou laços que tenham mais de um comando para executar. Por exemplo:
if (condição) {
    comando_1;
    comando_2;
}

Outro exemplo:

for (contador; expressão; incremento)
     if (condicional) {
         comando_1;
         comando_2;
     }

Neste caso, veja que o 'for' não precisou de chaves porque só tem um comando subordinado a ele, o 'if'. Os dois comandos, 1 e 2, são subordinados ao 'if', não ao 'for', daí precisa de chaves no bloco do 'if'. Mantendo o uso das chaves, todo o comando poderia ser escrito numa linha só.

Mais um exemplo:

for (contador; expressão; incremento) comando;

Neste caso é até desejável omitir as chaves, pois as chaves atrapalham a leitura do único comando subordinado ao laço. Um laço escrito assim é semelhante à notação de somatória da matemática '∑'. Compare a leitura de uma expressão com o símbolo da somatória na matemática com um laço escrito numa linha só.


  • O símbolo '#' (sustenido). Ficam no topo dos programas #include <biblioteca.h> e #define. Funções como printf() e scanf() estão definidas nos arquivos cabeçalho, sem eles não poderemos utilizar as funções definidas nele a não ser que essas funções sejam definidas no nosso próprio programa. A função dos arquivos cabeçalho é organizar grupos de funções, principalmente as muito requisitadas. Como nem todas são utilizadas sempre, não temos um único arquivo cabeçalho para todas. Como os algoritmos tratados na introdução são simples e não são feitos programas grandes e complexos, esse tipo de gerenciamento de funções não é estudado.
#define SIM 1
#define NAO 0

Com isso, podemos substituir valores 1 e 0, respectivamente, por SIM e NAO onde houverem valores 1 ou 0 que representem uma resposta SIM ou NAO, assim fica mais fácil de ler o código.

Existem outros usos para '#' mas não são vistos na introdução.


  • Código de formato. Em operações aritméticas '%' é a operação resto da divisão (o resto sempre é inteiro). Na função printf() é usado como código de formato. Exemplo:
printf("variavel tem o valor %d", variavel);

O código '%d' será substituído pelo valor da variável na impressão. É assim que se geram textos "dinâmicos", textos que variam o que será impresso na tela sem que o próprio código do programa tenha um texto fixo e inalterável. Consulte a documentação do C para saber todos os "marcadores" disponíveis. Um erro comum é a variável ter um tipo, mas na impressão o código de formato estar errado, daí o valor impresso sai errado, mesmo que a conta esteja certa; isso pode confundir e levar um bom tempo para corrigir...


  • Incrementos e decrementos. Existem "atalhos", expressões que podem ser simplificadas com o uso do sinal duplo '++' ou '--'. Cuidado! Não pode haver espaço em branco entre o sinal duplo, '+ +' por exemplo, mas pode haver antes ou depois. Consulte a documentação da linguagem C para saber todas as simplificações disponíveis.

Exemplo:

/* Os pares significam a mesma coisa, versão simplificada, seguida
   da versão por extenso */
cont++; 
cont = cont + 1;

cont--;
cont = cont - 1;

cont += a; 
cont = cont + a;

/* cuidado com este! */
cont = cont + cont++;

Quanto ao último exemplo: suponhamos que a variável 'cont' tenha o valor 1, na leitura da expressão, a conta é 1 + 1 ou 1 + 2? Executando a operação de incremento primeiro, o resultado final é 3, mas poderia ser 2 se o compilador assim entendesse. Para evitar este tipo de ambiguidade, simplesmente dividimos a expressão em duas linhas separadas, dois comandos separados, assim teremos certeza sobre qual operação é executada primeiro. Exemplo prático para diferenciar incremento anterior e posterior:

int a = 0, b;
while (a != 10) {
       b = ++a;
       printf("\n%d", b);
}

Qual a diferença entre ++a e a++? No primeiro caso, este simples laço contará de 1 a 10, no segundo caso, a contagem é de 0 a 9. No primeiro caso, a variável 'a' primeiro é incrementada de um, depois atribuído o valor um à variável 'b'. No segundo caso, primeiro 'b' recebe o valor zero, depois 'a' é incrementada de um. Cuidado com a simulação disto! São duas operações em sequência e na mesma iteração do laço, não conte uma iteração e dois incrementos!


  • Vetores e matrizes. Uma matriz ou vetor é declarada como variável, mas precisa de um par '[]' para cada dimensão a ser usada.
int vetor[10] = {1,2,3,4,5,6,7,8,9,10};
int matriz[2][2] = {{1,2},
                    {3,4}};

/* Errado! Não é permitido atribuir valores dessa forma depois da declaração do vetor, o mesmo para matriz */
vetor[10] = {1};

/* Pegadinha! O vetor tambem pode ter operadores de incremento e decremento no índice. Por quê não até um
   elemento de outro vetor como índice? Tome cuidado o porque o incremento pode ser anterior ou posterior
   à expressão em que o vetor estiver */
vetor[i++];
vetor[a[i]];

Note que o compilador não distingue espaços em branco, então não tem problema vetor [] ou matriz[] [ ] por exemplo. O índice da dimensão deve ser inteiro e deve ser constante, não pode ser uma variável. Não faz sentido um conjunto ter meias posições ou meios elementos. Vetores e matrizes de tamanho dinâmico, variável durante a execução do programa, não são estudados numa introdução. Não tem problema se a matriz ou vetor tiver muitas posições não usadas. Na atribuição de valores na matriz, não é obrigatório dispor os elementos em linhas e colunas no próprio código, isso é só para facilitar a identificação das posições. No exemplo do vetor de 10 posições, para não atribuir nada numa posição, basta apagar um dos números dali, deixando a vírgula que sobra do jeito que esta mesmo, duas vírgulas seguidas.

Um erro bastante comum é confundir o elemento com a posição e vice-versa. Por exemplo: lista[10] = 123; Na posição 10 do vetor 'lista' é atribuído o valor 123. Cuidado! Quando se declara um tamanho de 10 elementos por exemplo, o índice começa sempre do zero, então a última posição é a 9 e não 10. Caracteres. Fundamentalmente são iguais a variáveis para valores simples e a vetores para sequências, a diferença é que caracteres utilizam uma tabela onde cada código numérico corresponde a um caractere.

char letra = 'a';
char palavra[4] = {"casa"};

Assim como acontece com vetores, somente na declaração da string é possível atribuir automaticamente valores a todos os índices, usando aspas e chaves. Os índices tambem começam do zero, não do um. Expressões lógicas. Em C, por convenção, verdadeiro esta associado a qualquer valor diferente de zero, enquanto o zero esta associado a falso. Assim:

/* condicional sempre verdadeira */
if (5)
    comando;
/* condicional sempre falsa */
if (0)
    comando;

As operações de comparação, como (a > b) ou (a == b), também são associadas a verdadeiro ou falso. Assim, a = (1 > 2), 'a' recebe o valor 0.

Comparações compostas são otimizadas pelo compilador, no caso do operador '||' (condição_1 || condição_2), se 1 for verdadeira, então 2 não é avaliada pois uma condição já foi satisfeita. No caso do operador '&&' (condição_1 && condição_2), se 1 for verdadeira, é preciso avaliar 2, pois se 2 não for, então a condicional não será satisfeita. Se 1 for falsa, então toda a condicional já não é satisfeita e 2 não é avaliada. Em combinações de ambos (condição_1 || condição_2 && condição_3) o uso dos parêntesis é necessário para saber qual o par avaliado com '&&' ou com '||'.

Em condicionais, é possível trocar uma afirmação por uma negação. Por exemplo:

while !(a == 2)
        comando;

No lugar do laço executar enquanto 'a' for igual a 2, o laço é executado enquanto 'a' não for igual a 2. Tem o mesmo efeito de trocar a comparação '==' por '!='. Em alguns casos pode ser mais prático trocar uma condicional afirmativa por uma negativa.


  • Declaração e uso de funções. Uma função, assim como uma variável, precisa ser declarada, pois ela também é um lugar na memória. A programação em C já se inicia com o uso de funções, mas a finalidade do 'return 0' e da função principal são completamente ignoradas até que se tenha o conhecimento sobre os principais comandos. Consulte a documentação do C para saber quais os tipos de função disponíveis na linguagem.
/* isto é uma definição de função. Os parâmetros não tem nenhuma 
   regra sobre a ordem de um e de outro */
int func (int a) { 
    
    /* variavel só existe nesta função, ela não é visível para
       outras funções */
    int var_local;
    
    /* alguma operação aqui */
    
    /* devolve algum valor */
    return algum_valor;
}

É entendendo a diferença entre variável local e "global" (aspas porque é no sentido de não local) que se entende por que variáveis podem ter o mesmo nome, mas cada uma numa função diferente.

/* 'a' deixou de ser um parâmetro e agora é uma variável local */
int func () { 
    int a;
}

/* isto não tem sentido, confusão entre definir e chamar a função */
int func (int a = 2) { }

Pensemos no seguinte: f(x) = x + 2. Escrever f(3) = x + 2 é errado. Escrever f() = x + 2 também é sem sentido.

/* isto é uma chamada de função. Os argumentos estão na mesma ordem 
   que os parâmetros na declaração da função. */
func (argumento1, argumento2, ...);

Uma função precisa ser declara antes de ser chamada, ao contrário, não compila. Porém, há um modo de definir a função depois da chamada, esse modo é definindo um protótipo de função.

/* protótipo da função, não precisa dar os nomes das variáveis parâmetro aqui */
int func (int, ...);

/* quando o parâmetro for ponteiro ou vetor / matriz, pode omitir o nome, 
   mas não esqueça do asterisco! */
int func2 (int *, int *);

/* função principal, onde esta o corpo do seu programa */
int main() { }

/* declarar função dentro de função é proibido */
int main() {
    int sub_func() { }
}

/* declaração da função */
int func (int param1, ...) { }

/* vetor / matriz como parâmetro, note que o primeiro índice
   sempre esta vazio */
int func2 (int vetor[], int matriz[][indice_max]) { }

A função do protótipo é informar ao compilador qual o tipo da função e dos parâmetros, isso evita problemas com chamada de função com tipos incompatíveis de argumentos ou número incorreto destes. A outra função é permitir a organização do programa em diferentes arquivos, mas isso não é feito na introdução porque todos os algoritmos e programas feitos são muito simples.

Observação: se uma função devolve um valor ela pode ser usada como parte ou até mesmo ser a própria condicional. Porém, uma função que não devolve nada não pode ser usada na condicional, uma vez que "nada" não pode ser associado nem a VERDADEIRO nem a FALSO.


  • Ponteiros e funções. Ponteiros são declarados como variáveis, mas tem um asterisco para indicar que é um ponteiro e não uma variável qualquer. O operador 'e comercial' indica "endereço (de memória) de".
/* sintaxe para declarar ponteiros */
int *p = &var;
int *p;

/* O de cima atribui o valor 10 não no ponteiro, mas aloca o valor
   no local de memória apontado. Cuidado! E se o ponteiro foi
   declarado, mas sem um endereço de memória atribuído a ele? Aí
   o valor 10 foi alocado num local desconhecido da memória.
   O de baixo está "ligando" o ponteiro a um endereço de memória.
   Cuidado! 'p' recebeu o endereço de 'var' e não o valor! */
*p = 10;
p = &var;

/* assim está errado */
*p = &var;

/* se p foi declarado como ponteiro, então a sintaxe está errada. 
   p não pode ser "transformado" numa variável qualquer */
p = 10;

Ponteiros são variáveis com propósito específico, precisam de um tipo como qualquer outra variável, mas não são utilizados como uma variável qualquer. Ponteiros só fazem sentido quando utilizados em conjunto com funções. A definição de um ponteiro está atrelada ao funcionamento da memória do computador. Para entendê-los a melhor forma é resolvendo problemas que dependam de ponteiros.

Vejamos como é a sintaxe de ponteiros, vetores, matrizes e funções quando usados em conjunto. Note que a diferenciação entre local e não local também se aplica a ponteiros. A variável não precisa ter o mesmo nome do ponteiro:

/* recebe apenas um endereço de memória de uma variável */
int func (int *p) { }

/* recebe endereço de memória de uma variável num ponteiro e um valor */
int func2 (int *p, int valor) { }

/* recebe um endereço de memória de uma função. No primeiro parêntesis
   o ponteiro, no segundo parêntesis, o(s) parâmetro(s) da função apontada.
   Assim como o protótipo, é permitido omitir os nomes das variáveis.
   Cuidado! O(s) parâmetro(s) são da função apontada, não da função func3.
   func3 neste exemplo tem apenas um parâmetro, o ponteiro */
double func3 ((*p)(float, float, ...)) { }

int main() {
    /* uma declaração de variável e vetor de 10 posições */
    int var, vetor[10];
    
    /* um ponteiro com atribuição de um endereço de memória do início do vetor
       Quando chamamos uma função com o nome do vetor como parâmetro, tem o
       mesmo efeito de passar o endereço do índice zero do vetor. Isso é melhor
       entendido com o estudo da aritmética dos ponteiros */
    int *p = &vetor[0];
    
    /* a função "func" só pode ser chamada com endereços de memória, não com valores */
    func(&var);
    
    /* isso não esta chamando a função com o valor do elemento 5, mas sim
       com o endereço de memória do elemento 5 do vetor */
    func(&vetor[5]);

    /* errado! Isto está passando um endereço de memória do próprio ponteiro p 
       para a função. Um ponteiro não pode receber um endereço de outro ponteiro,
       a menos que estejamos lidando com ponteiros de ponteiros, o que não é o caso */
    func(&p);
    
    /* o primeiro argumento é um endereço de memória, o início do vetor, daí só ter o nome
       do vetor sem colchetes. O segundo não é o valor 5 e nem um endereço de memória, é o
       valor que está guardado no índice 5 do vetor */
    func2(vetor, vetor[5]);

    /* a chamada de func3 é feita como uma função qualquer, mas neste caso, o nome da função 
       apontada é o próprio endereço de memória, não use '&' como se faz com variáveis */
    func3(nome);
}