Ir para conteúdo

Problema para "desreferenciar" Ponteiro de Ponteiro de Ponteiro!


LLuciano

Posts Recomendados

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 para o comentário
Compartilhar em outros sites

  • Administradores

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 para o comentário
Compartilhar em outros 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 para o comentário
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...