Jump to content

Problema para "desreferenciar" Ponteiro de Ponteiro de Ponteiro!


LLuciano

Recommended Posts

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

Link to comment
Share on other sites

  • Administrators

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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