LLuciano Posted August 10 Posted August 10 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... Quote
Administrators Fernando Mercês Posted August 10 Administrators Posted August 10 A explicação é complicada (para os meus parâmetros hehe), mas vou tentar. 🙂 *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). Para a sum(), ao fazer &argv você passa o endereço de argv, ou seja, 0x200. Na sum() você recebe esse endereço como um ponteiro para ponteiro para um array de chars: **args[]. O que está certo. Na sum(), args contém um único elemento (o ponteiro para um array chars e não o array de chars em si). Com *args[i], sabendo que a precedência de [] é maior que a de *, então vamos analisar o loop: args[0] é o primeiro (e único) elemento de args. *args[0] o acessa. args[1] falha, pois args só contém um elemento. Segfault aqui. Agora, com (*args)[i]: (*args) é resolvido primeiro em todas as iterações do loop. O resultado é um ponteiro para um array de chars. (*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. 1 Quote
LLuciano Posted August 10 Author Posted August 10 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. 1 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.