6.9 KiB
Curso Básico da Linguagem C
Aula 9: Argumentos e ambiente
Vetores e strings
char str[] = "banana";
printf("Tamanho de str : %zu\n", sizeof(str)); // Imprime 7.
O vetor str
é do tipo "array of char", tem 7 elementos (char[7]
) e seu
nome é o endereço do primeiro caractere.
char *pstr = "cabana";
printf("Tamanho de pstr: %zu\n", sizeof(pstr)); // Imprime 8.
O operador
sizeof
avaliou 8 bytes porque é o tamanho de um endereço.
O ponteiro pstr
é do tipo "pointer to char", não tem referência sobre
o tamanho da string e seu nome avalia o endereço do primeiro caractere.
Vetores de "ponteiros"
Um vetor de ponteiros é um ponteiro para um vetor de endereços:
int d1[] = {1,2,3};
int d2[] = {4,5,6};
int d3[] = {7,8,9};
int *v[] = {d1, d2, d3};
printf("Tamanho de v : %zu\n", sizeof(v)); // Imprime 8.
printf("Tamanho de v[0]: %zu\n", sizeof(v[0])); // Imprime 24 (3*8).
printf("Tamanho de d1 : %zu\n", sizeof(d1)); // Imprime 12 (3*4).
O operador
sizeof
avaliou 8 bytes porque é o tamanho de um endereço.
O tipo de v
é int *
porque os tipos de seus elementos serão interpretados
na compilação como "pointer to int", em vez de "array of int".
Para receber esse tipo de vetor, o parâmetro dessa função teria que ser decladado como…
void print_arr(int *arr[], int size);
Ou…
void print_arr(int **arr, int size);
Vetores de strings
Vetores de strings são vetores de ponteiros:
char *frutas[] = {"banana", "laranja", "abacate"};
printf("Tamanho de frutas : %zu\n", sizeof(frutas)); // Imprime 24 (3*8).
printf("Tamanho de frutas[0]: %zu\n", sizeof(frutas[0])); // Imprime 8.
O operador
sizeof
avaliou 8 bytes porque é o tamanho de um endereço.
As strings literais são interpretadas como "pointer to char" (char *
).
Numa função…
void print_list(char **list);
Vetor de strings terminado com NULL
Os vetores de argumentos e de ambiente são vetores de strings terminados
com uma string nula (contendo apenas '\0'), o que pode ser representado por
(void *)0
(ponteiro nulo) ou simplesmente NULL
.
Esta seria uma lista de strings terminada com NULL
.
char *frutas[] = {"banana", "laranja", "abacate", NULL};
int i = 0;
while (frutas[i]) {
printf("[%d] => %s\n", i, frutas[i]);
i++;
}
/* Imprime:
[0] => banana
[1] => laranja
[2] => abacate
*/
Parâmetros da função 'main'
As especificação dizem que a função main
pode ser definida sem receber
nenhum parâmetro…
int main(void) { /* ... */ }
Ou com dois parâmetros:
- Primeiro parâmetro: variável inteira para a quantidade de argumentos
(tipicamente
argc
). -
Segundo parâmetro: ponteiro para o endereço da lista de argumentos da linha do comando (strings) que iniciou o programa (tipicamente
argv
).int main(int argc, char **argv) { /* ... */ }
Em algumas implementações, a função
main
pode declarar um terceiro parâmetro para receber a lista das variáveis exportadas para o ambiente do processo:int main(int argc, char **argv, char **envp) { /* ... */ }
Mesmo que o terceiro parâmetro não seja declarado, o ambiente pode ser acessado com a função
getenv
(stdlib.h
) ou com o ponteiro globalenviron
.
#include <stdio.h>
#include <stdlib.h>
// Para informar que 'environ' existe...
extern char **environ;
int main(int argc, char **argv) {
printf("environ[0] => %s\n", environ[0]); // Imprime: SHELL=/bin/bash
char *env = getenv("SHELL");
printf("getenv(\042SHELL\042) => %s\n", env); // Imprime: /bin/bash
return 0;
}
Conversão de strings para números
Como todos os argumentos são recebidos como strings, haverá situações em que eles deverão ser convertidos para os valores numéricos correspondentes.
Exemplo: programa soma.c
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
long str2int(char *str) {
int num; // Recebe o inteiro convertido.
char *end; // Recebe o endereço onde termina o inteiro na string.
errno = 0; // Zerar a variável global 'errno'.
// Converte a parte numérica inicial de 'str' pata long int.
num = strtol(str, &end, 10);
// Checagem de erros...
if (str == end) {
// A parte inicial da string não era numérica...
fprintf(stderr, "Nenhum dígito encontrado!\n");
exit(EXIT_FAILURE);
} else if (errno == ERANGE) {
// O número está fora dos limites do tipo 'long int'...
fprintf(stderr, "Número fora dos limites!\n");
exit(EXIT_FAILURE);
} else if (*end != '\0') {
// Caracteres não convertidos restantes...
fprintf(stderr, "Caracteres inválidos: %s\n", end);
exit(EXIT_FAILURE);
}
return num;
}
int main(int argc, char **argv) {
int soma = 0;
int num;
for (int i = 1; i < argc; i++) {
num = str2int(argv[i]);
if (i == 1) {
printf("%d ", num);
} else {
printf("+ %d ", num);
}
soma += num;
}
printf("= %d\n", soma);
return 0;
}
Aqui, nós usamos a função strtol
, que converte os dígitos encontrado na parte
inicial de uma string em seu correspondente numérico com o tipo long int
na
base numérica especificada no terceiro argumento.
long strtol(const char *restrict nptr, char **_Nullable restrict endptr, int base);
Seu retorno é o valor numérico convertido, a menos que o valor exceda os limites
do tipo long int
, o que fará com que o retorno seja LONG_MIN
ou LONG_MAX
. Se esta
for a situação, a global errno
receberá o valor da constante simbólica ERANGE
,
o que nos levou a utilizar o cabeçalho errno.h
no exemplo.
Como a conversão pode retornar o valor
0
, é preciso inicializarerrno
com0
antes de chamarstrtol
.
Ao fazer a conversão, o endereço do término da parte numérica é registrado no
parâmetro endptr
, o que possibilita fazer diversas verificações, como:
- Se o endereço em
endptr
for igual ao endereço do início da string, a parte inicial contém dígitos; - Se o valor em
endptr
for igual a\0
, a string só contém dígitos.
Além disso, se houver uma parte final não numérica, ela pode ser recuperada
a partir do endereço em endptr
.
Compilando e executando…
:~$ gcc -Wall -o soma soma.c :~$ ./soma 1 2 3 4 1 + 2 + 3 + 4 = 10