paulo1205
(usa Ubuntu)
Enviado em 27/08/2020 - 03:52h
danilomarto escreveu:
Para a minha pergunta ficar mais clara. Suponha que o meu programa, chamado main, precise receber uma quantidade qualquer de parâmetros do console.
Para tanto, eu teria que fazer isso.
int main( int argc, char *argv[] ){
...
return 0;
}
Correto?
Sim.
Em seguida eu compilo e executo passando os seus parâmetros.
$ gcc main.c -o main
$ ./main par1 par2 ... parn
Eu gostaria de saber como a função main consegue pegar a quantidade de parâmetros passados (que pode ser arbitrária) e jogar na variável argc e jogar todos eles dentro do array *argv[]?
O
shell interpreta o que você coloca na linha de comando e considera que um ou mais espaços (que não estejam cercados por aspas ou apóstrofos ou escapados por uma barra invertida) funcionam como separadores de argumentos. Esses argumentos são, então, colocados pelo
shell dentro de um
array , e esse
array é passado como argumento para a chamada ao sistema
execve () (junto com outro
array , que contém as variáveis de ambiente). Essa chamada dispara a execução de um novo comando, e coloca os dois
arrays numa região de memória que é entregue ao programa.
Quando o programa começa a executar, existe um código que executa antes de
main () (sim, existe um código que executa dentro do seu programa antes de
main (), que o
linker coloca no executável na hora em que o produz) associa essas regiões de memória ao
array recebido através de
argv e, no caso das variáveis de ambiente, ao
array global (declarado como pointeiro)
environ .
Ou seja, o
shell provavelmente tem algum código parecido com o seguinte.
char *command, **new_args=NULL, **new_environ=NULL;
ptrdiff_t token_offset=0;
// Extrai o comando e a lista de argumentos.
command=get_token(cmd_line, &token_offset); // Extrai o primeiro token da linha de comando (get_token é um nome fictício, mas possível de ser feito).
if(command==NULL){
fprintf(stderr, "Erro: ponteiro nulo ao tentar extrair comando.\n";
return -1;
}
size_t n_args=1;
new_args=reallocarray(new_args, n_args+1, sizeof *new_args);
if(new_args==NULL){
fprintf(stderr, "Não foi possível alocar memória: %s.\n", strerror(errno);
return -1;
}
new_args[0]=command;
new_args[1]=NULL;
char *next_arg;
while((next_arg=get_token(cmd_line, &token_offset))!=NULL){
++n_args;
void *new_ptr=reallocarray(new_args, n_args+1, sizeof *new_args);
if(new_ptr==NULL){
fprintf(stderr, "Não foi possível alocar memória: %s.\n", strerror(errno);
free(new_args);
return -1;
}
new_args=new_ptr;
new_args[n_args-1]=next_arg;
new_args[n_args]=NULL;
}
// Para as variáveis de ambiente, poderia também ser um laço de repetição varrendo alguma coisa, mas eu
// preferi colocar com valores fixos para ilustrar (poderiam ser valores fixos para os argumentos, também).
new_environ=malloc((N_ENVVARS+1)*sizeof *new_environ);
new_environ[0]="PATH=/bin:/usr/bin:/usr/local/bin";
new_environ[1]="HOME=/home/paulo1205";
new_environ[2]="TZ=America/Sao_Paulo";
/* ... */
new_environ[N_ENVVARS-1]="ULTIMA_VARIAVEL=ultimo valor";
new_environ[N_ENVVARS]=NULL; // O último elemento tem de ser um ponteiro nulo.
execve(caminho_executavel, new_args, new_environ);
Com a chamada acima, o programa que vier a ser executado terá
n_args entregue como valor de
argc , e os argumentos dispostos em
argv vão corresponder exatamente ao que tiver sido colocado no
array new_args no código acima, incluindo o
n_args+1 -ésimo (ou
argc+1 -ésimo) elemento, que é o ponteiro nulo. Semelhantemente, os valores dispostos no código acima no
array new_environ vão aparecer no programa no
array global
environ , incluindo também o ponteiro nulo para demarcar o final da lista de argumentos.
Há duas formas, portanto, de percorrer os argumentos recebidos em
argv , mostrados abaixo.
for(int n=0; n<argc; ++n)
puts(argv[n])
for(char **parg=argv; *parg!=NULL; ++parg)
puts(*parg);
De modo parecido com o segundo, você pode percorrer as variáveis de ambiente.
extern char **environ;
void print_env(void){
for(char **penv=environ; *penv!=NULL; ++penv)
puts(*penv);
}
Mais ainda, tem como reproduzir esse comportamento em uma função ordinária? Caso sim, Como? Poderia me fornecer um exemplo.
Acho que está bem explicado acima. A única coisa que eu não detalhei foi a implementação interna de
get_token (), mas isso é relativamente fácil de fazer. E você pode até mesmo usar funções da biblioteca padrão para tanto, tais como
strtok (),
strsep () ou mesmo
sscanf ().
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)