cblc/aulas/09-args/README.org

6.9 KiB

Curso Básico da Linguagem C

Aula 9: Argumentos e ambiente

Vídeo desta aula

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 global environ.

#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 inicializar errno com 0 antes de chamar strtol.

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