Uma variável ou função é booleana se admite apenas dois valores: 0 e 1.
(A propósito, veja a interface stdbool.h, que define o tipo bool e as constantes true e false.)
Toda expressão em C tem um valor. Por exemplo, se x vale 20 então a expressão 3 * x + 4 tem valor 64. Nas mesmas condições, a expressão
y = 3 * x + 4
(note que y =
faz parte da expressão)
tem valor 64.
O cálculo do valor dessa expressão tem o efeito colateral
de atribuir à variável y
o valor da subexpressão
3 * x + 4.
Assim, y passa a valer 64.
Outro exemplo: se x vale 20 então o valor da expressão x = x + 1 é 21. Calcular o valor dessa expressão tem o efeito colateral de incrementar o valor de x.
Eis um exemplo mais delicado, muito comum em C: se x vale 20 então o valor da expressão
x++
é 20. Um exemplo sutilmente diferente: se x vale 20 então o valor da expressão
++x
é 21. Em ambos os casos, as expressões têm como efeito colateral o incremento do valor de x, que passa a valer 21.
Algo análogo vale para expressões da forma x-- e --x.
(Veja a resposta
à pergunta What is the difference between I++ and I = I+1 in C language?
no Quora.)
Mais um exemplo: O valor da expressão c = (a = 1) + (b = 2) é 3. O efeito colateral de cálcular o valor da expressão é a atribuição dos valores 1, 2 e 3 às variáveis a, b e c respectivamente.
Último exemplo: No trecho de código abaixo, o valor da expressão c = i é 255 (e não −1, como alguém poderia imaginar):
unsigned char c; int i; i = -1; c = i;
Toda expressão booleana (= expressão lógica) em C tem valor 0 ou 1. Por exemplo, o valor da expressão
tt == 32
é 1 se tt vale 32 e é 0 se tt não vale 32. O valor da expressão
rand () <= RAND_MAX / 2
é 1 se a função rand devolver um número menor ou igual a RAND_MAX / 2 e é 0 se a função devolver um número maior que RAND_MAX / 2.
A ordem dos fatores em uma expressão booleana. O valor de toda expressão booleana é calculado da esquerda para a direita. Se o valor da expressão ficar claro antes que a expressão toda tenha sido examinada, o cálculo é interrompido. Essa regra — conhecida como minimal evaluation ou short-circuit evaluation — evita o cálculo de subexpressões de valor indefinido.
No exemplo abaixo, embora as duas expressões pareçam equivalentes, a primeira está correta enquanto a segunda pode estar errada se o valor de v[n] não estiver definido.
if (j < n && v[j] < x) ... if (v[j] < x && j < n) ...
Se n é um número positivo do tipo int, o valor da expressão n/2 é ⌊n/2⌋ , ou seja, o piso do quociente de n por 2. Por exemplo: se n vale 9 então n/2 vale 4.
Se n é um número negativo do tipo int, o valor da expressão n/2 é −⌊−n/2⌋ . (Parece estranho, mas é assim que funciona.) Por exemplo: se n vale −9 então n/2 vale −4 e não −5.
De modo mais geral, se n e m são do tipo int e têm o mesmo sinal, então o valor da expressão n/m é ⌊n/m⌋. Se os sinais de n e m são diferentes então o valor da expressão n/m é −⌊−n/m⌋.
Um (int)
antes de uma expressão aritmética
é um molde (= cast),
ou coerção.
Se x é uma variável do tipo double,
a expressão
(int) (x/2)
tem por valor a parte inteira de x/2. Assim, se x vale 9.0 então o valor da expressão é 4 (não 4.5).
Outro exemplo: se n é uma variável do tipo int então a expressão
(double) n / 2
((double) n) / 2
e transforma o valor de n em um número do tipo double antes de fazer a divisão por 2. Se o valor de n for 9 o valor da expressão será 4.5 (não 4).
Mais um exemplo: se h é uma variável do tipo int com valor −1 então o valor da expressão
(unsigned) h
é UINT_MAX, ou seja, 4294967295.
O casting é automático em situações como
char *ptr; ptr = malloc (1);
e portanto não é necessário escrever
ptr = (char *) malloc (1);
No fragmento de código abaixo, a é uma variável, 999 é uma constante inteira, e 'a' é uma constante-caractere (também conhecida como literal):
a = 999; c = 'a';
Da mesma forma, a expressão "EXEMPLO" no fragmento de código a seguir é uma constante-string (também conhecida como literal):
strcpy (s, "EXEMPLO");
O primeiro argumento das funções printf e scanf é usualmente uma constante-string:
scanf ("%d", &n); printf ("O valor de n é %d", n);
A sintaxe do operador typedef é simples.
Primeiro, escreva a declaração de uma variável de algum tipo.
Depois, escreva typedef
antes da declaração.
Por exemplo,
int *ptri;
declara uma variável ptri
(do tipo ponteiro-para-inteiro).
Agora escreva typedef
antes da declaração:
typedef int *ptri;
Com isso, ptri passa a ser o nome de um novo tipo (idêntico ao tipo ponteiro-para-inteiro). Esse tipo pode ser usado para declarar novos ponteiros-para-inteiro:
ptri p, q;
Outro exemplo: A seguinte declaração cria uma variável ponto (que é um par-de-inteiros):
struct { int x; int y; } ponto;
Agora escreva typedef
antes da declaração:
typedef struct { int x; int y; } ponto;
Isso transforma ponto em um novo tipo (idêntico a par-de-inteiros). Esse tipo pode ser usado para declarar novos pares-de-inteiros:
ponto a, b;
(A propósito, veja typedef versus #define in C em GeeksforGeeks.)
A palavra função tem sentidos um pouco diferentes na computação e na matemática. Na matemática, uma função recebe argumentos e devolve um valor. Na computação, além de receber argumentos (= dados) e devolver um valor (= resultado), uma função pode também produzir efeitos colaterais (como modificar os argumentos, ou imprimir algo).
Em C,
quando uma função é invocada,
todos os argumentos são passados à função
em valor
(e não por referência
).
Assim, a função recebe os valores de variáveis
e não as variáveis propriamente ditas.
(Podemos dizer, informalmente,
que a função recebe cópias das variáveis.)
Essa maneira de passar argumentos a uma função
é conhecida como call by value.
Isso vale, em particular, para argumentos que são vetores. Um vetor em C é representado pelo endereço de seu primeiro elemento. Assim, quando um vetor é passado para uma função, a função recebe esse endereço (e não algum ponteiro que armazena esse endereço).
Uma variável é global (= global) se for definida fora de todas as funções que constituem o programa. Variáveis globais são acessíveis em qualquer ponto do programa (mesmo em outros módulos do programa). Exemplo:
int glob; // variável global
int main (void) {
glob = 0;
func ();
printf ("%d", glob);
return EXIT_SUCCESS;
}
void func (void) {
glob += 1;
}
Não use variáveis globais indiscriminadamente! Use variáveis globais apenas quando elas são realmente necessárias!
A palavra-chave static antes da declaração de uma variável global torna a variável privada no módulo em que está sendo declarada. Em outras palavras, a variável é invisível para outros módulos.
A palavra-chave static tem o mesmo efeito sobre as declarações de funções: a função torna-se privada no módulo e invisível em outros módulos.
Por exemplo, num módulo que implementa o algoritmo Mergesort, é razoável que a função auxiliar intercala não possa ser vista de fora do módulo:
static void intercala (int p, int q, int r, int v[]) { . . . } void mergesort (int p, int r, int v[]) { if (p < r-1) { int q = (p + r); mergesort (p, q, v); mergesort (q, r, v); intercala (p, q, r, v); } }
(Veja static funcions e static variables em GeeksforGeeks.)
Todo programa em linguagem C é um conjunto de funções, uma das quais é main. A execução do programa consiste na execução da função main (que tipicamente invoca outras funções do conjunto). A função main devolve um inteiro para informar o sistema operacional sobre o fim da execução do programa. Por exemplo,
int main (void) { ... return 0; }
ou
int main (int argc, char **argv) ... return 0; }
A função main devolve 0 caso o programa tenha terminado de maneira normal e devolve um número não-nulo caso o programa tenha terminado de maneira excepcional. (Veja também a função exit.) Usamos as constantes EXIT_SUCCESS (que vale 0) e EXIT_FAILURE (que vale 1) como valor devolvido por main.
É um erro definir a função main como se ela fosse do tipo void:
void main (...) { ... }
(Veja resposta de Terry Lambert à pergunta Why do most of the people use int as return type for main function and return value as 0 even when there is no use of returning it? no Quora. Veja também Is it fine to write “void main()” or “main()” in C/C++? em GeeksforGeeks.)
A função exit da biblioteca stdlib interrompe a execução do programa e fecha todos os arquivos que o programa tenha porventura aberto. Se o argumento da função for 0, o sistema operacional é informado de que o programa terminou com sucesso; caso contrário, o sistema operacional é informado de que o programa terminou de maneira excepcional.
O argumento da função é tipicamente a constante EXIT_FAILURE, que vale 1, ou a constante EXIT_SUCCESS, que vale 0.
A propósito, a instrução return XXX; que encerra o código da função main é equivalente a exit (XXX);.
O layout da definição
de um ponteiro é sabidamente desconfortável.
Conceitualmente,
um ponteiro-para-int é um novo tipo-de-dados
e isso sugere que se escreva o
*
colado ao int
:
int* p;
Do ponto de vista técnico, entretanto,
o *
modifica a nova variável e não o int
.
Isso sugere que o
*
seja colado ao p
:
int *p;
O compilador C aceita qualquer das formas. Também aceita
int * p;
(Veja a pergunta In C/C++, why do people declare pointers as "int *x" instead of "int* x"? no Quora.)
Convém não deixar ponteiros soltos (= dangling pointers) no seu código, pois isso pode mascarar erros de programação e pode ser explorado por hackers para atacar sua máquina. Portanto, atribua NULL a cada ponteiro liberado:
free (ptr); ptr = NULL;
Atribuir um valor a um ponteiro que se tornou inútil é certamente deselegante. Infelizmente, não há como lidar com hackers de maneira elegante…
Por motivos semelhantes, recomenda-se inicializar todo ponteiro (ou seja, atribuir algum valor ao ponteiro) tão logo ele é declarado. Por exemplo, recomenda-se escrever
int *p = NULL;
em vez que
int *p;
O presente sítio ignora essas recomendações de segurança para não cansar o leitor com detalhes repetitivos.
É um costume civilizado colocar um caractere \n no fim de todo arquivo de texto.
(Também é uma boa ideia eliminar os espaços no fim das linhas, ou seja, evitar que o caractere que precede um \n seja um \ .)