Jump to content

Recommended Posts

Posted

Fala pessoal..

Estava estudando C aqui e resolvi fazer um programinha de soma, bem simples.. eu passo no terminal o números e ele me devolve a soma. Dava para fazer tudo na main.. mas decidi criar uma função para o caso de animar a fazer outras operações além da soma e também treinar o uso de ponteiros.. ai que veio o problema. O programa está assim:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int sum(int n, char **args[]);

int main(int argc, char *argv[])
{
  if (argc < 3) {
    puts("usage: calc n1 n2");
    exit(1);
  }
  
  int result;

  result = sum(argc, &argv);
  printf("%d\n", result);

  return 0;
}

int sum(int n, char **args[])
{
  int result = 0;
  for (int i = 1; i < n; i++)
      result += strtol((*args)[i], NULL, 10);
  
  return result;
}

O problema está na linha 26:

result += strtol((*args)[i], NULL, 10); // aqui é versão que funciona

Primeiro eu fiz o strtol sem o enclausurar o *args nos parênteses.. da seguinte forma:

result += strtol(*args[i], NULL, 10); // versão com falha na segmentação.
O Compilador aceitou, usei o GCC default para compilar.. porém em execução deu Segmentation fault:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555201 in sum (n=4, args=0x7fffffffdfe0) at err-sum.c:26
26            result += strtol(*args[i], NULL, 10);
Eu lembrei que para declarar uma ponteiro para função é necessário os parênteses devido a regra de precedência:
int (*nome)(parametro1, ..) //ponteiro para função

int * nome(parametro1, ..) // função que retorna um ponteiro para um inteiro.

Então, por puro chute, eu resolvi colocar os parênteses e tudo funcionou lindamente… o problema é que não entendi o motivo que eu preciso usar esse parênteses ai. Na minha cabeça ao fazer *args[i] eu já estaria “desreferenciando” o suficiente.

Alguém consegue me explicar? Tentei buscar nos materiais que eu tenho mas não consegui encontrar a explicação. Dá uma agonia qdo o negócio funciona e eu não entendo o movito.. kkkk

Valeu

Luciano.

Ps: O nome mais esquisito esse de desreferenciar...

  • Administrators
Posted

A explicação é complicada (para os meus parâmetros hehe), mas vou tentar. 🙂

  1. *argv[] na main() já é um ponteiro para um array de chars. Mas é uma variável, e tem um endereço de memória (supomos, 0x200).
  2. Para a sum(), ao fazer &argv você passa o endereço de argv, ou seja, 0x200.
  3. Na sum() você recebe esse endereço como um ponteiro para ponteiro para um array de chars: **args[]. O que está certo.
  4. Na sum(), args contém um único elemento (o ponteiro para um array chars e não o array de chars em si).
  5. Com *args[i], sabendo que a precedência de [] é maior que a de *, então vamos analisar o loop:
    1. args[0] é o primeiro (e único) elemento de args. *args[0] o acessa.
    2. args[1] falha, pois args só contém um elemento. Segfault aqui.
  6. Agora, com (*args)[i]:
    1. (*args) é resolvido primeiro em todas as iterações do loop. O resultado é um ponteiro para um array de chars.
    2. (*args)[0] funciona, assim como (*args)[1], (*args)[2], até argc-1.

Um desenho certamente explicaria melhor, ou talvez com o uso do GDB. Se não entender, avisa aí que eu tento de outra forma. Ou talvez outra pessoa explique melhor. 🙂

Em tempo, você complicou mais do que precisa. O que eu faria é:
 

sum(int n, char *args[]) {
	// código
	strtol(args[i], ...);
}

main(int argc, char *argv[]) {
	// código
	sum(argc, argv);
}

Dessa forma você passa o argv, que já é um ponteiro para um array de char (C strings, neste caso) para sum() e ela acessa por args, que é do mesmo tipo.

Outras ideias, só por curiosidade:

Economizando a variável i ao somar do último para o primeiro elemento (ou seja, na ordem reversa):

int sum(int n, char *args[])
{
   int result = 0;
   n--;

   while (n > 0) {
      result += strtol(args[n], NULL, 10);
      n--;
   }

  return result;
}

Economizando o uso de argc, já que o último elemento de argv é sempre nulo - ou seja, argv[argc] == NULL:

int sum(char *args[])
{
   int result = 0;

   while (*args) {
      result += strtol(*args, NULL, 10);
      args++;
   }

  return result;
}

Ou uma versão menorzinha, aproveitando que ++ é um operador de pós-incremento (primeiro o valor é lido e depois é incrementado):

int sum(char *args[])
{
   int result = 0;

   while (*args)
      result += strtol(*args++, NULL, 10);

  return result;
}

Mas aí já entram mais conceitos.. por exemplo *args é o mesmo que fazer *args != NULL. E args++ é aritmética de ponteiros. Não é "mais um" e sim "mais X bytes", onde X é o tamanho do tipo.

Versão "olha mãe, sem as mãos":

int sum(char *args[]) {
   return *args ? strtol(*args, NULL, 10) + sum(args + 1) : 0;
}

Aqui já entra recursividade (pode esgotar a pilha) e o ternário.

Fiz essas coisas só para despertar a curiosidade mesmo e provar que C é a linguagem na qual o universo foi escrito. 😄 

Abraço.

  • Agradecer 1
Posted

Nossa! Que aula! Valeu demais!!

Em 10/08/2024 em 12:00, Fernando Mercês disse:

4 -Na sum(), args contém um único elemento (o ponteiro para um array chars e não o array de chars em si).

Agora ficou tão óbvio!KKKK

Acho que entendi perfeitamente sua explicação!

Nada melhor do que tentar desenvolver do 0 minhas coisas.. eu sempre fiquei tentando resolver alguns exercícios que achava, mas no exercício já vem no meio do capítulo de um livro e ai a gente já vai achando as respostas e etc..

Em 10/08/2024 em 12:00, Fernando Mercês disse:
int sum(char *args[]) {
   return *args ? strtol(*args, NULL, 10) + sum(args + 1) : 0;
}

Isso ficou demais!

Um dia chego ai! Eu acho recursão o estado da arte na programação.. ainda não consegui desenvolver sozinho nenhuma função com recursão! Agora, colocar a recursão no meio de um ternário é pra emoldurar o código!

Muito obrigado por responder!

Forte Abraço!

Luciano.

  • Curtir 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...