Pergunta Extremamente Difícil C++ string [RESOLVIDO]

1. Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 16:03h

Não faço a mínima idéia do que está acontecendo, mas meu código está dando Segmentation fault apenas pq estou zerando um campo string! Não entendo o porque disso!

O Database acima tem 7 registros preenchidos! O que eu quero é apagar o campo abaixo
std::string Database[1000][7];

// Linha 2 Campo 4 estou apagando seu conteúdo e não quero mecher nos outros campos! Apenas isso!
Database[1][3] = ""; // Segmentation fault 0x00007ffff7daea29 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)

Não é assim que se apaga uma String? Em outros casos eu vou receber um valor vazio de algum controle, o Database precisa aceitar um valor vazio!

O que tá errado nesse código? HELP!
Porque o meu ACIMA não funciona? e o ABAIXO funciona? O que eu não estou conseguindo enxergar????

Apenas para acrescentar isso abaixo FUNCIONA
std::string Database[5][3] = { {"Linha 1", "1/2", "1/3"}, {"Linha 2", "2/2", "2/3"}, {"Linha 3", "3/2", "3/3"}, {"Linha 4", "4/2", "4/3"}, {"Linha 5", "5/2", "5/3"} };

Database[0][1] = "";


Acrescentando mais detalhes: Vale observar que no exemplo que dá problema, o campo do Database já está vazio!
Pois ele não recebeu nenhum valor, enquanto outros campos receberam.
Pois eu carrego o database de um arquivo! Então qdo não tem valor, meu LEITOR ignora aquele campo, pulando ele! Tipo assim:
campo 1 TEM VALOR NO ARQUIVO? Preenche!
campo 2 TEM VALOR NO ARQUIVO? NÃO? Então pulo pro próximo campo!

Ou seja, o campo nem é mechido ou modificado

Quando declaro assim, meu Database tá vazio!
std::string Database[1000][7]; 


Somente depois preencho com um Loop exemplo 2. OBS A necessidade do += é pq eles recebem 1 caracter de cada vez!
Database[0][0] += "A";
Database[0][1] += "B";
// Ou seja PULEI aqui o campo [0][2] não mechi nele.
Database[0][3] += "D";


E é justamente esse campo que não foi mechido que está BUGANDO qdo tento colocar nele um valor "" assim VAZIO!
Database[0][2] = ""; 


mas qual seria o problema de um campo VAZIO receber novamente VAZIO?

Estou muito perdido porque até isso abaixo funciona! Só no meu database que não aceita!
   std::string Database[5][3];
Database[0][1] = "";




  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/07/2020 - 20:53h

Caramba. Pensei que o programa fosse bem menor.

Em todo caso, isto aqui é errado. Remova sem pena.
      memset(&Database, 0, sizeof(Database)); 


Não sei se tem mais erros, mas isso aí não pode ficar.

Perceba que o tipo std::string não é um tipo simples. Ele tem ponteiros internos que têm de ser tratados pelo construtor da classe, e o construtor é chamado para cada string que você tiver. No caso de uma matriz de strings, o construtor é usado para cada elemento do array de arrays, ajustando valores dos campos internos de acordo com a necessidade, o que possivelmente significa que cada elemento vai apontar para um lugar de memória diferente. Ao chamar memset() você sobrescreve os valores desses ponteiros internos da classe e demais campos de controle.

Se você quer zerar todas as 7000 strings, basta deixar que o construtor default atue sobre cada elemento, que é o que acontece automaticamente por causa da declaração. Ele cria objetos válidos, contendo representações de strings vazias.

O programa abaixo serve para mostrar que os construtores default (i.e. que não recebem argumentos explícitos) são executados para arrays. Note que, no caso de arrays globais (ou qualquer variável global, na verdade), essas construções são feitas antes mesmo de main() ser invocada.
#include <iostream>

class myclass {
static unsigned glob_counter;
unsigned counter;

public:
myclass(): counter(++glob_counter) {
std::cout << "myclass object #" << counter << " @" << static_cast<void *>(this) << '\n';
}

~myclass(){
std::cout << "destroying object #" << counter << " @" << static_cast<void *>(this) << '\n';
}
};
unsigned myclass::glob_counter=0;

myclass matriz[3][4];

int main(){
std::cout << "main() has begun.\n";
do {
std::cout << "a will be created.\n";
myclass a;
std::cout << "end of life for a.\n";
} while(false);
do {
std::cout << "local array will be created.\n";
myclass local_array[3];
std::cout << "end of life for local array.\n";
} while(false);
std::cout << "end of main().\n";
}



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)

3. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/07/2020 - 18:17h

Não parece ter nada errado no trecho que você mostrou.

Possivelmente o problema está em algum outro pedaço, e está se manifestando ali apenas por acaso. Tem como mandar o programa inteiro? Aproveite e informe também as versões do S.O. e do compilador que você está usando.


EM TEMPO: A pergunta não é difícil. Talvez a investigação seja difícil para você, mas seria mais útil para todos nós, incluindo você, se você usasse títulos mais descritivos do problema. No caso em questão, você poderia ter colocado algo como “SIGSEGV ao usar matriz de std::string”.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


4. Re: Pergunta Extremamente Difícil C++ string

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 19:05h

paulo1205 escreveu:
Possivelmente o problema está em algum outro pedaço, e está se manifestando ali apenas por acaso. Tem como mandar o programa inteiro? Aproveite e informe também as versões do S.O. e do compilador que você está usando.

Vou postar o Código e os Arquivos prontos agora. Terei que colocar em um diver virtual pq não veio em anexo! Já posto o Link

Link com os arquivos: https://drive.google.com/file/d/1yYz5KlXE9-CSaz0jWbWNkSV7Z3WrqAas/view?usp=sharing

Explicando: Usei C++ e FLTK v.1.4.0
Compila assim: g++ Encyclopedia.cpp -o Encyclopedia -lfltk -lfltk_images -O3 -Wall -pedantic -pedantic-errors -Werror

Uso Slackware64 Current, g++ 9.2.0

IMPORTANTE: A pasta Database é importante pro programa funcionar ele pega as coisas nela!
O Programa pega o Path da Aplicação para localizar a pasta Database!

O Local do Problema é: void Bt_New_Save_Click(Fl_Button*, void*) {
Quando eu edito um registro!
Não está completo, visto que está com problema para o Database receber os valores!
Se localizar o texto: Segmentation fault Pq HORA funciona e HORA não?
É onde o problema começa!

Para simular, basta dar 2 cliques em qualquer registro, Clicar no Botão editar, e clicar em Salvar apenas isso!
O Problema ocorre pq CkPendency está desmarcado, E porque BxFolder está em branco, é outro lugar que o problema acontece!



5. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 19:23h

O Problema começou qdo eu estava fazendo isso:
Database[Grid->callback_row()][3] = (CkPendencyTips->value()? "x": "");
Database[Grid->callback_row()][6] = BxFolder->label();

Os 2 Comandos abaixo estão tentando colocar um valor VAZIO no Database, ou seja apagar o que existe nele! E que por acaso, não existe NADA, ele já era vazio pq NUNCA recebeu um valor desde o início do programa!


6. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 19:30h

Os Databases são simples arquivos de texto, vc pode abrir em qualquer Editor. Os Caracteres especiais são RS, US, ETX que nem todos os editores vão exibir! O geany abre tranquilamente, o Scite


7. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 20:00h

Link do FORM no Fluid que esqueçi de colocar, eu uso somente para posicionar os controles
https://drive.google.com/file/d/1JSY422ghvww3TbMk3N5yluZpyW4uog1g/view?usp=sharing


8. Re: Pergunta Extremamente Difícil C++ string

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/07/2020 - 21:34h

paulo1205 escreveu:

Não parece ter nada errado no trecho que você mostrou.

Possivelmente o problema está em algum outro pedaço, e está se manifestando ali apenas por acaso.


QED.

(Apesar de que não é exatamente por acaso: foi o momento em que ocorreu a materialização do acesso a dados referidos por ponteiros que foram indevidamente sobrescritos com valores inválidos. Só é acaso se a gente considera aquele memset() espúrio como algo imprevisível, que não deveria ter ocorrido, como se fosse uma falta de luz ou uma radiação cósmica.)


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


9. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 05/07/2020 - 23:04h

paulo1205 escreveu:
Em todo caso, isto aqui é errado. Remova sem pena.
      memset(&Database, 0, sizeof(Database)); 

Não sei se tem mais erros, mas isso aí não pode ficar.

Se você quer zerar todas as 7000 strings, basta deixar que o construtor default atue sobre cada elemento, que é o que acontece automaticamente por causa da declaração. Ele cria objetos válidos, contendo representações de strings vazias.

Pois é entendi sua explicação, pode ser ele causando o efeito e vou testar agora!

Mas eu preciso saber mesmo como ZERAR o Database, porque quando eu edito ou deleto um registro eu vou gravar o arquivo inteiro, Logo sendo um novo arquivo, eu vou carregá-lo para o Database. Se o Database possuir registros vai dar problema! Exemplo: Tenho 20 registros, se eu deletar 5. Eu vou carregar para o Database 15 registros apenas, porém ele por ter 20, os últimos 5 aparecerão! Pois ele já tinha registro!

Motivo pelo qual usei o memset, e também porque não achei nada equivalente em C++ para apagar um Database!
Eu tentei antes de usar memset:
// &#9658; NO_WORK &#10148; Não consegui fazer funcionar o fill, acho que só funciona para int
std::fill(Database, Database+3, 0); // Não funciona com string
std::fill(Database.begin(), Database.end(), 0); // Não funciona com string

Logo a única opção que sei fazer e que sobrou para mim foi essa abaixo, clonar ele em outro vazio, se não existir melhor forma:
// &#9658; OPTION II &#10148; Copiando para um vazio
std::string Database[1000][7];
std::string Database_Vazio[1000][7];
memcpy(Database, Database_Vazio, sizeof(Database_Vazio));

Sua explicação que vc deu sobre Class não compreendi direito, visto que declarei ele de forma simples, não criei uma Class! E ainda assim não entendi como seria o Destructor, eu meio que ainda não sei chamar um Destructor de uma Class.


10. Re: Pergunta Extremamente Difícil C++ string

Paulo
paulo1205

(usa Ubuntu)

Enviado em 06/07/2020 - 00:59h

Nick-us escreveu:

Pois é entendi sua explicação, pode ser ele causando o efeito e vou testar agora!

Mas eu preciso saber mesmo como ZERAR o Database,


Então você não entendeu a explicação.

porque quando eu edito ou deleto um registro eu vou gravar o arquivo inteiro, Logo sendo um novo arquivo, eu vou carregá-lo para o Database. Se o Database possuir registros vai dar problema! Exemplo: Tenho 20 registros, se eu deletar 5. Eu vou carregar para o Database 15 registros apenas, porém ele por ter 20, os últimos 5 aparecerão! Pois ele já tinha registro!


Eis uma boa razão para preferir std::vector a arrays nativos: você seleciona o registro que não quer mais e da um chama um erase nele, e não se preocupar com mais nada.

Mas eu imagino que você deva ter uma variável que diga quantos registros estão efetivamente em uso. Os que não estiverem em uso não precisam ir para o arquivo, e você pode apagar seus campos atribuindo-lhes strings vazias ou chamando std::string::clear().

Motivo pelo qual usei o memset, e também porque não achei nada equivalente em C++ para apagar um Database!


Não existe “Database” em C++, e a sua Database é um array de arrays de objetos do tipo std::string. E não existe mesmo, em C++ OU em C, nenhuma forma de manipular diretamente arrays; você só pode operar individualmente com seus elementos, seja por manipulação direta de cada um deles, seja chamando alguma função que receba um ponteiro para um conjunto de elementos mas que, internamente, também opera com cada um deles individualmente.

Eu tentei antes de usar memset:
// &#9658; NO_WORK &#10148; Não consegui fazer funcionar o fill, acho que só funciona para int
std::fill(Database, Database+3, 0); // Não funciona com string
std::fill(Database.begin(), Database.end(), 0); // Não funciona com string


Nenhuma das duas faz sentido. A primeira tenta atribuir um valor inteiro (0) a quatro diferentes arrays (cada elemento de Database é um array de strings), e a segunda tenta chamar os métodos begin() e end() sobre uma entidade cujo tipo não é uma classe (e portanto não tem métodos).

Logo a única opção que sei fazer e que sobrou para mim foi essa abaixo, clonar ele em outro vazio, se não existir melhor forma:
// &#9658; OPTION II &#10148; Copiando para um vazio
std::string Database[1000][7];
std::string Database_Vazio[1000][7];
memcpy(Database, Database_Vazio, sizeof(Database_Vazio));


Cada uma das declarações acima é suficiente para alocar 7000 (1000×7) strings vazias.

Funções como memset() e memcpy() só podem ser usadas com plain old data (POD). POD são, grosseiramente falando, dados que estão em uma destas categorias:
(i) tipos nativos simples (bool, char, short int, int, long int, long long int e suas variantes com unsigned ou signed explícito, float, double e long double, e nullptr_t), (ii) arrays cujos elementos sejam POD ou (iii) estruturas ou classes em que todos os membros sejam POD e não tenham funções-membros virtuais.

std::string não é POD (tem elementos internos que são ponteiros), portanto não deve ser manipulada como se fosse.

Sua explicação que vc deu sobre Class não compreendi direito, visto que declarei ele de forma simples, não criei uma Class! E ainda assim não entendi como seria o Destructor, eu meio que ainda não sei chamar um Destructor de uma Class.


Minha explicação não é sobre classes, mas sim sobre arrays cujos elementos são objetos de uma classe. Eu mostrei com uma classe minha justamente para deixar visíveis os pontos em que o construtor e o destrutor são chamados (implicitamente, tanto na declaração quanto no final do tempo de vida, respectivamente, e individualmente para cada elemento de cada array).



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


11. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 06/07/2020 - 01:34h

paulo1205 escreveu:
Eis uma boa razão para preferir std::vector a arrays nativos: você seleciona o registro que não quer mais e da um chama um erase nele, e não se preocupar com mais nada.

Bom saber disso! Preciso então aprender a usá-los de forma melhor! Eu até acho que sei fazer, o que me fez parar foi em algum momento achar que não seria exatamente necessário. Essa indicação sua me dá um caminho!

Mas eu imagino que você deva ter uma variável que diga quantos registros estão efetivamente em uso. Os que não estiverem em uso não precisam ir para o arquivo, e você pode apagar seus campos atribuindo-lhes strings vazias ou chamando std::string::clear().

Sim, isso eu sei fazer.

Não existe “Database” em C++, e a sua Database é um array de arrays de objetos do tipo std::string. E não existe mesmo, em C++ OU em C, nenhuma forma de manipular diretamente arrays; você só pode operar individualmente com seus elementos, seja por manipulação direta de cada um deles, seja chamando alguma função que receba um ponteiro para um conjunto de elementos mas que, internamente, também opera com cada um deles individualmente.

Eu até sei disso! Mas é como se eu estivesse cego! Tenho que evitar misturar as coisas! Obrigado por mais uma vez chamar atenção a casos assim!

Eu tentei antes de usar memset:
// NO_WORK Não consegui fazer funcionar o fill, acho que só funciona para int
std::fill(Database, Database+3, 0); // Não funciona com string
std::fill(Database.begin(), Database.end(), 0); // Não funciona com string

Nenhuma das duas faz sentido. A primeira tenta atribuir um valor inteiro (0) a quatro diferentes arrays (cada elemento de Database é um array de strings), e a segunda tenta chamar os métodos begin() e end() sobre uma entidade cujo tipo não é uma classe (e portanto não tem métodos).

Obrigado também por mais essa orientação, pq isso descartar o que agendei para aprender a fazer. Se não é bom, não farei! E mais importante a explicação do problema, pq me ajuda a entender como funciona as coisas!

Logo a única opção que sei fazer e que sobrou para mim foi essa abaixo, clonar ele em outro vazio, se não existir melhor forma:
// OPTION II Copiando para um vazio
std::string Database[1000][7];
std::string Database_Vazio[1000][7];
memcpy(Database, Database_Vazio, sizeof(Database_Vazio));

Cada uma das declarações acima é suficiente para alocar 7000 (1000×7) strings vazias.

Funções como memset() e memcpy() só podem ser usadas com plain old data (POD). POD são, grosseiramente falando, dados que estão em uma destas categorias:
(i) tipos nativos simples (bool, char, short int, int, long int, long long int e suas variantes com unsigned ou signed explícito, float, double e long double, e nullptr_t), (ii) arrays cujos elementos sejam POD ou (iii) estruturas ou classes em que todos os membros sejam POD e não tenham funções-membros virtuais.

std::string não é POD (tem elementos internos que são ponteiros), portanto não deve ser manipulada como se fosse.

Muito importante saber disso, pq eu não fazia idéia do problema!

Sua explicação que vc deu sobre Class não compreendi direito, visto que declarei ele de forma simples, não criei uma Class! E ainda assim não entendi como seria o Destructor, eu meio que ainda não sei chamar um Destructor de uma Class.

Minha explicação não é sobre classes, mas sim sobre arrays cujos elementos são objetos de uma classe. Eu mostrei com uma classe minha justamente para deixar visíveis os pontos em que o construtor e o destrutor são chamados (implicitamente, tanto na declaração quanto no final do tempo de vida, respectivamente, e individualmente para cada elemento de cada array).

Ok, vou estudar melhor esse seu código! Mas de qualquer forma as explicações detalhadas já me ajudaram! E sei que tenho muito o que fazer!
Vou nesse meio tempo decidir entre o formato atual ou se já coloco vector como controle, pq eu já havia percebido que vector trabalharia melhor para deletar registros no meio da Lista, só não continuei pq ficaram dúvidas ainda. Mas não cheguei a testar tudo!

Mas penso que neste exato momento, eu acho que inicialmente vou consertar o código, limpando corretamente as Strings, e ao concluir, mudo tudo para vector!




12. Re: Pergunta Extremamente Difícil C++ string [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 06/07/2020 - 03:07h

paulo1205 escreveu:
Em todo caso, isto aqui é errado. Remova sem pena.
memset(&Database, 0, sizeof(Database)); 

Não sei se tem mais erros, mas isso aí não pode ficar.

Apenas informando! ESSE foi mesmo o causador do problema! Removi como sugeriu e coloquei no lugar provisóriamente um for para todos os registros para testar o problema!

for(int x = 0; x < 1000; x++) {
Database[x][0].clear();
Database[x][1].clear();
Database[x][2].clear();
Database[x][3].clear();
Database[x][4].clear();
Database[x][5].clear();
Database[x][6].clear();
}

Como vc informou em textos anteriores, isso causou um BUG, misturando ponteiros e etc... O que dava Segmentation fault qdo eu tentava em outra parte do código apagar o campo! Removido o memset o problema acabou!

Muito Obrigado mesmo pela Ajuda e as Explicações!

Agora tentarei entender melhor os famosos vectors, que inicialmente me deixou a dúvida se declaro vector de string simples, ou se declaro vector de vector de string
É que vector simples de string me pareceu a primeira vista que é apenas uma lista de string.

Se eu declarar
std::vector<std::string> Database[5][3]; 

Eu estaria informando do mesmo jeito Qtd de Registros, embora eu desconfie que de para declarar
std::vector<std::string> Database; 

E Adicionar linhas e a Qtd de colunas que eu desejar igual eu faria abaixo, apenas não sei se é possível!

Assim. sei que posso eu mesmo definir a Qtd de linhas e Colunas, criando vetor de vetores, embora eu goste mais da forma acima se eu conseguir!
std::vector<std::vector<std::string>> Database;

std::vector<std::string> Row1 = { "Linha 1", "Linha 1", "Linha 1" };
std::vector<std::string> Row2 = { "Linha 2", "Linha 2", "Linha 2" };
std::vector<std::string> Row3 = { "Linha 3", "Linha 3", "Linha 3" };

Database.push_back(Row1);
Database.push_back(Row2);
Database.push_back(Row3);





  
01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts