C++: Segmentatie fout

Status
Niet open voor verdere reacties.

Voldemort

Gebruiker
Lid geworden
21 jul 2006
Berichten
111
Ik ging proberen hetvolgende te maken:

"regen" omkeren wordt "regen"
"aap" omkeren wordt "paa"

Je kan 1 woord ingeven, de code draait deze om en als het omgedraaide gelijk is aan het origineel, dan komt er "gelukt", anders "mislukt". Nu ging ik even testen met een string in een array steken en loop ik vast:

Code:
#include <iostream>
#include <string>

using namespace std;

int main()
{
	//Variablen aanmaken
	char* woord;
	int lengte;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in:";
	cin >> woord;

	const char* woord3 = woord;

	//Lengte ophalen
	lengte = strlen(woord3);

	//Array maken
	char* woord2 [lengte];

	//Woord in een array steken
	int i = 0;
	for(i = 0; i < lengte; i++)
	{
		int i2 = i+1;

		strncpy(woord2[i],woord3,i2);
	}

	int i3 = 0;

	for(i3 = 0; i3 < lengte; i3++)
	{
		cout << woord2[i3] << endl;
	}
	
	return 0;
}

De compiler geeft geen fouten. alleen als ik uitvoer (het gekregen a.out bestand) krijg ik een fout:

Geef een woord in:aap
Segmentatie fout

1) Waarom krijg ik een segmentatie fout? Wat is dat? Ik heb al even gekeken en het ligt aan die strlen, alleen zie ik de fout niet.
2) Is er een foreach lus in C++? Zoja, hoe werkt deze?
3) Kan ik waarde in een array stoppen zonder de key mee te geven, in PHP gaat het zo:

Code:
$array = array();

$array[] = "Nieuwe waarde erin";

Kan er ook zoiets in C++?
4) Wat vinden jullie van m'n code tot nu toe? Wat kan beter (buiten die ene segmentatie bug dan)?
 
Waarom krijg ik een segmentatie fout? Wat is dat?
Een segmentatiefout treed op wanneer je geheugen probeerd te gebruiken dat niet 'van jou' is. In jou programma zit dat o.a. hier:
Code:
	char* woord;
	int lengte;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in:";
	cin >> woord;
woord is een pointer naar een karakter, dus 1 geheugenadres/plek. Maar je probeerd daar een string van onbekende grote in te zetten en dus roep ga je geheugen gebruiken dat niet gebruikt mag worden: Segmentation fault.
Is er een foreach lus in C++?
Nee helaas.. Waarvoor zou je die willen gebruiken dan?
3) Kan ik waarde in een array stoppen zonder de key mee te geven, in PHP gaat het zo:
$array = array();
$array[] = "Nieuwe waarde erin";
Alleen met het initialiseren:
Code:
char array[] = "hallo";
4) Wat vinden jullie van m'n code tot nu toe? Wat kan beter (buiten die ene segmentatie bug dan)?
De gedachte is goed, er zitten nog wel meer fouten in:

const char* woord3 = woord;
dit moet zijn:
const int iets = strlen(woord);
char woord3[iets];


char* woord2 [lengte];
Dit kan niet, lengte zou als const int gedeclareerd moeten zijn.

int i2 = i+1;
strncpy(woord2,woord3,i2);

Dit zou kunnen zijn:
strncpy(woord2,woord3,i+1);

En nog een paar kleine dingetjes.
Als ik jou was zou ik een array declareren die 100% zeker groot genoeg is, daar de input ingooien. Alle andere arrays dezelfde (vaste) grote maken en dan controleren hoe vol je array zit.

Groeten Niek
 
De bedoeling van de array is dit:

het ingevoerde woord bv. "aap" in een array stoppen die er zo uit ziet:

array[0] = "a";
array[1] = "a";
array[2] = "p";

1) Mijn code is nu:

Code:
#include <iostream>
#include <string>

using namespace std;

int main()
{
	//Variablen aanmaken
	char* woord;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in:";
	cin >> woord;

	const int lengte = strlen(woord);

	//Array maken
	char* woord2 [lengte];

	//Woord in een array steken
	int i = 0;
	for(i = 0; i < lengte; i++)
	{
		strncpy(woord2[i],woord,i+1);
	}

	int i3 = 0;

	for(i3 = 0; i3 < lengte; i3++)
	{
		cout << woord2[i3] << endl;
	}
	
	return 0;
}

Nog steeds die segmentatiefout.

2) foreach is handig om een array uit te lezen en z'n keys meteen te kennen.

3) Mijn array is toch altijd groot genoeg. Een woord van 3 letters heeft maar 3 plaatsen nodig. Niet?
 
Nog steeds die segmentatiefout.
Dayt klopt, je hebt je array ook nog steeds verkeerd gedeclareerd. Zie ook het eerste gedeelte van mijn vorige post.
2) foreach is handig om een array uit te lezen en z'n keys meteen te kennen
als je 2 arrays hebt dan kun je de elementen ook direct overkopieren. Voorbeeld:

Code:
char a[4] = {'a','a','p','\0'};
char b[4];
for (int i=0; i<strlen(a)-1; i++)
{
b[i] = a[3-i]
}

voila: de string is omgedraait!

3) Mijn array is toch altijd groot genoeg. Een woord van 3 letters heeft maar 3 plaatsen nodig. Niet?
Eigenlijk 4 plaatsen omdat er ook nog een '\0' bijhoort. Maar ik had ook begrepen dat je er ook langere woorden mee wou testen.

Groeten Niek
 
Wat moet ik dan veranderen voor die fout? Ik dacht dat char* de manier was om een string te maken. Niet dan? Kan je even verduidelijken? Als ik het in string aanpas geeft strlen fouten bij het compilen. Dus in wat verander ik het dan?

Edit: Ik heb wat zitten opzoeken en ik zag dat een string eigenlijk al een array is. Dus ik heb een heel stuk code weggedaan. Ik heb ook die char* naar string vernaderd. Dit is m'n huidige code:

Code:
#include <iostream>
#include <string>

using namespace std;

int main()
{
	//Variable aanmaken
	string woord;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in: ";
	cin >> woord;

	//Lengte ophalen
	const int lengte = strlen(woord);

	int i;
	for(i = 0; i < lengte; i++)
	{
		cout << woord[i] << endl;
	}
	
	return 0;
}

En de compiler (g++) zegt

Palindroom.cpp: In function ‘int main()’:
Palindroom.cpp:16: error: cannot convert ‘std::string’ to ‘const char*’ for argument ‘1’ to ‘size_t strlen(const char*)’

strlen, de lengte van een string berekenen, dan verwacht je toch dat ie een string verwacht? Het moet een const char* zijn lees ik. Maar aan een constante heb ik niks. Dus moet ik nu const char* woord2 = woord; doen en dan woord2 in de strlen zetten? Is er geen betere manier?
 
Laatst bewerkt:
PHP:
#include <iostream>
#include <string>

using namespace std;

int main()
{
	//Variablen aanmaken
	char woord[256];
	char* palin;
	int lengte;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in:";
	cin >> woord;
    
    lengte = strlen(woord);
    
    palin = new char[lengte + 1];
    palin[lengte] = 0;
    for(int p=0;p<lengte;p++){
           palin[lengte-p-1] = woord[p];
    }
    cout << "Palin: ";
    cout << palin;
    delete palin;
}
 
Die werkt, maar kan je even uitleggen waarom:

- Wat de mijne verkeerd doet en wat eraan te veranderen.
- Je eigen code even meer uitleggen.

Dit dingetje maak ik om C++ te leren, daarom dat ik graag wat extra info zou willen.

Wat is het verschil tussen char en string?
 
Wat is het verschil tussen char en string?

het char-type dient om 1 karakter op te slaan, als je een reeks tekens wil opslaan heb je dus een reeks (-> array) van deze char's nodig. Het nadeel van een array is wel dat je op voorhand moet weten hoeveel elementen hij heeft. Dus als je iemand iets liet invoeren moest je een beperking op het aantal tekens invoeren, niet altijd even praktisch. Het gebruik van char-array's komt voort uit de C-taal

Omdat zoveel mensen deze string's nodig hadden, is er bij het maken van de C++-taal beslist om het string-type in te voeren. Dit is een klasse met een hele hoop voorgedefiniëerde functies (diegene die het meeste voorkomen). Eigenlijk verzorgt deze klasse het aanmaken van een char-array van de juiste grootte. In machinecode gebeurt dus nog steeds ongeveer hetzelfde, alleen moet jij als programmeur je hoofd niet langer breken over correct geheugengebruik en of je string wel groot genoeg is of niet...

Om gebruik te maken van deze mogelijkheden dien je de <string>-header te includen, waarin al deze methodes beschreven zijn.

Het , in mijn ogen een van de, grotere voordeel van een string-object is dat je het nog steeds als een char-array kan gebruiken, dus je kan zoiets doen:
Code:
string s = "test";
char c = s[2]; //c == 's'

--Johan
 
Waarom wil strlen dan een char hebben? Als ik niet weet hoeveel letters het woord bevat, dus ik wil een string, hoe moet je daar dan de lengte van bepalen? Zo zijn er vast nog functies met dit probleem. Kan iemand verduidelijken?
 
strlen is nog een functie die overkomt vanuit C. als je met een string-object werkt van C++ kan je de lengte op volgende manieren opvragen:
Code:
string s = "test";
int lengte = s.length();
int dezelfde_lengte = s.size();

Zowel .size() als .length() geven met de string "test" de waarde 4

--Johan
 
Ik heb het programma uitgebreid en het werkt helemaal:

Code:
#include <iostream>
#include <string>

using namespace std;

string get_woord()
{
	string woord;

	//Woord vragen en in variable stoppen
	cout << "Geef een woord in: ";
	cin >> woord;

	return woord;
}

//Functie om het omgedraaide op te halen
string get_omgedraaid(string woord)
{
	string omgekeerd;
	int lengte2;

	//Lengte ophalen
	const int lengte = woord.length();
	lengte2 = woord.length();

	//Het omgedraaide berekeken door een forlus
	for(int i = 0; i < lengte; i++)
	{
		lengte2--;

		omgekeerd += woord[lengte2];
	}

	//Het omgekeerde teruggeven
	return omgekeerd;
}

void result(string woord, string omgekeerd)
{
	cout << "Het omgekeerde is " << omgekeerd;

	if(omgekeerd == woord)
		cout << ", dit is een palindroom.";
	else
		cout << ", dit is geen palindroom";

	cout << endl << endl;
}

int nog_eens()
{
	int answer;
	string answer2;

	cout << "Nog een woord invoeren? (yes/no) ";
	cin >> answer2;

	if(answer2 == "yes")
		answer = 1;
	else if(answer2 == "no")
		answer = 0;
	else
		answer = 2;

	return answer;
}

int main()
{
	//Variable aanmaken
	string woord, omgekeerd;
	int inog_eens = 1;

	while(inog_eens == 1)
	{
		woord = get_woord();
		omgekeerd = get_omgedraaid(woord);
		result(woord, omgekeerd);
		inog_eens = nog_eens();
	}

	if(inog_eens == 0)
		return 0;
	else
	{
		cout << "U gaf een ongeldig antwoord." << endl;

		inog_eens = nog_eens();
	}
}

1) Alleen is er 1 raar ding. Als ik in de functie result die if/else verander in:

Code:
cout << (omgekeerd == woord) ? ", dit is een palindroom." : ", dit is geen palindroom";

Dan staat er na in de console:

Het omgekeerde is [hier het omgekeerd][een 1 of een 0]

(de dingen tussen de [] zijn veranderlijk, vandaar de [] ;))

Waarom staat daar een 1 of een 0 als ik het verkort schrijf?

2) Wat vinden jullie van deze code?
 
Laatst bewerkt:
Ik heb het programma uitgebreid en het werkt helemaal:
1) Alleen is er 1 raar ding. Als ik in de functie result die if/else verander in:

Code:
cout << (omgekeerd == woord) ? ", dit is een palindroom." : ", dit is geen palindroom";

Dan staat er na in de console:
console zei:
Het omgekeerde is [hier het omgekeerd][een 1 of een 0]

(de dingen tussen de [] zijn veranderlijk, vandaar de [] ;))

Waarom staat daar een 1 of een 0 als ik het verkort schrijf?

Je probleem ligt in de plaats van de cout in die code, als je jouw code terug naar een if/else herschrijft krijg je dit:
Code:
cout << 
      if (omgekeerd == woord)
              ", dit is een palindroom.";
      else
              ", dit is geen palindroom";
wat niet meer hetzelfde is als het oorspronkelijke, die 1 of 0 komt van de uitvoering van (omgekeerd==woord), wat waar of vals is (dus 1 of 0).

Ik denk dat de correcte verkorte code dit is (ongetest!):
Code:
(omgekeerd == woord) ? cout << ", dit is een palindroom." : cout << ", dit is geen palindroom";


2) Wat vinden jullie van deze code?
Ik vind ze zeker niet slecht. Op het eerste zicht zou ik toch 1 ding anders aanpakken (maar dat is daarom niet beter).
Ik zou van de functie nog_eens() een bool maken, true als er nog eens dient ingelezen te worden, false om het programma te stoppen, en de loop op een ongeldeige waarde in die functie zelf plaatsen.

Een verdere verbetering ligt bij het gebruike van referenties/pointers bij de argumenten, maar daarvoor raad ik je aan eerst zelf op onderzoek uit te trekken over references, zodat je goed weet wat ze juist zijn en doen.

--Johan
 
Ik snap ongeveer wat een pointer is, alleen snap ik het nut er niet van. Wanneer moet je nou een geheugenadres gebruiken? Zo werkt het toch ook? Is het dan zo'n groot verschil?
 
Ik snap ongeveer wat een pointer is, alleen snap ik het nut er niet van. Wanneer moet je nou een geheugenadres gebruiken? Zo werkt het toch ook? Is het dan zo'n groot verschil?

Het komt neer op geheugengebruik, neem volgende code:
Code:
//test of een getal positief is
bool test1(int getal) {
    if (getal > 0)
         return true;
    return false;
}

//test of een getal deelbaar is door 2
bool test2(int getal) {
    if (getal%2 == 0)
        return true;
    return false;
}

void test(int getal) {
    if ( test1(getal) && test2(getal) )
         cout << "getal voldoet aan de voorwaarden!";
    else
         cout << "getal voldoet niet aan de voorwaarden!";
}
bij elke aanroep van een van deze functies wordt niet met het getal zelf gewerkt maar met een kopie ervan, dus verbuik je extra geheugen. voor 1 variable maakt dat natuurlijk niet uit.
Maar wat als je een array met een duizendtal elementen moet meegeven? Elke keer die 1000 elementen kopiëren zou niet alleen veel tijd, maar ook veel ruimte innemen. Terwijl je gewoon het adres van de array kan meegeven, wat neer komt op 999 minder gegevens die moeten gekopiëerd worden!

Er is nog een geval waarin het handiger kan zijn om met references/pointers te werken. Stel dat je een functie wil schrijven die een lijst getallen sorteert, dan merk je dat je altijd opnieuw 2 getallen moet vergelijken en dan eventueel van plaats wisselen. Nu kan je dit makkelijk in een aparte functie doen. Op het eerste zicht zou je zoiets doen:
Code:
void wissel(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
Binnen die functie zijn de waardes nu wel omgewisseld, maar eenmaal je eruit komt is alles weer zoals het ervoor was, omdat er op een kopie van je variabelen is gewerkt. Als je nu in plaats van de variabele, het adres meegeeft, kan je zeggen: "Vervang wat op die plaats in het geheugen zit". Je krijgt dan deze code:
Code:
void wissel(int& a, int& b) {
     int temp = a;
     b = a;
     a = temp;
}
Na het gebruik van deze functie zal de waarde van a en b ook effectief in de rest van het programma omgewisseld zijn.

--Johan
 
Dus in mijn code zou ik beter met pointers werken bij de functies? De functies waar ik als argumenten een string meegeef? Zijn er zo nog plaatsen in die code waar ik dat beter doe? Eigenlijk overal waar de gebruiker de variablen kan beïnvloeden (bv. int lengte, string woord, string omgekeerd)? Of redeneer ik nu verkeerd?
 
Wat ik er van zou maken:


get_woord krijgt de variabele waarin ie moet opslaan als reference mee (dus het adres), zo wordt het woord ook rechtstreeks ingelezen.

get_omgedraaid in plaats van lengte2 altijd opnieuw te bereken gewoon de variabele van woord.lenght()-1 tot 0 laten aftellen, lijkt me heel wat eenvoudiger ;)

result is ok zo, het is hier niet nodig om references te gebruiken, aangezien er niet hoeft gewijzigd te worden.

nog_eens vrij ingrijpend veranderd naar een boolean, om eerder vermelde reden

main aangepast aan andere aanpassingen :D

Code:
#include <iostream>
#include <string>

using namespace std;

void get_woord(string& woord)
{
	//Woord vragen en in variable stoppen
	cout << "Geef een woord in: ";
	cin >> woord; cin.get();
}

//Functie om het omgedraaide op te halen
string get_omgedraaid(string woord)
{
	string omgekeerd = "";
	//Lengte ophalen
	const int lengte = woord.length();

	//Het omgedraaide berekeken door een forlus
	for(int i = (lengte - 1); i >= 0; i--)
	{
		omgekeerd += woord[i];
	}
	//Het omgekeerde teruggeven
	return omgekeerd;
}

void result(string woord, string omgekeerd)
{
	cout << "Het omgekeerde is " << omgekeerd << ", dit is ";

	if(omgekeerd != woord)
		cout << "g";

	cout << "een palindroom" << endl << endl;
}

bool nog_eens()
{
    //startswaarde voor anwser == "no", zo werkt loop correct de eerste maal
	string answer="no";

    while (true) {
        cout << "Nog een woord invoeren? (yes/no) ";
        cin >> answer; cin.get();
        if (answer == "no")     //antwoord was NEE
            return false;
        if (answer == "yes")    //antwoord was JA
	        return true;
        cout << "Ongeldige keuze!";
    }
	return false;
}

int main()
{
	//Variable aanmaken
	string woord, omgekeerd;
	//het programma laten starten en uitvoeren totdat men "no" ingeeft
	do {
		get_woord(woord);
		omgekeerd = get_omgedraaid(woord);
		result(woord, omgekeerd);
	} while (nog_eens());
    return 0;
}

stel gerust vragen mocht je een bepaald deel niet goed begrijpen,
--Johan
 
Mijn vragen op jouw code:

1) while(true) in nog_eens ==> while(true) geeft toch een oneindige lus? Hoe komt het dat dat hier niet gebeurt?
2) in result, waarom daar geen references? Nu kopieer je toch die strings woord en omgekeerd wat toch extra geheugen vraagt?
3) Waarvoor dient die do in main?
4) Waarom doe je enkel pointers als er iets gewijzigd moet worden? (zie ook vraag 2)
5) Welke dingen in de console zou ik nog kunnen maken om C++ te oefenen?
 
1) while(true) in nog_eens ==> while(true) geeft toch een oneindige lus? Hoe komt het dat dat hier niet gebeurt?
while(true) geeft inderdaad een oneindige lus. Velen vinden dit dan ook geen 'mooie' programmeerpraktijk, omdat er op het eerste zicht een oneindige lust wordt gemaakt. Nu zijn er 2 manieren om uit een lus voortijdig te beëindigen:
1. break; -> gaat gewoon uit de lus en de code er net onder wordt verder uitgevoerd
2. return; -> de hele functie of methode wordt stilgelegd en er wordt eventueel een bepaalde waarde teruggestuurd naar de oproeper van die functie (dit is wat ik gedaan heb). In 'leestaal' schreef ik:
Code:
Doe altijd deze controlestructuur, maar als ik "no" of "yes" ben, stop dan met mezelf uit te voeren en geef een gepaste waarde weer.

2) in result, waarom daar geen references? Nu kopieer je toch die strings woord en omgekeerd wat toch extra geheugen vraagt?
Als je een reference gebruikt vraag je het adres op, maar dat adres moet ook ergens bewaard blijven. In plaats van een kopie van de variabele op te slaan, sla ik eigenlijk een kopie van zijn adres op. Pointers en references nemen dus ook wel degelijk extra geheugen in! In het geval van een enkelvoudige variabele (elke variabele die geen array, struct, class-instantie is) maakt de keuze tussen pointer/reference/kopie dus vaak geen verschil.

3) Waarvoor dient die do in main?
Die do hoort bij die while. In C++ bestaan er 3 basisstructuren om een lus te schrijven, de for-lus, de while-lus en de do-while-lus.
de For-lus
Code:
for (startwaarde; eindeconditie; waardeverandering) { commando's }
bvb

for (int i = 1; i < 10; i = i + 2) {
       cout << "i = " << i << endl;
}
de While-lus
Code:
while (voorwaarde) { commando's }
bvb

int i = 1;
while (i < 10) {
        cout << "i = " << i << endl;
        i = i + 2;
}
de Do-while-lus
Code:
do { commando's } while (voorwaarde);  //let op de puntkomma op het einde!
bvb

int i = 1;
do {
        cout << "i = " << i << endl;
        i = i + 2;
} while ( i < 10 );
Waar zit het verschil tussen while en do-while? bij while wordt de voorwaarde nageken VOOR de lus wordt uitgevoerd, bij do-while is dat ERNA. een klein voorbeeldje
Code:
int i = 10;
//deze lus zal nooit uitgevoerd worden
while (i < 0) {
       cout << "in while" << endl;
       i++;
}
cout << "de waarde van i na de while-lus = " << i << endl;
//deze lus zal echter eenmaal uitgevoerd worden
do {
        cout << "in do-while" << endl
        i++;
} while (i < 0);
cout << "de waarde van i na de do-while-lus = " << i << endl;

4) Waarom doe je enkel pointers als er iets gewijzigd moet worden? (zie ook vraag 2)
Zoals gezegd in mijn antwoord in vraag 2, maakt het gebruik van pointers/references op enkelvoudige variabelen qua geheugengebruik weinig uit. Het enige voordeel dat ze dan opleveren is dat je jouw variabele rechtstreeks kan aanpassen, want je hebt het adres ervan.
Zoals ook al gezegd gebruik je pointers niet enkel als er iets moet aangepast worden, maar ook voor het doorgeven van samengestelde variabelen, omdat er daar wel een geheugenvoordeel meespeelt.

5) Welke dingen in de console zou ik nog kunnen maken om C++ te oefenen?
Als je wil, rakel ik mijn oefeningen van in de les van vorig jaar even op. Je mag je aan een persoonlijk bericht verwachten met de opgaves dan.
Ik stel wel voor dat als je daar ook op problemen zou stuiten je weer een topic opent, wie weet zitten anderen hier met eenzelfde probleem en komen ze er dan ook uit :)

--Johan
 
Dus enkel pointers voor arrays, struct en class-instantie?

1) Een string is toch zowiezo een array, waarom op een string dan niet?
2) Kan je van elk van de 3 dingen (arrays, struct en class-instantie) eens een simpel pointer voorbeeld geven?
3) Ik ben geïnteresseerd in je oefeningen, zo kan ik zo wat oefenen.
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan