Programação em C: os fundamentos da linguagem
16 de Abril de 2024
A linguagem C tem vindo a ser a escolha de muitos programadores ao longo de décadas. Neste artigo, vamos aprofundar as bases que sustentam esta linguagem e descobrir por que continua a ser uma referência incontestável no mundo da tecnologia para novos projetos.
Origem da linguagem de programação C
Origens e desenvolvimento da linguagem C
A linguagem de programação C foi desenvolvida no início dos anos 70 por Dennis Ritchie nos laboratórios da Bell. Apesar de ter ultrapassado meio século desde sua criação, a linguagem C tem sido continuamente atualizada e melhorada por grupos de trabalho da Organização Internacional de Normalização (ISO – International Organization for Standardization). Essas atualizações incluem a inclusão de operações aritméticas usando variáveis do tipo floating-point. Atualmente, C é uma das linguagens de programação mais populares e utilizadas em todo mundo, de acordo com o índice TIOBE.
Influência de C noutras linguagens de programação
A linguagem C influenciou significativamente outras linguagens de programação, como Java, Perl, PHP, Python, C#, entre outras. Não esquecendo a influência direta em linguagens como Objective-C, ou C++ que, em particular, começou por ser uma extensão da linguagem C.
C é frequentemente escolhida como linguagem base para ensinar programação. Por isso, é comum dizer-se que saber programar em C, poderá facilitar a aprendizagem de outras linguagens como Java, C++, C#, Python, devido às suas as sintaxes bastante similares.
Fundamentos básicos de programação em C
Estrutura de um programa em C: declaração de bibliotecas e função main()
O exemplo prático mais comum nas linguagens de programação é sobre a mensagem “Hello World!”, como podemos ver na imagem utilizando a linguagem C.
#include <stdio.h>
int main{
printf("Hello World!");
return 0;
}
Os programas desenvolvidos com C são implementados através de dois tipos de ficheiros. Se por um lado temos os ficheiros .c - que contêm o código fonte (source), por outro, temos os ficheiros .h (headers) – que funcionam como uma espécie de lista ou biblioteca de funções a implementar no ficheiro .c, podendo também conter definição de estruturas, variáveis, entre outros.
Num programa desenvolvido utilizando a linguagem C, é essencial a existência de pelo menos um ficheiro .c com a função main(), como a que é apresentada na figura anterior. Esta função é a origem, o ponto de partida do nosso programa e por isso, a primeira a ser executada.
Neste exemplo, também é possível verificar a inclusão de uma biblioteca, concretamente stdio.h, que nos permite o uso da função “printf” para a escrita de mensagens na consola.
Sintaxe básica: declaração de variáveis, operadores, expressões
A declaração de variáveis segue uma nomenclatura específica:
tipo_variável nome_variável = valor;
Os tipos de variáveis em C comuns são char, int, float e double, contudo o tipo de variável pode ser outro como tipo estrutura (struct), enumeração (enum), booleano (incluído em C99), ou uma extensão de algumas das variáveis mais comuns com a keyword long.
O nome da variável é uma opção do programador, porém, existem regras que devem ser seguidas, tais como a de não poder conter espaços, caracteres especiais ou a de iniciar com um número.
A parte final da expressão (= valor) não sendo obrigatória, é considerada uma boa prática.
Estruturas de controlo em C
Utilização de estruturas condicionais: if-else, switch-case
As condições lógicas podem ser usadas para a execução de uma determinada tarefa, podendo essa tarefa ser constituída por uma ou mais operações.
A estrutura if-else permite executar uma tarefa se algo acontecer (if- condição lógica) ou executar outra tarefa em caso contrário (else). A sintaxe de if-else é:
if (condição_lógica){
Operação(ões);
}else{
Operação(ões);
}
A condição_lógica é implementada com base nas operações lógicas da matemática: menor que (a<b); menor ou igual a (a<=b); maior que (a>b); maior ou igual a (a>=b); igual (a==b); ou não igual (a!=b).
A estrutura switch-case permite agrupar e simplificar o uso de várias estruturas if-else que estejam interligadas, como, por exemplo, o intervalo de uma variável inteira (de 0 a 100, tarefa 1; de 101 a 200, tarefa 2, etc.) ou até diferentes tarefas para cada letra do alfabeto (se pressionado a executa tarefa 1; se pressionado b executa tarefa 2, etc.).
A sintaxe do switch-case é:
switch (expressão) {
case x: Operação(ões);
break;
case y: Operação(ões);
break;
default: Operação(ões);
}
A expressão pode ser, por exemplo, uma variável, e o seu valor será comparado com os valores especificados em cada case. Quando o valor da variável e o valor de um case coincidem as operações desse bloco serão executadas. A keyword break fecha o bloco de operações.
Uma boa prática na decisão de quando usar if-else ou switch-case é considerar o número de condições a serem avaliadas. Se estivermos a usar mais de dois if-else-if, então será um bom indicativo para mudar para switch-case. Isso vai tornar o código mais limpo, fácil de entender e potencialmente mais eficiente.
Utilização de estruturas de repetição: for, while, do-while
As estruturas de repetição for, while e do-while, permitem executar um conjunto de operações até uma condição ser verificada.
A sintaxe é a seguinte:
|
|
|
No caso da estrutura de repetição for, a expressão1 inicia a variável de controlo e é executada apenas uma vez e antes das operações que estão dentro do ciclo for. A expressão2 define a condição de controlo para que o ciclo for termine. Quando esta condição for falsa a estrutura de repetição for termina. A expressão3, é executada a cada iteração no fim das operações.
Nas estruturas de repetição while e do-while temos a condição de controlo, condição, que nos permite definir quando e enquanto este ciclo deve ser executado.
Eis alguns exemplos da sua aplicação:
Neste exemplo, observamos um caso onde se imprimem no ecrã os números de 0 a 10 utilizando 3 estruturas diferentes de repetição:
|
|
|
programacao-c
Declaração e utilização de arrays
Os arrays são usados para guardar múltiplos valores numa única variável. Um array pode, por exemplo, ser do tipo char, int, float e double.
Um array pode ser declarado de duas formas: com valores iniciais ou apenas definindo o tamanho pretendido:
int arrayName[] = { 0, 10, 20, 30, 40};
int arrayName2[5];
Para obter ou mudar um valor num array devem ser utilizados parêntesis rectos ([ ]) com o índice pretendido. Por exemplo, se for pretendido o terceiro valor do array arrayName, ou seja, o valor 20, este é obtido utilizando arrayName[2]. É importante referir que a indexação na linguagem de programação C é iniciada a 0 (zero).
Para falarmos de apontadores existe a necessidade de mencionar que, cada vez que instanciamos uma variável, essa variável é colocada numa posição de memória disponível e ocupa um determinado espaço em memória dependendo do tipo da variável.
Como exemplo, o tipo char ocupa 1 bytes, int ocupa 2 ou 4 bytes, float ocupa 4 bytes, e double ocupa 8 bytes.
É possível obtermos a localização em memória de uma variável ao utilizar o operador ‘&’ antes do nome da variável:
int X = 10;
printf(“%p”, &X);
A posição da memória de uma variável depende do sistema em que estamos a implementar o nosso programa e das variáveis já criadas.
Um apontador é uma variável que nos permite guardar o endereço de memória de outra variável. Uma variável apontador é criada com o operador ‘*’ e é do mesmo tipo da variável da qual vai guardar a posição de memória.
Vejamos um exemplo com uma variável do tipo char:
char character = ‘b’;
char *ptr = &character;
printf(“%p”, ptr);
É também possível obter o valor da variável através do apontador:
printf(“%c”, *ptr); //- Output: b
Conceito de ponteiros e a sua aplicação em C
Os apontadores são uma das características que fazem com que a linguagem C se diferencie de outras linguagens como Python e Java, pois permitem manipular diretamente os dados na memória, o que leva a reduzir o tamanho do código e a melhorar a performance do programa.
Contudo, é necessária alguma prudência ao trabalhar com apontadores, pois como referido, estes permitem alterar dados diretamente na memória e por isso, danificar outros endereços de memória.
Os apontadores são muito usados para aceder às posições de arrays de grandes dimensões, pois permitem melhorar a performance, a strings (que na prática são arrays de caracteres), aceder a ficheiros e em funções, quando é pretendido alterar valores de variáveis externas ao bloco dessa mesma função.
Funções e procedimentos em C
Definição e utilização de funções em C
Uma função é uma porção de código que é executado quando é chamado. As funções têm um papel relevante em qualquer programa, pois permitem organizar o código, mas, mais importante, permitem a reutilização de código sem ter de repetir a sua escrita.
Nos exemplos anteriores já foi possível observar o uso de funções, nomeadamente a função printf(), que permite exibir informação no ecrã.
Para criar ou instanciar uma função, deve ser utilizada a seguinte sintaxe:
tipo_a_retornar nome_função (lista_parametros) {
Operação(ões);
}
Uma função pode ou não retornar um valor, assim tipo_a_retornar pode ser void se não for pretendido que a função retorne um valor, ou pode ser o tipo de uma variável como char, int, etc.
O nome da função (nome_função) é uma opção do programador, no entanto há regras a seguir tais como a de não poder conter espaços, caracteres especiais ou a de iniciar com um número.
A lista de parâmetros (lista_parametros) não é obrigatória, podendo ser então mantida vazia ou utilizar void, na existência de parâmetros estes são colocados entre vírgulas.
Os parâmetros são variáveis, ou seja, char, int, apontadores, etc, seguido do nome da variável.
Como exemplo, mostramos como é declarada uma função com o nome Soma, que executa a soma de dois inteiros e retorna o resultado da operação:
int Soma(int a, int b) {
return a + b;
}
Passagem de parâmetros por valor e por referência
No exemplo anterior, a função Soma utiliza dois parâmetros de entrada (a e b). Por sua vez, estes estão a ser passados por valor. Quando uma função é invocada com valores, estes têm o nome de argumentos, e é importante entender esta distinção. Vejamos a função Soma. Esta pode ser chamada da seguinte forma Soma(5, 10) como pode ser Soma(Arg1, Arg2), sendo Arg1 e Arg2 duas variáveis do tipo int.
Os argumentos são o 5, 10, Arg1 e Arg2, que na realidade acaba por ser feita uma cópia dos valores para os parâmetros (neste caso a e b), que apenas estão disponíveis no âmbito da função.
| Resultado: |
Na imagem anterior está a execução do nosso exemplo, onde se verifica que os argumentos Arg1 e Arg2 estão em posições de memória diferentes dos parâmetros a e b.
Outra forma de passar valores para funções é por referência. Ao utilizar este método estamos a indicar a posição na memória onde os argumentos estão a ser guardados.
Retomando o exemplo anterior, vamos adaptar para receber o parâmetro b por referência, e além de retornar a soma vamos também retornar o número de vezes que b cabe em a, utilizando o parâmetro b para retornar esse valor.
| Resultado: |
Analisando o resultado, podemos confirmar que o argumento Arg1 e o parâmetro a estão em posições diferentes de memória, enquanto o argumento Arg2 e parâmetro b apontam para a mesma posição de memória.
Como o tipo das variáveis é int o resultado é um inteiro, pois o resultado da divisão de dois números inteiros é apenas a parte inteira dessa divisão.
Num mundo cada vez mais digitalizado e conectado, a linguagem de programação C mantém-se como uma referência na tecnologia moderna. Utilizada numa vasta gama de aplicações, a sua versatilidade e a eficiência tornaram-na uma escolha indispensável para programadores um pouco por todo o mundo.
Nos últimos anos, temos observado um crescimento constante na popularidade de C, não apenas entre os programadores experientes, mas também entre os mais novos que se juntam à esfera tecnológica. Além disso, a comunidade global continua a contribuir para a evolução da linguagem, garantindo uma adaptação contínua às necessidades e volatilidade das indústrias.
Para o futuro, as perspetivas são promissoras com a crescente procura por sistemas computacionais eficientes e de baixo nível, assim como o avanço de novas tecnologias como IoT, Inteligência Artificial e computação em nuvem, a necessidade de programadores nesta linguagem tenderá a aumentar.
Em suma, a sua versatilidade, eficiência e relevância contínua garantem que C permanecerá como uma ferramenta essencial para aqueles que procuram construir o futuro da tecnologia.
Artigos populares
O que é Outsystems e como está a transformar o desenvolvimento de software
Java - O que torna esta linguagem tão popular entre programadores?
A importância das boas práticas na programação em .NET
Linux: um sistema operacional versátil e poderoso
COBOL - Será esta linguagem relevante nos dias de hoje?
Implementar um sistema de Business Intelligence