#+title: Curso Básico da Linguagem C #+subtitle: Aula 9: Argumentos e ambiente #+author: Blau Araujo #+startup: show2levels #+options: toc:3 * Aula 9: Argumentos e ambiente [[https://youtu.be/uufnW60rg2Q][Vídeo desta aula]] ** Vetores e strings #+begin_src c char str[] = "banana"; printf("Tamanho de str : %zu\n", sizeof(str)); // Imprime 7. #+end_src O vetor =str= é do tipo "/array of char/", tem 7 elementos (=char[7]=) e seu nome é o endereço do primeiro caractere. #+begin_src c char *pstr = "cabana"; printf("Tamanho de pstr: %zu\n", sizeof(pstr)); // Imprime 8. #+end_src #+begin_quote O operador =sizeof= avaliou 8 bytes porque é o tamanho de um endereço. #+end_quote 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: #+begin_src c 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). #+end_src #+begin_quote O operador =sizeof= avaliou 8 bytes porque é o tamanho de um endereço. #+end_quote 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... #+begin_src c void print_arr(int *arr[], int size); #+end_src Ou... #+begin_src c void print_arr(int **arr, int size); #+end_src *** Vetores de strings Vetores de strings são vetores de ponteiros: #+begin_src c 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. #+end_src #+begin_quote O operador =sizeof= avaliou 8 bytes porque é o tamanho de um endereço. #+end_quote As strings literais são interpretadas como /"pointer to char"/ (=char *=). Numa função... #+begin_src c void print_list(char **list); #+end_src *** 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=. #+begin_src c 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 */ #+end_src ** Parâmetros da função 'main' As especificação dizem que a função =main= pode ser definida sem receber nenhum parâmetro... #+begin_src c int main(void) { /* ... */ } #+end_src 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=). #+begin_src c int main(int argc, char **argv) { /* ... */ } #+end_src 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: #+begin_src c int main(int argc, char **argv, char **envp) { /* ... */ } #+end_src 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 global =environ=. #+begin_src c #include #include // 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; } #+end_src ** 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= #+begin_src c #include #include #include 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; } #+end_src 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. #+begin_src c long strtol(const char *restrict nptr, char **_Nullable restrict endptr, int base); #+end_src 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. #+begin_quote Como a conversão pode retornar o valor =0=, é preciso inicializar =errno= com =0= antes de chamar =strtol=. #+end_quote 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...* #+begin_example :~$ gcc -Wall -o soma soma.c :~$ ./soma 1 2 3 4 1 + 2 + 3 + 4 = 10 #+end_example