-
Posts
6.518 -
Cadastrado em
-
Última visita
Tópicos solucionados
-
O post de arfneto em Meu primeiro tópico em C C# e C++ foi marcado como solução
C
#include <stdio.h> int main(void) { puts("Feliz Natal para todos!"); }
C#
using System; namespace Natal { class Program { static void Main(string[] args) { Console.WriteLine("Feliz Natal para todos!"); } } }
C++
#include <iostream> int main(void){ std::cout << "Feliz Natal para todos!"; }
-
O post de arfneto em Cadastro de informações? Sincronização e salvamento? foi marcado como solução
No banco de dados você vai criar.... um banco de dados para essas pessoas, com as tabelas com as informações. O banco de dados atende via um endereço IP, que pode ser o de sua própria máquina, e uma porta que depende do banco de dados e da configuração. São apenas números. Como um nome de arquivo.
Você --- seu programa --- em qualquer linguagem se conecta com o banco de dados e acessa as informações. Não existe esse lance de nuvem, é transparente. Como não existe internet.
Apenas um endereço. Se seu banco de dados estiver na Polonia o endereço IP dele vai resolver isso. Por exemplo o SQL Server da Microsoft é muito usado. A porta padrão para esse é 1433. Se você usar 127.0.0.1:1433 como endereço e sua máquina estiver rodando SQL Server vai funcionar. Se estiver no Google Cloud ou no Azure ou na AWS vai trocar o endereço apenas.
Note que muitas vezes a plataforma faz isso por você e os endereços nem são vistos. Muito de programação hoje é apenas preencher formulários com endereços e eventos e tal.
-
O post de arfneto em Como colocar em ordem alfabetica a listagem de produtos foi marcado como solução
nunca use void(void). Isso só vai te dar muito mais trabalho.
Escreveu um programa C. Não precisa se limitar ao dialeto C se está programando em C++ que é muito mais expressiva.
Comente o que está fazendo e porque está fazendo. Sugiro evitar esses comentários óbvios tipo "variáveis aqui", "op" é a opção....
Em C++ não há razão para escrever assim. Está claro que tem um cadastro de produtos. Só que no programa não tem. Apenas um vetor de struct. E sem nenhum método? Não se escreve assim em C++ porque só fica mais difícil...
C++ tem sort. Basta escrever uma função que compara dois produtos. Pode ter uma linha só....
-
O post de arfneto em Fila só está pegando o último que entrou foi marcado como solução
Talvez seu professor tenha feito assim de propósito com algum outro plano. Mas está muito ruim, como eu disse.
Entre outras coisas
typedef não é usado em C++. Não há razão. as tais funções em C++ fazem parte da struct. Este é o princípio básico de C++: a noção de classe, objeto não use void. é um desperdício ou um erro não escreva um programa interativo . NUNCA. Apenas coloque isso ao final. depois de tudo testado não se preocupe com detalhes tipo cor, limpar a tela, fontes. Escreva o objetivo de seu programa. Implemente uma fila de alunos um cout de oito linhas é muito mais esperto que 8 cout de uma linha. E se vai apenas mostrar valores use puts(). Muito mais esperto e rápido. não use system(). Nunca. não use gets(). NUNCA. Isso nem existe mais em alguns compiladores. escreva em torno dos dados. Um container é um container. TEM que ter isso no código. C ou C++ ou java ou ou COBOL... use os #define antes dos #include
EXEMPLO, em C++
Essa é a saída do exemplo, um programa mínimo só pra você ter um exemplo de uma maneira comum de escrever isso nessa linguagem.
Acho que vai perceber que é muito mais simples.
Fila: "Unica" tem 0 alunos Fila: "Unica" tem 8 alunos Nome..., RA #1 Nome..., RA #2 Nome..., RA #3 Nome..., RA #4 Nome..., RA #5 Nome..., RA #6 Nome..., RA #7 Nome..., RA #8 Fila "Unica"Cheia Fila "Anonima" Vazia Atendeu o cara de RA #1 da fila "Unica" Atendeu o cara de RA #2 da fila "Unica" Atendeu o cara de RA #3 da fila "Unica" Atendeu o cara de RA #4 da fila "Unica" Atendeu o cara de RA #5 da fila "Unica" Atendeu o cara de RA #6 da fila "Unica" Atendeu o cara de RA #7 da fila "Unica" Atendeu o cara de RA #8 da fila "Unica" Fila: "Unica" tem 0 alunos Apagando a fila "Anonima" Apagando a fila "Unica"
Eis o programa
int main(void) { Fila unica("Unica",8); unica.imprime(); // vazia ainda int seq = 1; while (unica.enfileira(Aluno(seq++, "Nome...")) > 0); unica.imprime(); // mostra a fila agora // fila cheia? if (unica.cheia()) cout << "Fila \"" << unica.nome << "\"Cheia\n"; else cout << "Fila \"" << unica.nome << "\" não esta cheia\n"; // fila vazia? Fila outra; if (outra.vazia()) cout << "Fila \"" << outra.nome << "\" Vazia\n"; else cout << "Fila \"" << outra.nome << "\" não esta vazia\n"; // atende todo mundo da fila 'unica' while (not unica.vazia() ) unica.atende(); unica.imprime(); // mostra a fila agora return 0; }
O programa cria 2 filas, uma chamada unica e outra chamada outra. Sim, foi pobre a nomenclatura
Um loop enche a fila unica até dar erro. Vai dar erro, claro, quando não couber mais gente. Como cada fila tem um nome e uma capacidade nada de especial acontece.
A fila é mostrada antes e depois, e tem um teste de outras funções. No final atende todo mundo que estava na primeira fila e encerra o programa.
Eis o código "todo"
// https:www.clubedohardware.com.br/forums/topic/ // 1578391-fila-s%C3%B3-est%C3%A1-pegando-o- //%C3%BAltimo-que-entrou/#comment-8325416 #define LIMITE 10 #include <iostream> #include <queue> using namespace std; struct Aluno { int RA; string nome; void imprime(); Aluno(int ra, string nome) : RA(ra), nome(nome){}; Aluno() : Aluno(0, ""){}; // padrão }; struct Fila { unsigned limite; // maior tamanho aceito string nome; // para distinguir as filas queue<Aluno> fila; bool vazia() { return fila.empty(); }; bool cheia() { return fila.size() == limite; }; int enfileira(Aluno x) { if (fila.size() >= limite) return -1; fila.push(x); return (int) fila.size(); }; void desenfileira() { fila.pop(); }; void imprime(); // codigo abaixo Aluno atende(); // não tem as chaves... Fila(string n, int lim) : nome(n), limite(lim) { if (lim > LIMITE) limite = LIMITE; }; Fila() : Fila("Anonima",LIMITE){}; // fila padrão sem nome ~Fila() { cout << "Apagando a fila \"" << Fila::nome << "\"\n"; }; }; int main(void) { Fila unica("Unica",8); unica.imprime(); // vazia ainda int seq = 1; while (unica.enfileira(Aluno(seq++, "Nome...")) > 0); unica.imprime(); // mostra a fila agora // fila cheia? if (unica.cheia()) cout << "Fila \"" << unica.nome << "\"Cheia\n"; else cout << "Fila \"" << unica.nome << "\" não esta cheia\n"; // fila vazia? Fila outra; if (outra.vazia()) cout << "Fila \"" << outra.nome << "\" Vazia\n"; else cout << "Fila \"" << outra.nome << "\" não esta vazia\n"; // atende todo mundo da fila 'unica' while (not unica.vazia() ) unica.atende(); unica.imprime(); // mostra a fila agora return 0; } void Fila::imprime() { // Quantos tem? cout << "Fila: \"" << nome << "\" tem " << fila.size() << " alunos\n"; // agora os alunos for (int i = 0; i < fila.size(); i+=1) { Aluno a = fila.front(); fila.pop(); // tira da fila Fila::fila.push(a); // poe de volta no fim a.imprime(); // mostra esse } } Aluno Fila::atende() { Aluno prim = fila.front(); // salva o primeiro fila.pop(); // tira da fila cout << "Atendeu o cara de RA #" << prim.RA << " da fila " << "\"" << nome << "\"\n"; return prim; // retorna } void Aluno::imprime() { cout << nome << ", RA #" << RA << "\n"; };
Veja que algumas funções tem só um linha de código.
Há maneiras diferentes e mais elegantes de escrever isso. Sugiro que se concentre em ver a diferença entre as duas linguagens. E entenda como em C++ as abstrações são muito mais simples.
-
O post de arfneto em c/c++ struct string não é exibida foi marcado como solução
Não há razão para usar typedef em C++
Isso está construindo um Carro. Use um construtor. É o simples. Muito mais simples.
não use void e uma função sem argumentos. Passe parâmetros. Retorne algo. exibe() vai exibir o que? passe o argumento com o endereço ou a referência do Carro em questão.
-
O post de arfneto em Destruir uma struct, é possível? foi marcado como solução
Um livro é uma boa opção, se for um bom livro
Mas estranho dizer que ~não acha muita coisa~ na internet. O que está procurando? Como está procurando?
Podia ter postado o tal exercício.
Está re-criando um vetor. C++ tem vetores para uso em <vector>, mas em C uma struct assim serviria bem.
Seu programa não está muito bom ainda. E tem um erro importante. E está programando praticamente em C e não em C++. E é muito mais vantagem programar em C++ essas coisas.
De volta ao programa
struct Vetor { int capacidade; int tam; int* data; }; Vetor* Vetor_criar(int capacidade) { Vetor* data = new Vetor[capacidade]; tam = 0; this->capacidade = capacidade; return data; }
Aposto que pretendia alocar algo de um certo tamanho em data... Pense nisso. Esse é o vetor que quer criar. E aí a nomenclatura fica realmente confusa.
Provavelmente você quer alocar um vetor de int com uma certa capacidade e colocar dentro da struct. E não alocar um vetor da strut que tem um vetor dentro. E esse é o erro. Alocou o Vetor, mas esse não é o vetor que quer Seu vetor é data[] dentro da struct Vetor
Em C++, mas em C
Isso é o que imagino que queria fazer. É criado um vetor de 300 caras e destruído em seguida. Só coloquei um valor no último item como exemplo. Não vai usar assim porque vai usar tam para controlar o tamanho:
#include <iostream> using namespace std; struct Vetor { int capacidade; int tam; int* data; }; Vetor* Vetor_criar(int capacidade) { Vetor* novo = new Vetor; novo->tam = 0; novo->capacidade = capacidade; novo->data = new int[capacidade]; return novo; } Vetor* Vetor_destroi(Vetor* vetor) { // destroi ao contrario delete[] vetor->data; delete vetor; return nullptr; } int main(void) { Vetor* um = Vetor_criar(300); um->data[299] = 42; cout << "Ultimo valor do vetor: " << um->data[299] << "\n"; um = Vetor_destroi(um); return 0; }
É esperto retornar nullptr de Vetor_destroi() porque assim pode invalidar o ponteiro na mesma linha e nunca vai ter um ponteiro inválido e vivo em seu programa.
A saída
Ultimo valor do vetor: 42
O normal em C++
#include <iostream> using namespace std; struct Vetor { int capacidade; int tam; int* data; Vetor(int c) : tam(0), capacidade(c) { data = new int[c]; } ~Vetor() { delete[] data; } }; int main(void) { Vetor* um = new Vetor(300); um->data[299] = 42; cout << "Ultimo valor do vetor: " << um->data[299] << "\n"; return 0; }
Compare os dois programas. A saída é a mesma. O segundo é mais certo.
-
O post de arfneto em Como funciona um wallpaper engine? foi marcado como solução
É uma chamada de sistema, mais uma biblioteca de mídia e um timer.
cessar quer dizer terminar.
-
O post de arfneto em Entender um código pequeno c++ (recursão) foi marcado como solução
1. Isso é void. Apenas retorna ao chamador. É a definição.
2. Você entendeu o que é esse lance da torre de Hanoi?
A saída do programa você postou. Aquelas linhas são o resultado do cout, a unica saida do programa.
A razão de usar assim é porque para trocar os discos de um pino para outro é a mesma coisa se já tiver uma disco a menos, desde que não coloque um maior sobre outro, e assim é mais simples de ler e implementar se simplesmente chamar a mesma função de novo especificando um disco a menos
-
O post de arfneto em Entender um código pequeno c++ (recursão) foi marcado como solução
1. Isso é void. Apenas retorna ao chamador. É a definição.
2. Você entendeu o que é esse lance da torre de Hanoi?
A saída do programa você postou. Aquelas linhas são o resultado do cout, a unica saida do programa.
A razão de usar assim é porque para trocar os discos de um pino para outro é a mesma coisa se já tiver uma disco a menos, desde que não coloque um maior sobre outro, e assim é mais simples de ler e implementar se simplesmente chamar a mesma função de novo especificando um disco a menos
-
O post de arfneto em Entender um código pequeno c++ (recursão) foi marcado como solução
1. Isso é void. Apenas retorna ao chamador. É a definição.
2. Você entendeu o que é esse lance da torre de Hanoi?
A saída do programa você postou. Aquelas linhas são o resultado do cout, a unica saida do programa.
A razão de usar assim é porque para trocar os discos de um pino para outro é a mesma coisa se já tiver uma disco a menos, desde que não coloque um maior sobre outro, e assim é mais simples de ler e implementar se simplesmente chamar a mesma função de novo especificando um disco a menos
-
O post de arfneto em Lista em C, não estou conseguindo fazer essas listas em C. foi marcado como solução
Os 3 exercícios são a mesma coisa, desde que perceba isso antes e escreva em torno dos dados
Nesse primeiro caso são 2 listas, duas vezes a mesma coisa. Os demais tem uma lista só.
No desenho tem até as linhas em azul mostrando os tais ponteiros da lista.
Acho que já sabe que a lista é uma coleção de coisas onde cada coisa aponta para a coisa seguinte. Se não soubesse saberia depois de ver esse desenho aí.
Na hora de colocar algo na lista é claro que tem que ter um critério, e a realidade é simples: ou insere no início, ou no final ou por alguma ordem, como a ordem alfabética, a ordem de CPF, o total da compra, seja lá o que for.
Nesse exemplo acima parece óbvio que é melhor inserir no final, como a noção que a gente tem de fila, porque assim fica na simples ordem de entrada e é mais fácil combinar a bebida com o acompanhamento.
Escrevendo em torno dos dados
Está claro que cada exercício vai ter uma lista e está claro que você deve escrever uma vez só esse lance de lista porque é para isso que serve uma lista em um programa de computador. Você escreve algo que possa usar sempre, ou não vale muito a pena escrever afinal.
Uma lista tem algo dentro: nós.
Veja isso
typedef struct { int N; // tamanho Node* inicio; // o primeiro } Lista;
E já está bom. A Lista é de nós, cada um é chamado Node, o nome comum nos livros. E o total deles é N.
Um node é como esses do desenho, mas só o esqueleto, sem o conteúdo. E dentro dele tem um ponteiro para o dado, ou mesmo o dado, dependendo de como preferir escrever.
Uma tentativa para Node
typedef struct st_no { struct st_no* prox; Dado* dado; } Node;
Então dado aponta para o dado, que pode ser qualquer coisa, e prox aponta para o próximo Node, igualzinho no desenho. Se prox for NULL acabou a Lista. Como no desenho.
O tal st_no na declaração está lá porque tem um ponteiro dentro da estrutura para outra do mesmo tipo, e o nome Node só aparece na linha de baixo e assim não dá para escrever Node* dado antes de definir o que é Node, certo? E você não quer ficar repetido struct XXX pelo programa afora.
Uma tentativa para Dado
typedef struct { char item[20]; } Dado;
E já está bom: tem a lista de nós e cada nó aponta para um dado.
Como criar uma lista? Como apagar uma lista?
Para uma vida longa e tranquila, uma função pode criar a lista, outra pode apagar a lista e assim se funcionar está ok e se der m. já se sabe onde procurar. Se fizer diferente vai encher seu programa com ponteiros para início de uma e outra lista e ponteiros auxiliares e tal. Um inferno.
Claro, estamos programando estruturas de dados (listas) com tamanho dinâmico em C e tem uns ponteiros óbvios desde o enunciado... As linhas com a flecha...➡️ É muito mais simples ter ponteiros para frente e para trás na lista. Ponteiros só para um lado são um pesadelo porque... não dá para ir para o outro lado.
E assim criar e apagar lista podem ser tão simples como
Lista* criar(); Lista* apagar(Lista*);
Acho que dá pra entender não? Como criar e apagar uma lista em seguida?
int main(void) { Lista* minha_lista = criar(); apagar(minha_lista); return 0; }
Mas depois de apagar a lista minha_lista se o programa continuar o ponteiro vai ficar apontando para a lista que já foi apagada e se você acha que isso é um problema pode apostar que é. Então por isso é esperto depois de apagar a lista zerar o ponteiro, antes de usar por engano e cancelar o programa.
int main(void) { Lista* minha_lista = criar(); apagar(minha_lista); minha_lista = NULL; // apaga a referencia return 0; }
Mas isso é um porre e é mais uma coisa para lembrar então seria legal se apagar() retornasse o NULL para invalidar a referência para minha_lista na mesma linha...
int main(void) { Lista* minha_lista = criar(); minha_lista = apagar(minha_lista); return 0; }
Aí sim. Em outras linguagens se pode até exigir que o cara use o valor que a função retorna. Em C ele pode ser ignorado e se pode escrever como na primeira versão.
E como criar uma lista afinal?
Lista* criar() { Lista* L = (Lista*)malloc(sizeof(Lista)); if (L == NULL) return NULL; L->inicio = NULL; L->N = 0; return L; };
Não é só isso? aloca uma lista com tamanho zero e apontando para NULL como no desenho...
O simples. Dentro da lista está o tamanho da lista, dentro da lista tem o endereço de início da lista. Como eu sugeri antes seria claro melhor ter também o ponteiro para o último nó porque assim facilita muito percorrer a lista.
E como seria apagar uma lista?
Não seria equivalente a apagar todos os nós e depois apagar a própria lista? Sim. É só isso.
Lista* apagar(Lista* uma) { if (uma->N == 0) { // esta vazia free(uma); return NULL; }; // a lista tem pelo menos 1 cara // entao p aponta paa o segundo Node* p = uma->inicio->next; for (int i = 0; i < uma->N; i += 1) { free(uma->inicio); uma->inicio = p; // o proximo } free(uma); // apaga a lista em si return NULL; };
Se a lista está vazia apaga a lista em si e retorna, porque não tem dado nenhum. Se tem algum apaga os N dados. Antes de apagar o nó salva o endereço do próximo, porque depois já era. Antes de travar a porta confirme que tem a chave...
Sim, é verdade, @Lucca Rodriguesmas não é muito bom assim. Node é algo interno a Lista. O que se quer é inserir um Dado na Lista. Prefira sempre
int inserir(Dado* d, Lista* l)
Porque pode usar isso para qualquer dado. Só aqui tem 3 exercícios por exemplo.
@patopaloro sugiro que escreva nessa linha. É a maneira comum de fazer isso em qualquer linguagem. E é comum por alguma razão.
Veja no caso do exercício 1: você vai em um loop inserir as bebidas chamando inserir() em uma Lista de bebidas e outra Lista de acompanhamentos. Depois é só listar os N caras das duas listas que claro vão ter o mesmo tamanho.
Um "programa" até aqui, juntando o que está acima...
#include <stdio.h> #include <stdlib.h> typedef struct { char item[20]; } Dado; typedef struct st_no { struct st_no* next; Dado* dado; } Node; typedef struct { int N; // tamanho Node* inicio; } Lista; Lista* criar(); Lista* apagar(Lista*); int inserir(Dado*,Lista*); int main(void) { Lista* bebidas = criar(); Lista* comidas = criar(); apagar(bebidas); apagar(comidas); return 0; } Lista* apagar(Lista* uma) { if (uma->N == 0) { // esta vazia free(uma); return NULL; }; // a lista tem pelo menos 1 cara // entao p aponta paa o segundo Node* p = uma->inicio->next; for (int i = 0; i < uma->N; i += 1) { free(uma->inicio); uma->inicio = p; // o proximo } free(uma); // apaga a lista em si return NULL; }; Lista* criar() { Lista* L = (Lista*)malloc(sizeof(Lista)); if (L == NULL) return NULL; L->inicio = NULL; L->N = 0; return L; }; int inserir(Dado* d, Lista* L) { return 0; }
É claro que vai usar o mesmo código nos 3 programas. Só muda a definição de dado. Por isso é importante deixar os dados separados da estrutura. No fundo se cria um arquivo .h com as funções de lista e usa um #include em todos os programas. Mas deixa a definição de Dado de fora para poder trocar em cada programa
-
O post de arfneto em Lista em C, não estou conseguindo fazer essas listas em C. foi marcado como solução
Os 3 exercícios são a mesma coisa, desde que perceba isso antes e escreva em torno dos dados
Nesse primeiro caso são 2 listas, duas vezes a mesma coisa. Os demais tem uma lista só.
No desenho tem até as linhas em azul mostrando os tais ponteiros da lista.
Acho que já sabe que a lista é uma coleção de coisas onde cada coisa aponta para a coisa seguinte. Se não soubesse saberia depois de ver esse desenho aí.
Na hora de colocar algo na lista é claro que tem que ter um critério, e a realidade é simples: ou insere no início, ou no final ou por alguma ordem, como a ordem alfabética, a ordem de CPF, o total da compra, seja lá o que for.
Nesse exemplo acima parece óbvio que é melhor inserir no final, como a noção que a gente tem de fila, porque assim fica na simples ordem de entrada e é mais fácil combinar a bebida com o acompanhamento.
Escrevendo em torno dos dados
Está claro que cada exercício vai ter uma lista e está claro que você deve escrever uma vez só esse lance de lista porque é para isso que serve uma lista em um programa de computador. Você escreve algo que possa usar sempre, ou não vale muito a pena escrever afinal.
Uma lista tem algo dentro: nós.
Veja isso
typedef struct { int N; // tamanho Node* inicio; // o primeiro } Lista;
E já está bom. A Lista é de nós, cada um é chamado Node, o nome comum nos livros. E o total deles é N.
Um node é como esses do desenho, mas só o esqueleto, sem o conteúdo. E dentro dele tem um ponteiro para o dado, ou mesmo o dado, dependendo de como preferir escrever.
Uma tentativa para Node
typedef struct st_no { struct st_no* prox; Dado* dado; } Node;
Então dado aponta para o dado, que pode ser qualquer coisa, e prox aponta para o próximo Node, igualzinho no desenho. Se prox for NULL acabou a Lista. Como no desenho.
O tal st_no na declaração está lá porque tem um ponteiro dentro da estrutura para outra do mesmo tipo, e o nome Node só aparece na linha de baixo e assim não dá para escrever Node* dado antes de definir o que é Node, certo? E você não quer ficar repetido struct XXX pelo programa afora.
Uma tentativa para Dado
typedef struct { char item[20]; } Dado;
E já está bom: tem a lista de nós e cada nó aponta para um dado.
Como criar uma lista? Como apagar uma lista?
Para uma vida longa e tranquila, uma função pode criar a lista, outra pode apagar a lista e assim se funcionar está ok e se der m. já se sabe onde procurar. Se fizer diferente vai encher seu programa com ponteiros para início de uma e outra lista e ponteiros auxiliares e tal. Um inferno.
Claro, estamos programando estruturas de dados (listas) com tamanho dinâmico em C e tem uns ponteiros óbvios desde o enunciado... As linhas com a flecha...➡️ É muito mais simples ter ponteiros para frente e para trás na lista. Ponteiros só para um lado são um pesadelo porque... não dá para ir para o outro lado.
E assim criar e apagar lista podem ser tão simples como
Lista* criar(); Lista* apagar(Lista*);
Acho que dá pra entender não? Como criar e apagar uma lista em seguida?
int main(void) { Lista* minha_lista = criar(); apagar(minha_lista); return 0; }
Mas depois de apagar a lista minha_lista se o programa continuar o ponteiro vai ficar apontando para a lista que já foi apagada e se você acha que isso é um problema pode apostar que é. Então por isso é esperto depois de apagar a lista zerar o ponteiro, antes de usar por engano e cancelar o programa.
int main(void) { Lista* minha_lista = criar(); apagar(minha_lista); minha_lista = NULL; // apaga a referencia return 0; }
Mas isso é um porre e é mais uma coisa para lembrar então seria legal se apagar() retornasse o NULL para invalidar a referência para minha_lista na mesma linha...
int main(void) { Lista* minha_lista = criar(); minha_lista = apagar(minha_lista); return 0; }
Aí sim. Em outras linguagens se pode até exigir que o cara use o valor que a função retorna. Em C ele pode ser ignorado e se pode escrever como na primeira versão.
E como criar uma lista afinal?
Lista* criar() { Lista* L = (Lista*)malloc(sizeof(Lista)); if (L == NULL) return NULL; L->inicio = NULL; L->N = 0; return L; };
Não é só isso? aloca uma lista com tamanho zero e apontando para NULL como no desenho...
O simples. Dentro da lista está o tamanho da lista, dentro da lista tem o endereço de início da lista. Como eu sugeri antes seria claro melhor ter também o ponteiro para o último nó porque assim facilita muito percorrer a lista.
E como seria apagar uma lista?
Não seria equivalente a apagar todos os nós e depois apagar a própria lista? Sim. É só isso.
Lista* apagar(Lista* uma) { if (uma->N == 0) { // esta vazia free(uma); return NULL; }; // a lista tem pelo menos 1 cara // entao p aponta paa o segundo Node* p = uma->inicio->next; for (int i = 0; i < uma->N; i += 1) { free(uma->inicio); uma->inicio = p; // o proximo } free(uma); // apaga a lista em si return NULL; };
Se a lista está vazia apaga a lista em si e retorna, porque não tem dado nenhum. Se tem algum apaga os N dados. Antes de apagar o nó salva o endereço do próximo, porque depois já era. Antes de travar a porta confirme que tem a chave...
Sim, é verdade, @Lucca Rodriguesmas não é muito bom assim. Node é algo interno a Lista. O que se quer é inserir um Dado na Lista. Prefira sempre
int inserir(Dado* d, Lista* l)
Porque pode usar isso para qualquer dado. Só aqui tem 3 exercícios por exemplo.
@patopaloro sugiro que escreva nessa linha. É a maneira comum de fazer isso em qualquer linguagem. E é comum por alguma razão.
Veja no caso do exercício 1: você vai em um loop inserir as bebidas chamando inserir() em uma Lista de bebidas e outra Lista de acompanhamentos. Depois é só listar os N caras das duas listas que claro vão ter o mesmo tamanho.
Um "programa" até aqui, juntando o que está acima...
#include <stdio.h> #include <stdlib.h> typedef struct { char item[20]; } Dado; typedef struct st_no { struct st_no* next; Dado* dado; } Node; typedef struct { int N; // tamanho Node* inicio; } Lista; Lista* criar(); Lista* apagar(Lista*); int inserir(Dado*,Lista*); int main(void) { Lista* bebidas = criar(); Lista* comidas = criar(); apagar(bebidas); apagar(comidas); return 0; } Lista* apagar(Lista* uma) { if (uma->N == 0) { // esta vazia free(uma); return NULL; }; // a lista tem pelo menos 1 cara // entao p aponta paa o segundo Node* p = uma->inicio->next; for (int i = 0; i < uma->N; i += 1) { free(uma->inicio); uma->inicio = p; // o proximo } free(uma); // apaga a lista em si return NULL; }; Lista* criar() { Lista* L = (Lista*)malloc(sizeof(Lista)); if (L == NULL) return NULL; L->inicio = NULL; L->N = 0; return L; }; int inserir(Dado* d, Lista* L) { return 0; }
É claro que vai usar o mesmo código nos 3 programas. Só muda a definição de dado. Por isso é importante deixar os dados separados da estrutura. No fundo se cria um arquivo .h com as funções de lista e usa um #include em todos os programas. Mas deixa a definição de Dado de fora para poder trocar em cada programa
-
O post de arfneto em Função que recebe um inteiro como parâmetro foi marcado como solução
for(int i=0;i=N;i++){ s=s+((i*i)+1)/(i+3); }
Esse trecho é problemático.
Como colocou errado i=N na condição só vai sair do loop quando N for zero. Use i<=N. Em C 0 é falso. Qualquer outro valor é verdadeiro. o numerador é int e o denominador é int então antes de somar em s a divisão vai ser feita e gerar um valor inteiro. Converta um dos dois para float. Veja o exemplo abaixo. TESTE sempre o retorno de scanf(). Como está aprendendo ao menos no início dos testes mostre na tela os valores lidos. É muito melhor saber antes. E na série claro que deve mostrar os valores das parcelas. Se tivesse feito isso desde o início saberia do problema... Não precisa de chaves em comandos de uma linha só. Só fica mais difícil de ler. Se o programa só faz isso e você sabe o que, então passe N na linha de comando. Quando está testando use um loop para mostrar alguns valores. É bem melhor que ficar rodando o programa várias vezes. main() deve ser SEMPRE a primeira função de seu programa. Exemplo
#include<stdio.h> float soma(int); int main(void) { for ( int a=0; a<5; a+=1) printf("Soma = %.4f\n",soma(a)); return 0; } float soma(int N) { float s=0.f; printf("\n==> Calculando a soma para N = %d\n", N); for(int i=0;i<=N;i+=1) { float parcela = (i*i+1) / (float)(i+3); printf( " i = %d, parcela = %.4f\n", i, parcela ); s += parcela; } return s; };
Que mostra
==> Calculando a soma para N = 0 i = 0, parcela = 0.3333 Soma = 0.3333 ==> Calculando a soma para N = 1 i = 0, parcela = 0.3333 i = 1, parcela = 0.5000 Soma = 0.8333 ==> Calculando a soma para N = 2 i = 0, parcela = 0.3333 i = 1, parcela = 0.5000 i = 2, parcela = 1.0000 Soma = 1.8333 ==> Calculando a soma para N = 3 i = 0, parcela = 0.3333 i = 1, parcela = 0.5000 i = 2, parcela = 1.0000 i = 3, parcela = 1.6667 Soma = 3.5000 ==> Calculando a soma para N = 4 i = 0, parcela = 0.3333 i = 1, parcela = 0.5000 i = 2, parcela = 1.0000 i = 3, parcela = 1.6667 i = 4, parcela = 2.4286 Soma = 5.9286
E já tira umas dúvidas desde o início. Só DEPOIS você escreve algo como
#include<stdio.h> float soma(int); int main(void) { int a; float total=0; printf("Digite um numero: "); if ( 1 != scanf("%d",&a) ) return -1; printf("Soma = %.4f\n",soma(a)); return 0; } float soma(int N) { float s=0.f; for(int i=0; i<=N; s+= (i*i+1) / (float)(i+3), i+=1); return s; };
Apagando os printf() e os testes passo a passo...
Programação defensiva.
-
O post de arfneto em Usando ponteiros e alocação dinâmica, mas deu o erro "signal: segmentation... foi marcado como solução
Não consigo entender o que iria funcionar aqui. Não entendi o propósito do programa e não há razão para se programar assim, em C ou C++.
cout << "\nFunciona !\n";
Isso só mostraria que o programa não cancelou ainda. E que compilou ok. Não serve como um teste.
O que é ponteiro?
delete[] * ponteiro;
Há muitos anos, desde C++11 não há razão para escrever assim em C++. E mesmo em C++98 esse delete deveria estar em um destrutor e nunca solto pelo programa.
int main() { int** v; int x = Quest(); int** p; criar(p, x); cout << "\nFunciona !\n"; delete[] * ponteiro; }
já perguntei sobre ponteiro... e v seria o que? não foi alocado e jamais foi usado.
E Quest() serve apenas para ler um número então o que muda no programa em relação a escrever
int x = 42;
e seguir com seja lá o que for que o programa faz?
Ao que parece cria()
void criar(int** p, int x) { *p = new int[x]; for (int i = 0; i < x; i++) { p[i] = new int[x]; } }
apenas cria um vetor de tamanho x, o valor que foi lido por Quest(). Só que depois aloca um vetor de x int para cada posição de *p. Só que *p é int[] e não um ponteiro. Uma confusão.
E acho que está mesmo errado.
E ainda retorna void, um outro erro nesse caso.
O que pretende criar com cria() ? porque retorna void? Porque está usando C++ e escrevendo C?
Não está claro que cria() é um construtor????
Se cria(int** p, int x ) deveria criar em p um vetor de x vetores para int
Escrever assim só complica as coisas. Muito. Porque void se vai criar alocando? Não retorne void nunca. Isso se eu entendi o que quer que "funcione" aqui. Vou te mostrar dois exemplos considerando isso. Se não for assim acho que não vai ter dificuldade em adaptar para seu caso. Ou perguntar de novo.
Pode escrever
int** criar(int x);
E usar
int** p = criar(x);
Que é bem mais simples eu acho.
Mas entenda que
e só de ver isso não dá pra saber que *p é um ponteiro para um int ou um vetor.
Então se quer criar um vetor de vetores para int de tamanho x precisa de fato criar isso. Imagine que vai criar um vetor de x vetores para vetores de x int. Precisa saber se de fato criou isso. Não basta escrever "funciona". Eis um exemplo:
Imagine então que os valores em cada vetor serão assim, para x = 4
cria() vai retornar 4 vetores de ponteiros para int cada um desses vai ser um vetor de 4 int e eles vão valer para p[0] 0,1,2,3 para p[1] 10,11,12,13 para p[2] 20,21,22,23 para p[3] 30,31,32,33 Assim pode listar isso depois de chamar cria() e ver se deu certo porque já sabe os valores que tem que mostrar, certo? Espero que tenha dado para entender porque não basta escrever "funciona".
E depois de criar tem que apagar todo mundo na ordem inversa ou seu programar pode cancelar no final, porque TEM que liberar tudo que alocou...
Um exemplo em C++, no modo C dos anos 80
int** apagar(int**,int); int** criar(int x); int mostrar(int**,int);
O jeito comum de fazer em C, uma função cria o vetor, uma apaga e outra mostra. Quest() não é relevante aqui. Não há razão para escrever um programa interativo.
Essa versão de cria() usa o exemplo acima e preenche todas as posições de todos os vetores com valores conhecidos assim pode conferir facilmente:
int** criar(int x) { int** p = new int*[x]; for (int i = 0; i < x; i+=1) { p[i] = new int[x]; for ( int j = 0; j<x; j+=1 ) p[i][j] = 10*i + j; } return p; }
Essa versão de apagar() apaga tudo na ordem certa
int** apagar(int** p, int x) { for (int i = 0; i < x; i+=1) delete[] p[i]; delete[] p; return nullptr; }
Note que usando apagar assim permite que se invalide o ponteiro no chamador, o que é um seguro contra esquecer que apagou e usar o ponteiro em outro lugar...
Um exemplo completo
#include <iostream> using namespace std; int** apagar(int**,int); int** criar(int x); int mostrar(int**,int); int main() { int x = 8; int** p = criar(x); cout << "\nFunciona! x = " << x << "\n"; int res = mostrar(p,x); cout << "mostrar() listou " << res << " elementos\n"; cout << "apagando tudo...\n"; p = apagar(p,x); return 0; } int** apagar(int** p, int x) { for (int i = 0; i < x; i+=1) delete[] p[i]; delete[] p; return nullptr; } int** criar(int x) { int** p = new int*[x]; for (int i = 0; i < x; i+=1) { p[i] = new int[x]; for ( int j = 0; j<x; j+=1 ) p[i][j] = 10*i + j; } return p; } int mostrar(int** v, int d) { int n = 0; for (int i = 0; i < d; i+=1) { cout << "[" << i << "] "; for ( int j = 0; j<d; j+=1 ) n+=1, cout << v[i][j] << " "; cout << "\n"; } cout << "\n"; return n; };
A saída
Funciona! x = 8 [0] 0 1 2 3 4 5 6 7 [1] 10 11 12 13 14 15 16 17 [2] 20 21 22 23 24 25 26 27 [3] 30 31 32 33 34 35 36 37 [4] 40 41 42 43 44 45 46 47 [5] 50 51 52 53 54 55 56 57 [6] 60 61 62 63 64 65 66 67 [7] 70 71 72 73 74 75 76 77 Listados 64 elementos Apagando...
Claro que mostra() não precisava retornar nada, mas retornar o total de elementos dá mais uma segurança, já que deve ser claro x*x.
Um modo comum de fazer isso, desta vez em C++ 2011
v_int_int é a struct com os vetores de vetores de int. main() pode ser bem simples:
int main(void) { v_int_int teste(8); cout << teste; };
E o código é o mesmo praticamente. Só que em C++ é mais fácil de escrever e ler. Redefinir << é algo comum para uma struct em C++ porque assim pode usar cout << struct e escrever como quiser sem ter que ficar formatando em toda parte ou escrever uma função, o que seria a mesma coisa só que mais difícil de ler
criar() é simplesmente construir uma struct dessas. apagar() é o destrutor, com o til na frente. mostrar é o tal << e a vida segue.
O exemplo completo
#include <iostream> using namespace std; struct v_int_int { int x; int** p; v_int_int(int); ~v_int_int(); }; ostream& operator<<(ostream& out, v_int_int& c); int main(void) { v_int_int teste(8); cout << teste; }; v_int_int::~v_int_int() { cout << "\nApagando"; for (int i = 0; i < x; i+=1) delete[] p[i]; delete[] p; cout << "...\n"; } v_int_int::v_int_int(int tamanho) : x(tamanho) { cout << "\nFunciona! x = " << x << "\n"; p = new int*[x]; for (int i = 0; i < x; i+=1) { p[i] = new int[x]; for ( int j = 0; j<x; j+=1 ) p[i][j] = 10*i + j; } } ostream& operator<<(ostream& out, v_int_int& c) { int n = 0; for (int i = 0; i < c.x; i+=1) { out << "[" << i << "] "; for ( int j = 0; j<c.x; j+=1 ) n+=1, cout << c.p[i][j] << " "; out << "\n"; } out << "\nListados " << n << " elementos\n"; return out; };
A saída é a mesma.
No geral não é mais preciso alocar e liberar memória assim em C++, mas agora não posso escrever outro exemplo
-
O post de arfneto em Transformar essa lista simplesmente encadeada em uma simplesmente circular foi marcado como solução
// definição de uma lista simples typedef struct{ struct Musica *primeiro; char nome[30]; struct Musica *proxima; }Musica;
Sugiro que evite esse tipo de comentário. Comente o que está fazendo e porque está fazendo, como um recado para você mesmo daqui um tempo, ou para quem esteja lendo seu programa. E evite esses comentários meio que óbvios.
Sobre a estrutura
Evite ao máximo escrever assim.
Uma lista é um container. Pode ter qualquer coisa dentro. Mas a lista é feita de nós, os tais links, um que aponta para o outro. DENTRO desses links é que estão os dados. As músicas. Só que no próximo programa serão livros, carros, int, char.
Não misture as coisas. Só vai dificultar sua vida. A lista é genérica. Pode ser de qualquer coisa.
Veja esse exemplo:
typedef struct st_no { char nome[30]; } Musica;
Esse é o dado.
A lista é de nós:
typedef struct st_node { Info* info; struct st_node* ini; struct st_node* fim; } Node;
E a lista o que é?
Uma série de links. Seu modelo deve representar isso.
typedef struct { Node* ini; Node* fim; unsigned size; // tamanho } Lista;
O que falta?
typedef Musica Info;
Pronto: nesse programa a lista é de Musica.
No geral se usa void* como o tipo da lista, para ficar genérico. Pense nisso.
Em resumo, juntando tudo:
typedef struct { char nome[30]; } Musica; typedef Musica Info; typedef struct st_node { Info* info; struct st_node* ini; struct st_node* fim; } Node; typedef struct { Node* ini; Node* fim; unsigned size; // tamanho } Lista;
É muito mais simples programar assim. E seu próximo programa de listas pode ser o mesmo: a lista é uma lista, uma série de links de um Node para outro.
Sobre as funções
void inicializarMusica(Musica **musica); void inserirMusica(Musica **musica,char auxMusica[30]); void exibirMusicas(Musica **musica); void removerMusicaDaLista(Musica **musica,char auxMusica[30]);
Aqui você tem o reflexo do problema de que falei acima. Não retorne void. Não misture lista com musicas. Não tem sentido inicializarMusica por exemplo.
Se Musica é um dado que vai estar numa lista não deveria ter referência a Musica**. Pense nisso.
O normal:
Lista* inicializarLista(); int inserir_na_lista(Info* info, Lista* l); int exibir_musicas(Lista* lista); int remover_da_lista(Info* info, Lista* l);
Compare: Não tem referência a Musica na Lista. Apenas em exibir_musicas aparece a Lista porque afinal é onde estão as músicas.
E a lista circular?
Lista circular não existe. Trata-se apenas de uma disciplina de inserção. Você coloca um limite na lista e ao chegar a esse limite não cria mais nós: apenas passa a escrever o novo em cima do mais antigo. Só isso.
Algo assim:
typedef struct { Node* ini; Node* fim; unsigned size; // tamanho unsigned limite; // tamanho } Lista;
E aí dependendo do modelo você implementa a inserção de acordo. É só isso: um critério. O que fazer ao atingir o limite. Em relação a isso uma maneira comum de implementar a lista é ter um registro fixo, que é chamado de sentinela na literatura --- sentinel record --- para facilitar a programação: a lista vazia só tem o sentinela. E assim evita que o programa se perca no tal círculo de nós.
-
O post de arfneto em Qual o melhor compilador para c/c++? foi marcado como solução
Se você não tivesse o gcc não conseguiria usar essa extensão. Leu o que eu expliquei?
Uma vantagem de usar VS Code é que pode rapidamente alternar para outros compiladores e continuar usando um IDE.
No Windows os compiladores mais comuns são o da Microsoft, CL, o gcc, o Clang e o compilador da Intel. Todos requerem instalação, mesmo caso do Linux e do Mac.
Você marcou C# no tópico, mas está falando de C e C++...
-
O post de arfneto em Qual o melhor compilador para c/c++? foi marcado como solução
Se você não tivesse o gcc não conseguiria usar essa extensão. Leu o que eu expliquei?
Uma vantagem de usar VS Code é que pode rapidamente alternar para outros compiladores e continuar usando um IDE.
No Windows os compiladores mais comuns são o da Microsoft, CL, o gcc, o Clang e o compilador da Intel. Todos requerem instalação, mesmo caso do Linux e do Mac.
Você marcou C# no tópico, mas está falando de C e C++...
-
O post de arfneto em Qual o melhor compilador para c/c++? foi marcado como solução
Se você não tivesse o gcc não conseguiria usar essa extensão. Leu o que eu expliquei?
Uma vantagem de usar VS Code é que pode rapidamente alternar para outros compiladores e continuar usando um IDE.
No Windows os compiladores mais comuns são o da Microsoft, CL, o gcc, o Clang e o compilador da Intel. Todos requerem instalação, mesmo caso do Linux e do Mac.
Você marcou C# no tópico, mas está falando de C e C++...
-
O post de arfneto em Como usar C# de forma mais eficiente? foi marcado como solução
Pois é. Seu sistema roda talvez Windows em português e está configurado para usar o sistema métrico, separar os milhares com . e os decimais com vírgula?
O sistema está tentando ajudar você. Se é "ineficiente" para o seu caso mude as configurações... O simples.
Se usa Windows 10 pode apertar o botão iniciar e escrever "mudar região" e vai ver isso
Ou pode apertar Windows+C e perguntr para a Cortana em português: "Como mudar as configurações Regionais?" e ela vai te falar em português enquanto mostra o texto ao lado
E te todo modo vai acabar nessa janela
Onde você pode mudar o que achar mais eficiente.
No entanto isso vale de imediato para todos os programas, afinal trata-se de um padrão.
Você pode criar várias localidades com o nome que quiser e dar nomes e usar em seus programas, e para isso usa
aquela classe a que se referiu.
Isso também vale para a classe CultureInfo. E onde está a documentação daquilo?
Está onde está a absolutamente extensa documentação do fabricante.
E você pode ver exemplos em português, apesar de constantes tropeços do mecanismo de tradução, em https://docs.microsoft.com/pt-br/dotnet/api/system.globalization.cultureinfo?view=net-5.0 ou de acordo com a versão de .Net que use. No entanto acho que até a versão 6.0 e desde sempre nada tem mudado. E nem no Windows porque aquela tela que eu mostrei já tinha residência no Windows 95.
Um exemplo que está em https://docs.microsoft.com/pt-br/dotnet/api/system.globalization.cultureinfo?view=net-5.0 começa por
public static void Main() { // Creates and initializes the CultureInfo which uses the international sort. CultureInfo myCIintl = new CultureInfo("es-ES", false); // Creates and initializes the CultureInfo which uses the traditional sort. CultureInfo myCItrad = new CultureInfo(0x040A, false); // Displays the properties of each culture. ...
E acho que já entendeu que pode criar uma classe que define seu CultureInfo e importar no início para não ter que mudar no sistema.
E claro que pode escrever um script de 2 linhas e deixar um atalho no "acesso rápido" do Windows e que muda para de e para os locais que queira.
Tudo vai dar no mesmo lugar, as configurações regionais do Windows e a CultureInfo no C# vão acabar em alguma estrutura interna do sistema e uma função em C.
-
O post de arfneto em Dificuldade para configurar rede no Windows 10 foi marcado como solução
Vou te dizer o que eu disse antes: não precisa mexer no esquema. CRIE uma conta para autenticação. Não precisa sequer divulgar a senha. Uma mesma conta, em todos os micros, a mesma senha. O mesmo grupo, a mesma rede. Rede privada com a descoberta de rede ativada. Só isso.
Depois de criar a conta e fazer o compartilhamento você volta a máquina para a conta normal. E faz o que eu te expliquei. Ao acessar um compartilhamento na primeira vez claro que não vai se autenticar. Então vai pedir as credenciais. Você digita e salva. Pronto. O aluno pode usar a vida toda.
Se quiser se assegurar use um script de login e atribua uma letra para a pasta, como nos '90
-
O post de arfneto em Reproduzir imagem através de código foi marcado como solução
Isso eu te disse no primeiro momento...
Quando tem pressa deve ser ainda mais metódico
Esse era o plano 1, escrevendo em torno dos dados
Como te mostrei pela manhã...
Esse arquivo aí, com os 11 pontinhos. Primeiro lê e mostra, não adianta pensar em gravar se não ler p. nenhuma antes. E precisa ter um mínimo de segurança antes de gravar.
É tudo simples. Mas se atropelar vai levar dias....
image read_pts(char *name, int *nl, int *nc) { // -------------------------------------------- // Para Fazer // 1. abrir o arquivo texto 'nome' para leitura // 2. ler o número de linhas e colunas // 3. ler o número 'n' de pontos // 4. alocar o vetor 'img' com nl x nc // elementos // 5. preencher o vetor imagem com zeros // 6. ler as 'n' coordenadas (i, j) e preencher // a posição correspondente de img com 1. // --------------------------------------------
Você postou isso. Então faça isso. Com controle. Para dar certo da primeira vez
// 1. FILE *f = fopen(name, "r"); if (f == NULL) return NULL;
não existe modo "rt".
// 2. ler o número de linhas e colunas // 3. ler o número 'n' de pontos
Pois é:
// 2,3. int pt = 0; // total de pixels '1' int res = fscanf(f, "%d %d %d", nl, nc, &pt); if (res != 3) return NULL; // não leu direito, cancela
Não se preocupe com mensagens de erro e sim com o mais comum: ler e pronto. E use um printf() depois para ficar mais seguro de que está lendo o que acha que está lendo.
E TESTE a p. do resultado de fscanf().
E entenda que se não ler um dos 3 o programa já era de qualquer maneira. Você quer ler o que, no caso do J? 6, 10 e 11. Está lá no arquivo. Então coloca um printf() logo de cara. Vai ver os números certinhos e você apaga. Ou vai uns números enormes ou um crash e você corrige, MAS não segue em frente ou nunca vai terminar.
// 4. alocar o vetor 'img' com nl x nc // elementos
Não precisa declarar antes e ficar esperando. É uma chance a mais de errar algo. Escreva o simples
// 4. image img = alloc_memo(*nl, *nc);
// 5. preencher o vetor imagem com zeros
// 5. memset((void *)img, 0, sizeof(int) * (*nl) * (*nc)); // zera tudo
Basta ver o que alocou na tal rotina da linha de cima, certo?
image alloc_memo(int nl, int nc) { return (image)malloc(nl * nc * sizeof(int)); }
Está lá no código que recebeu...
// 6. ler as 'n' coordenadas (i, j) e preencher // a posição correspondente de img com 1. // --------------------------------------------
Pois é. O que vai ler aqui, no caso do J? Os 11 pontos. Acabou de ler esse valor. É só um loop
0 4 1 4 2 4 3 4 4 4 5 4 6 0 6 4 7 1 7 2 7 3
Vão colocar 1 em 11 pontos e depois mostrar os asteriscos no loop que te mostrei antes... E tanto faz digitar dois por linha ou 4 ou tudo numa linha só: fscanf() só entende uma coisa: espaço em branco. Pula tudo igualzinho, espaços, tabs, newlines, tanto faz. Só não pode ter letrinhas. Por isso é melhor aqui que fgets().
// 6. for (int n = 0, i = 0, j = 0; n < pt; n += 1) { // para os 'pt' pontos if (2 != fscanf(f, "%d %d", &i, &j)) { // deu erro: free(img); return NULL; } *(img + (i * *nc) + j) = 1; // marca o pixel }
Como te disse, ler todos de 2 em dois é mais fácil. E não é esperto deixar variáveis vivas pelo programa todo se só vai usar aqui, então pode declarar i e j no próprio loop. Se der erro deixa pra lá, retorna NULL. O programa não testa nada mesmo, como pode ver em main().
E copiar os caras é a conta simples que está no enunciado: seus "pixels" estão obviamente um depois do outro em img, como sabe. Foi o que alocou.
Então o pixel (i, j) está em
*(img + (i * *nc) + j) = 1; // marca o pixel
E deve estar tudo certo.
Mas para ficar mais seguro... Como te falei da primeira vez:
////////////////////////////////////////////////////////////////// // mostra o que leu int col = 6; pt = *nc * *nl; for (int i = 0; i < pt; i += 1) { if ( *(img + i) == 1 ) printf("* "); else printf(" "); if (++col % 6 == 0) printf("\n"); } printf("\n"); //////////////////////////////////////////////////////////////////
mostra na tela. De 6 em 6. Claro, porque J é 6x10 está no arquivo. A gene só quer saber se está indo no caminho certo. O tal programa depois vai mostrar a imagem de qualquer maneira, ou você pode usar uma imagem simples com uns 1o pontos e contar um por um já que esse modo P1 é texto e você sabe muito bem, porque está no enunciado...
E então ao ver isso na tela
* * * * * * * * * * *
já dá uma sensação de tranquilidade.
Estou mostrando tudo isso porque pode ajudar outros a ver que --- minha opinião --- sempre é mais fácil escrever em torno dos dados e seguir passo a passo. Em geral dá certo da primeira vez, porque esses problemas são simples e buscam mostrar um ou outro conceito por vez...
Eis a função toda:
image read_pts(char *name, int *nl, int *nc) { // -------------------------------------------- // Para Fazer // 1. abrir o arquivo texto 'nome' para leitura // 2. ler o número de linhas e colunas // 3. ler o número 'n' de pontos // 4. alocar o vetor 'img' com nl x nc // elementos // 5. preencher o vetor imagem com zeros // 6. ler as 'n' coordenadas (i, j) e preencher // a posição correspondente de img com 1. // -------------------------------------------- // 1. FILE *f = fopen(name, "r"); if (f == NULL) return NULL; // 2,3. int pt = 0; // total de pixels '1' int res = fscanf(f, "%d %d %d", nl, nc, &pt); if (res != 3) return NULL; // não leu direito, cancela // 4. image img = alloc_memo(*nl, *nc); // 5. memset((void *)img, 0, sizeof(int) * (*nl) * (*nc)); // zera tudo // 6. for (int n = 0, i = 0, j = 0; n < pt; n += 1) { // para os 'pt' pontos if (2 != fscanf(f, "%d %d", &i, &j)) { // deu erro: free(img); return NULL; } *(img + (i * *nc) + j) = 1; // marca o pixel } ////////////////////////////////////////////////////////////////// // mostra o que leu int col = 6; pt = *nc * *nl; for (int i = 0; i < pt; i += 1) { if ( *(img + i) == 1 ) printf("* "); else printf(" "); if (++col % 6 == 0) printf("\n"); } printf("\n"); ////////////////////////////////////////////////////////////////// fclose(f); return img; }
Pode estar errado, mas pode muito bem estar certo. Da primeira vez em que rodar.
-
O post de arfneto em Como concatenar uma string sem usar biblioteca especializada? foi marcado como solução
São 2 linhas na prática, mas veja assim
1 char* strcat ( char* dest, const char* nova ) 2 { char* p = dest; // vai retornar dest 3 while ( *p != 0 ) p+=1; // avanca 4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto 5 return dest; // retorna o mesmo 6 };
Está claro que strcat() retorna dest, certo?
E porque isso? Para poder usar numa expressão., como
Do exemplo:
char* p = strcat( origem, resto); printf( "A+B = \"%s\"\n", p );
podia ser
printf( "A+B = \"%s\"\n", strcat( origem, resto) );
Então é claro que o return vai ser
return dest;
a linha 5. E então tem que salvar esse valor ou não vai ter o que retornar. Por isso a linha 2
char* p = dest;
Porque assim usa p em vez de dest para poder retornar o cara inalterado....
Uma string em C não é uma coisa que exista: é uma convenção: uma série de coisas terminada por um zero.
Então pra juntar duas, nesse caso para colocar uma segunda no fim da primeira, tem que fazer duas coisas
achar o fim da primeira copiar até achar o fim da segunda É só isso.
Como no final da string concatenada vai ter um 0 porque é uma string afinal, e se sabe que no final da segunda já tem um zero porque também é uma string, isso resolve a escolha dos loops:
o while() testa antes, porque precisa parar ao achar o zero. O do() testa depois porque precisa incluir o 0, mesmo que a segunda string seja vazia, porque tem que ter um zero no fim. A linha 3 avança p, o ponteiro para a letrinha na string de destino, até encontrar um 0. Mas não passa do 0, porque é de onde começa a copiar a outra :
O asterisco é um operador e *p significa o conteúdo de seja lá para onde p aponte. Como p é char* será uma letra.
3 while ( *p != 0 ) p+=1; // avanca
A linha 4 copia as letras da string nova, até e incluindo o 0 para terminar a string de saída sem ficar fazendo testes.
4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto
quando o while encontra *nova == 0 e sai já copiou o 0 e a string de saída está completa. Os ++ avançam os ponteiros, e como vem DEPOIS da variável ele copia primeiro e soma depois.
E assim essa rotina deve ser bem mais rápida que a strcat() original, que usa memcpy() e strlen().
-
O post de arfneto em Como concatenar uma string sem usar biblioteca especializada? foi marcado como solução
São 2 linhas na prática, mas veja assim
1 char* strcat ( char* dest, const char* nova ) 2 { char* p = dest; // vai retornar dest 3 while ( *p != 0 ) p+=1; // avanca 4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto 5 return dest; // retorna o mesmo 6 };
Está claro que strcat() retorna dest, certo?
E porque isso? Para poder usar numa expressão., como
Do exemplo:
char* p = strcat( origem, resto); printf( "A+B = \"%s\"\n", p );
podia ser
printf( "A+B = \"%s\"\n", strcat( origem, resto) );
Então é claro que o return vai ser
return dest;
a linha 5. E então tem que salvar esse valor ou não vai ter o que retornar. Por isso a linha 2
char* p = dest;
Porque assim usa p em vez de dest para poder retornar o cara inalterado....
Uma string em C não é uma coisa que exista: é uma convenção: uma série de coisas terminada por um zero.
Então pra juntar duas, nesse caso para colocar uma segunda no fim da primeira, tem que fazer duas coisas
achar o fim da primeira copiar até achar o fim da segunda É só isso.
Como no final da string concatenada vai ter um 0 porque é uma string afinal, e se sabe que no final da segunda já tem um zero porque também é uma string, isso resolve a escolha dos loops:
o while() testa antes, porque precisa parar ao achar o zero. O do() testa depois porque precisa incluir o 0, mesmo que a segunda string seja vazia, porque tem que ter um zero no fim. A linha 3 avança p, o ponteiro para a letrinha na string de destino, até encontrar um 0. Mas não passa do 0, porque é de onde começa a copiar a outra :
O asterisco é um operador e *p significa o conteúdo de seja lá para onde p aponte. Como p é char* será uma letra.
3 while ( *p != 0 ) p+=1; // avanca
A linha 4 copia as letras da string nova, até e incluindo o 0 para terminar a string de saída sem ficar fazendo testes.
4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto
quando o while encontra *nova == 0 e sai já copiou o 0 e a string de saída está completa. Os ++ avançam os ponteiros, e como vem DEPOIS da variável ele copia primeiro e soma depois.
E assim essa rotina deve ser bem mais rápida que a strcat() original, que usa memcpy() e strlen().
-
O post de arfneto em Como concatenar uma string sem usar biblioteca especializada? foi marcado como solução
São 2 linhas na prática, mas veja assim
1 char* strcat ( char* dest, const char* nova ) 2 { char* p = dest; // vai retornar dest 3 while ( *p != 0 ) p+=1; // avanca 4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto 5 return dest; // retorna o mesmo 6 };
Está claro que strcat() retorna dest, certo?
E porque isso? Para poder usar numa expressão., como
Do exemplo:
char* p = strcat( origem, resto); printf( "A+B = \"%s\"\n", p );
podia ser
printf( "A+B = \"%s\"\n", strcat( origem, resto) );
Então é claro que o return vai ser
return dest;
a linha 5. E então tem que salvar esse valor ou não vai ter o que retornar. Por isso a linha 2
char* p = dest;
Porque assim usa p em vez de dest para poder retornar o cara inalterado....
Uma string em C não é uma coisa que exista: é uma convenção: uma série de coisas terminada por um zero.
Então pra juntar duas, nesse caso para colocar uma segunda no fim da primeira, tem que fazer duas coisas
achar o fim da primeira copiar até achar o fim da segunda É só isso.
Como no final da string concatenada vai ter um 0 porque é uma string afinal, e se sabe que no final da segunda já tem um zero porque também é uma string, isso resolve a escolha dos loops:
o while() testa antes, porque precisa parar ao achar o zero. O do() testa depois porque precisa incluir o 0, mesmo que a segunda string seja vazia, porque tem que ter um zero no fim. A linha 3 avança p, o ponteiro para a letrinha na string de destino, até encontrar um 0. Mas não passa do 0, porque é de onde começa a copiar a outra :
O asterisco é um operador e *p significa o conteúdo de seja lá para onde p aponte. Como p é char* será uma letra.
3 while ( *p != 0 ) p+=1; // avanca
A linha 4 copia as letras da string nova, até e incluindo o 0 para terminar a string de saída sem ficar fazendo testes.
4 do { *p++ = *nova++; } while ( *nova != 0 );// copia o resto
quando o while encontra *nova == 0 e sai já copiou o 0 e a string de saída está completa. Os ++ avançam os ponteiros, e como vem DEPOIS da variável ele copia primeiro e soma depois.
E assim essa rotina deve ser bem mais rápida que a strcat() original, que usa memcpy() e strlen().
-
O post de arfneto em funções e matrizes ISBN foi marcado como solução
Eu não entendo o que está tentando fazer.
Essa tabela aí fui eu que escrevi. O objetivo disso era calcular quando tempo cada uma dessas funções que eu escrevi, e mais essa outro que o usuário @Midoriescreveu, levam par calcular um certo n'mero de DV. E mostrar o resultado, uma tabela. Na posição 1 da tabela está o tempo com 0, que vai ser preenchido com os tempos calculados. E o valor será impresso foi no tópico #12 e a saída do programa era essa:
[Comparando as 4 funcoes] 2000 (*1.000) testes com valores aleatorios, mesma serie Rodando a versão oficial... ok! Rodando as outras versões. versão oficial = 1.0X 0.91X para versão pesos 1..9 1.10X para versão aprovada pelo professor :) 3.64X para versão @Midori
O programa não lê nada. Apenas roda as 4 funções para uma mesma série de 2 milhões de ISBN e mede o tempo. Leia o tópico #12. Era para testar 4 funções de cálculo.
Nada tem a ver com seu problema original, que é ler uns números de ISBN, e calcular o DV usando uma função, que pode por exemplo ser uma das que eu escrevi ou essa quarta aí.
Eu expliquei poruqe eu estaa escrevendo aquilo.
Assim me vem a pergunta:
Porque está usando essa estrutura no meio disso?
E onde está o resto do código? E porque mudou de IDE no meio disso tudo?
No tópico #5, https://www.clubedohardware.com.br/forums/topic/1561085-funções-e-matrizes-isbn/?tab=comments#comment-8259805 eu te mostrei esse programa, de onde tirou a função que está usando
/* * https://www.clubedohardware.com.br/ topic/1561085-fun%C3%A7%C3%B5es-e-matrizes-isbn/ ?tab=comments#comment-8259599 ARFNeto '21 */ #include <stdio.h> #include <stdlib.h> #include <string.h> char isbn_dv(char[9]); // a funcao char isbn_dv_oficial(char[9]); // com os pesos oficiais char isbn_dv_professor(char[9]); // como seu professor gosta int main(int argc, char** argv) { const char padrão[] = "isbn.txt"; char arquivo[30] = {0}; char linha[80]; if (argc > 1) // veio algo entao e o nome do arquivo strcpy(arquivo, argv[1]); else strcpy(arquivo, padrão); printf("Arquivo de entrada: \"%s\"\n", arquivo); FILE* entrada = fopen(arquivo, "r"); if (entrada == NULL) return -1; printf("Aberto \"%s\"\n", arquivo); char* p = fgets(linha, sizeof linha, entrada); while (p != NULL) { *(p+9) = 0; printf("%s-%c\t%s-%c\t%s-%c\n", p, isbn_dv( &linha[0]), p, isbn_dv_oficial( &linha[0]), p, isbn_dv_professor(&linha[0]) ); p = fgets(linha, sizeof linha, entrada); } fclose(entrada); return 0; } char isbn_dv(char isbn[9]) { int soma = 0; for (int d = 1; d <= 9; d += 1) soma += (isbn[d - 1] - '0') * d; int dv = soma % 11; if (dv == 10) return 'X'; return '0' + dv; }; char isbn_dv_oficial(char isbn[9]) { int soma = 0; for (int d = 10; d >= 2; d -= 1) soma += (isbn[10 - d] - '0') * d; int dv = 11 - (soma % 11); if (dv == 11) return '0'; if (dv == 10) return 'X'; return '0' + dv; }; char isbn_dv_professor(char isbn[9]) { int s1 = 0; int s2 = 0; for (int d = 0; d < 9; d += 1) s1 += isbn[d] - '0', s2 += s1; int dv = 11 - ((s1+s2) % 11); if (dv == 11) return '0'; if (dv == 10) return 'X'; return '0' + dv; };
É um programa completo. Te mostrei o arquivo de saída e o de entrada...
013162959X 0387901442 3540901442 0201179288
E a saída
Clube> ./or-0903 Arquivo de entrada: "isbn.txt" Aberto "isbn.txt" 013162959-X 013162959-X 013162959-X 038790144-2 038790144-2 038790144-2 354090144-2 354090144-2 354090144-2 020117928-8 020117928-8 020117928-8 Clube>
Onde cada coluna é o resultado de uma função.
O que falta aqui para o seu caso?
Tirar duas funções e colocar o lance do loop. Eu já te mostrei umas 4 vezes maneiras de fazer isso, e te mostrei em vários casos onde você está errando.
Já sabe o que mudar no loop. Eu te mostrei acima. Já tem a função. Eu escrevi 3.
Sequer precisa de um IDE para um programa de 50 linhas. E que já está pronto. Use os exemplos.
Juntando o que eu te disse o dia todo
char isbn_dv(char[9]); // a funcao int main(int argc, char** argv) { char arquivo[30] = {0}; char linha[80]; FILE* entrada = stdin; if (argc > 1) // veio algo entao e o nome do arquivo { strcpy(arquivo, argv[1]); printf("Arquivo de entrada: \"%s\"\n", arquivo); entrada = fopen(arquivo, "r"); if (entrada == NULL) return -1; printf("Aberto \"%s\"\n", arquivo); }; char final[] = "000000000"; linha[9] = 0; // tem que ser uma string afinal char* p = fgets(linha, sizeof linha, entrada); while ( (p != NULL) && (strcmp(linha,final) != 0) ) { linha[9] = 0; printf("%s-%c\n", p, isbn_dv(&linha[0])); p = fgets(linha, sizeof linha, entrada); linha[9] = 0; } fclose(entrada); return 0; } // fim
É só isso. E tem a vantagem de funcionar com qualquer arquivo na linha de comando ou lendo os dados do teclado, como seu professor folclórico recomenda. mesmo programa.
Veja (em Windows)
CH> isbn.txt | outro-0905 013162959-X 038790144-2 CH> outro-0905 < isbn.txt 013162959-X 038790144-2 CH>outro-0905 isbn.txt Arquivo de entrada: "isbn.txt" Aberto "isbn.txt" 013162959-X 038790144-2 CH> type isbn.txt 013162959X 0387901442 0000000000 3540901442 0201179288 CH>
Sobre o Clube do Hardware
No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais
Direitos autorais
Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais