Permalänk
Medlem

Programmeringshjälp C

Tjenare!

Sitter här med en programmeringsuppgift och har fastnat totalt på vissa av deluppgifterna. Uppgiften till hela handlar om ett textprogram där man ska först välja alternativ, exempelvis split text, ändra från liten till stort bokstav och tvärtom eller addera en bokstav i slutet av varje ord. Jag har lyckats lösa stor/liten bokstav, samt split text (även om den inte är med i denna kod, klasskompisen som har den versionen).

Det jag inte får att funka är skriva ut en bokstav i arrayen. Som ni ser i koden nedanför så har jag skapat två fält, text[100] och text1[100]. Sen när man väljer case och skriver in sina meningar, exempelvis "Hej, det är fint väder i Sverige idag" och lägger den i text, så får jag inte det att skriva ut text[1], där "e" bör ligga. Det blir inget fel när jag debuggar, det är bara att den printar en tom rad när jag använder [printf("%s", text[1]);. Har setat och testat sen igår utan att komma någon någonvart, testat kopiera över text till text1.

Jag antar att jag gör något kopiöst fel när jag anropar en bokstav i stringen eller att jag gör något fel när jag läser in skriften till variabeln. Löser jag detta så ska jag kunna lösa de ofärdiga casen förhoppningsvis av mig själv.

Så, någon där ute som kan hjälpa en stackare?

#include <stdio.h> #include <string.h> #include <locale.h> #include <ctype.h> void tolower_str(char* to, const char* from) { int i = 0; for (; from[i] != '\0'; i++) to[i] = tolower(from[i]); to[i] = '\0'; } void toupper_str(char* to, const char* from) { int i = 0; for (; from[i] != '\0'; i++) to[i] = toupper(from[i]); to[i] = '\0'; } void count_word(char* a) { int i = 0, word = 0; for (; a[i] != '\0'; i++) if (isspace(a[i])) { ++word; } printf("Words: %d\n", word); } void count_space(char* a) { int i = 0, space = 0; for (; a[i] != '\0'; i++) if (isblank(a[i])) { ++space; } printf("' ': %d\n", space); } int main() { int choice; char text[100], text1[] = ""; printf("Enter a number to choose option:\n1. Split text.\n2. Upper case to lower case.\n" "3. Lower case to upper case.\n4. Remove character.\n5. Add a character.\n" "6. Replace a character.\n7. Statistics.\n8. Exit.\nEnter an option: "); scanf("%d", &choice); getchar(); switch (choice) { case 1: // Split text printf("Enter a string up to 100 characters: "); fgets(text, 100, stdin); break; case 2: // From upper to lower printf("Enter a string up to 100 characters: "); fgets(text, 100, stdin); tolower_str(text1, text); printf("%s", text1); break; case 3: // From lower to upper printf("Enter a string up to 100 characters: "); fgets(text, 100, stdin); toupper_str(text1, text); printf("%s", text1); break; case 4: // Remove character break; case 5: // Add character break; case 6: // Replace a character break; case 7: // Statistics printf("Enter a string up to 100 characters: "); fgets(text, 100, stdin); count_word(text); printf("Characters: %d\n", strlen(text)-1); count_space(text); break; case 8: printf("Exiting application."); exit(0); break; default: printf("Wrong number!\n"); break; } return main(); }

Permalänk
Medlem

Har dålig koll på C++ samt har inte läst igenom koden helt så blir en liten gissning här. Men borde du inte ha printf("%c", text[1]) (alltså %c istället för %s) eftersom varje element i text[] är char och inte string?

Permalänk
Medlem
Skrivet av juncor:

Det blir inget fel när jag debuggar, det är bara att den printar en tom rad när jag använder [printf("%s", text[1]);.

Tips: text[1] är inte en sträng utan ett tecken. printf använder olika formateringskoder beroende på om motsvarande argument är en sträng eller ett tecken.

Dessutom ... borde du inte allokera minne för innehållet i text1 någonstans?

Permalänk
Medlem

Ja, du måste använda %c om du bara vill skriva ut en karaktär. %s förväntar sig en pekare till en sträng, så t.ex. 'e' kommer tolkas som adressen 0x65 vilket resulterar i att programmet kan skriva ut vad som helst eller t.o.m. krascha.

Din kod är för övrigt inte C++-kod, utan C.

Permalänk
Medlem
Skrivet av toobypls:

Har dålig koll på C++ samt har inte läst igenom koden helt så blir en liten gissning här. Men borde du inte ha printf("%c", text[1]) (alltså %c istället för %s) eftersom varje element i text[] är char och inte string?

Skrivet av perost:

Ja, du måste använda %c om du bara vill skriva ut en karaktär. %s förväntar sig en pekare till en sträng, så t.ex. 'e' kommer tolkas som adressen 0x65 vilket resulterar i att programmet kan skriva ut vad som helst eller t.o.m. krascha.

Din kod är för övrigt inte C++-kod, utan C.

Ah, där löste det sig direkt. Dum jag känner mig nu.. testat det mesta förutom det..
Använder mig av boken "C från början", läst igenom kapitlet X antal gånger, hittar inget alls om %c där, utan endast %s. Stort tack för hjälpen, ska nu se om jag kan lyckas klura ut resten av uppgifterna!!

PS: Råkade skriva till ++ i farten, menar självklart C! Haha, blev i frustration jag skrev.. Tack för påpekandet dock ;D

Permalänk
Medlem
Skrivet av Erik_T:

Tips: text[1] är inte en sträng utan ett tecken. printf använder olika formateringskoder beroende på om motsvarande argument är en sträng eller ett tecken.

Dessutom ... borde du inte allokera minne för innehållet i text1 någonstans?

Skulle egentligen vara char text1[100], som text.. Där var ett av mina desperata försök från när jag försökte använda strncpy och köra försöka skriva ut en bokstav... Somsagt, försökt med det mesta förutom att tydligen tänka att det inte var en sträng, dumt nog.. Setat och stirrat in i koden i två dagar och inte kommit någonvart... Så har helt glömt att ändra tillbaka och allokerat minnet för text1 samt att radera minnet när man kört caset (eftersom den ska loopa).
Eller om jag kanske är dum och misstolkat hur man allokerar minne från boken och det inte är så man alls gör som jag beskrev, haha...

Stort tack för alla svar!!

Permalänk
Medlem
Skrivet av juncor:

PS: Råkade skriva till ++ i farten, menar självklart C! Haha, blev i frustration jag skrev.. Tack för påpekandet dock ;D

Bra, då håller du i alla fall på att lära dig rätt språk Koden är ju giltig C++ också eftersom C++ för det mesta är bakåtkompatibelt, men är syftet att lära sig C++ så har man hamnat lite snett om man lär sig att koda på det sättet.

Skrivet av juncor:

Eller om jag kanske är dum och misstolkat hur man allokerar minne från boken och det inte är så man alls gör som jag beskrev, haha...

char text[100] allokerar en char-array med 100 element på stacken. Varje program har en viss mängd stack-minne (typiskt 1MB i Windows och 8MB i Linux) där minne allokeras på "sist in, först ut"-sätt. När du t.ex. anropar en funktion så allokeras funktionens lokala variabler på stacken, och när funktionen returnerar så avallokeras variablerna automatiskt. Det andra sättet att allokeras minne på är att allokera på heapen med t.ex. malloc(), då kan man använda i stort sett så mycket minne som datorn har men måste frigöra minnet själv. Men så långt har du troligtvis inte kommit än.

Ett problem med ditt program är att du i slutet av main anropar main rekursivt med return main();. Detta är tillåtet i C (men inte i C++), men det innebär att dina variabler i main aldrig frigörs förrän programmet avslutas. Varje gång programmet loopar runt så allokeras ytterligare 200 chars + 1 int på stacken, vilket troligtvis är 204 byte på ditt system. Om du har 1MB stack så kommer alltså ditt program få slut på stack och troligtvis krascha när programmet loopat runt ~5000 gånger. Inte världens största problem i det här fallet kanske, men något man måste tänka på när man kodar. Lösningen är att använda t.ex. en while-loop för att loopa istället för rekursion.

Permalänk
Medlem
Skrivet av perost:

Bra, då håller du i alla fall på att lära dig rätt språk Koden är ju giltig C++ också eftersom C++ för det mesta är bakåtkompatibelt, men är syftet att lära sig C++ så har man hamnat lite snett om man lär sig att koda på det sättet.
char text[100] allokerar en char-array med 100 element på stacken. Varje program har en viss mängd stack-minne (typiskt 1MB i Windows och 8MB i Linux) där minne allokeras på "sist in, först ut"-sätt. När du t.ex. anropar en funktion så allokeras funktionens lokala variabler på stacken, och när funktionen returnerar så avallokeras variablerna automatiskt. Det andra sättet att allokeras minne på är att allokera på heapen med t.ex. malloc(), då kan man använda i stort sett så mycket minne som datorn har men måste frigöra minnet själv. Men så långt har du troligtvis inte kommit än.

Ett problem med ditt program är att du i slutet av main anropar main rekursivt med return main();. Detta är tillåtet i C (men inte i C++), men det innebär att dina variabler i main aldrig frigörs förrän programmet avslutas. Varje gång programmet loopar runt så allokeras ytterligare 200 chars + 1 int på stacken, vilket troligtvis är 204 byte på ditt system. Om du har 1MB stack så kommer alltså ditt program få slut på stack och troligtvis krascha när programmet loopat runt ~5000 gånger. Inte världens största problem i det här fallet kanske, men något man måste tänka på när man kodar. Lösningen är att använda t.ex. en while-loop för att loopa istället för rekursion.

Ah okej! Nej så långt har vi inte kommit ännu, men jag tror det börjar närma sig där dock, så kan vara bra att förstå sig på det lite redan nu.
Så det är egentligen bara att sätta en while(1) { //Koden } under main() tills slutet, (jag antar att man sätter (1) pga av att 1 alltid är true)?? Väldigt bra information, speciellt när man vet bakgrunden till varför man gör så! "C från början" är inte direkt helt 100% givande när man är nybörjare, haha..

Permalänk
Medlem
Skrivet av juncor:

Så det är egentligen bara att sätta en while(1) { //Koden } under main() tills slutet, (jag antar att man sätter (1) pga av att 1 alltid är true)??

Ja, precis. Variabler (eller snarare minnet de representerar) avallokeras när de "faller ur scope", d.v.s.:

{ // <- start på nytt block int i = 4; // i allokeras på stacken. printf("%d\n", i); } // <- slut på block, alla variabler som deklarerats i blocket avallokeras.

Tekniskt sett kan du bara omsluta all kod i main förutom den sista return-raden i ett block, så att variablerna du deklarerat avallokeras innan du anropar main igen. Men att använda en while-loop är det typiska sättet att skriva sån här kod på, det gör det tydligare vad koden faktiskt gör.

Permalänk
Medlem

Skriver man i c++ slipper man krångla med printf. Man manipulerar ett objekt i utströmmen. Exempel:

#include <iostream>
using namespace std;
...

cout <<"Startvärde: "<<startvarde
<<" Ränta: "<<ranta<<"%"
<<" Tid: "<<tid<<" år"
<<" Nuvärde: "<<nuvarde<<endl;

Om "ränta" är en textsträng eller ett tal behöver man inte hålla reda på när man gör utskriften.