Stränghantering i C

Tråden skapades och har fått 19 svar. Det senaste inlägget skrevs .
1
  • Medlem
  • Örebro
  • 2006-12-23 13:17

Hallå!

Jag är lite ny på det här med programmering. Har en del kunskaper i php och javascript men vill nu gå lite längre.

En bra början, har jag hört, är att lära sig några grunder i C innan man börjar leka i xCode och Cocoa. (Jag har gått igenom några tutorials men insåg att jag måste ta det här från början)

Det jag har slitit mitt hår för nu i en hel dag är hur tusan man använder strängar i C. Heltal och floats går fint och även att spara en bokstav i en char, men när jag ska spara textsnuttar går det åt skogen direkt.

Jag har fått följande att fungera:

char text1[20] = "blabla";

printf("%s", text1);

So far so good, men när jag ska använda den metodin i typedefs blir det knöligare.
Enligt min hjärna borde det vara såhär men den har ju aldrig rätt:

#include <stdio.h>

typedef struct {
	int id;
	char name[10];
	int age;
} Person;

Person newPerson;

main() {

newPerson.id = 1;
newPerson.name = "Nisse";
newPerson.age = 20;

}

Någon som kan ge något tips?

Jo en till sak. Vad är det för skillnad på att innan main() deklarera en variabel enligt int id; och sen i mainI() skriva id = 2; och att direkt i main() skriv int id = 2;

Tack på förhand!

  • Medlem
  • Stockholm
  • 2006-12-23 14:07

Du behöver plugga mer kring pekare, och hur arrayer ibland kan tolkas som pekare.

Du skriver

char text1[20] = "blabla";

Detta skapar en array med 20 element, och fyller de 7 första med blabla och en nullterminator.

Man kan också skriva:

char text1[] = "blabla";
char *text2 ="blabla";

text1 skapar en array med så många tecken som behövs för att fylla med det du initierar till.

text2 är en pekare till en sträng, och initieras till att peka på strängen "blabla".

Tänk på att då du skriver "blabla" i C så skapar kompilatorn en skrivskyddad array någonstans med 7 bytes. Typen för "blabla" är const char*.

I din typedef har du egentligen två val för strängar. Du kan antingen göra som du gjort, dvs att har en buffer med plats för 20 tecken. Det är ju visserligen enkelt, men har problemet att antingen är bufferten för stor, och då slösar du utrymme, eller så är den för liten och då kan du inte lagra din sträng alls. Därför använder man ofta pekare istället.

typedef struct {
	int id;
	char *name;
	int age;
} Person;

Nackdelen är att du då måste börja allokera minne för strängarna.

newPerson.name = "Nisse";

Detta fungerar i min typedef, men inte i din eftersom newPerson.name inte är en typ kompatibel med (char*). För att kopiera strängar använder du strcpy (gör man strcpy i terminalen). Gör #include <string.h> i ditt program om du använder strcpy.

  • Medlem
  • Örebro
  • 2006-12-23 14:57

Kanon! Jag tänkte väl att det måste finnas bättre sätt än att stoppa in bokstäver i arrayer.

Ursprungligen av pesc:

Nackdelen är att du då måste börja allokera minne för strängarna.

Är det något jag behöver göra eller är det något som programmet sköter själv? Varför är det en nackdel?

Tack på förhand!

  • Medlem
  • Simrishamn
  • 2006-12-23 14:10

Jag är inte otroligt insatt i hur det funkar när det väl har kompilerat... men, jag tror att det är såhär, att när du deklarerar en variabel i en funktion, så allokeras minne för variabeln på stacken. Alltså, för varje variabel som deklareras så ökas stackpekaren med lika många steg som variabelns datatyp, och sedan accessas variablerna med stackpekaren och ett byte-delta för variabelns position. Typ såhär:

char a, b, c;
b = 1;
a = 3;
c = 2;

Blir: (asm-pseudokod)

push
push
push
load (stackpekare - 1), 1
load (stackpekare - 2), 3
load (stackpekare), 2
pop
pop
pop

(förutsatt att char är 1 byte på plattformen).

När du däremot deklarerar en variabel utanför en funktion, så tror jag att den allokeras på ett annat ställe i minnet, som inte förändras under programmets gång.

(EDIT2: Förresten, ska inte din main-metod returnera någonting?

(EDIT3: nu när jag läser din ursprungsfråga så ser jag att jag inte alls svarar på det du frågade... slarvigt läst, förlåt. Ignorera alltså detta meddelande
Men någon annan kan kanske svara på om jag har förstått det hela rätt?)

  • Medlem
  • Stockholm
  • 2006-12-23 14:23
Ursprungligen av HannesP:

Förresten, ska inte din main-metod returnera någonting?

Enligt ANSI C -99 så skall main deklareras såhär:

Citat:

The function called at program startup is named main. The implementation declares no
prototype for this function. It shall be defined with a return type of int and with no
parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be
used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.

Man måste inte göra return i main:

Citat:

If the return type of the main function is a type compatible with int, a return from the
initial call to the main function is equivalent to calling the exit function with the value
returned by the main function as its argument; reaching the } that terminates the
main function returns a value of 0.

  • Medlem
  • Stockholm
  • 2006-12-23 14:13
Ursprungligen av oskob:

Vad är det för skillnad på att innan main() deklarera en variabel enligt int id; och sen i mainI() skriva id = 2; och att direkt i main() skriv int id = 2;

Variabler deklarerade utanför en funktion (t.ex. main()) är globala variabler. De är synliga för alla funktioner du har länkat in i ditt program. (Du kan även deklarera en global variabel som static int id; och då är den enbart synlig för alla funktioner i din fil.)

Globala variabler anses vara ondskefulla för att de är trådosäkra, fungerar dåligt i rekursion, tar plats även då de inte används, ger upphov till sidoeffekter, och allmänt försvårar läsbarheten.

En variabel deklarerad i en funktion lagras på stacken då funktionen blir anropad (och försvinner då funktionen returnerar). De är att föredra eftersom de löser de problem som jag listade med globala variabler.

  • Medlem
  • Simrishamn
  • 2006-12-23 15:21

Nackdelen är att du får mer minne att hålla koll på
Jag kan inte använda C's standardfunktioner, men jag förmodar att du gör ungefär såhär:
char *myString = (char *)malloc( 10 * sizeof( char ) );
Om du vill ha plats för 9 bokstäver i den. (den sista byten behövs för att terminera stringen, och måste ha värdet 0).

  • Medlem
  • Örebro
  • 2006-12-23 17:13
Ursprungligen av HannesP:

char *myString = (char *)malloc( 10 * sizeof( char ) );

Jag förstår inte riktigt vad det där ska föreställa.

Jag har inte planerat att bli någon mästare i C. Jag vill bara ha lite kött på benen. Nu tycker jag att jag har kommit en bit i alla fall. Tillräckligt långt för att kunna lösa det mesta med hjälp av google.

Det är några saker jag vill kunna lite bättre innan jag går vidare dock.

Dels är det hantering av inmatning. scanf() och sånt. Och hur fasen man räknas posterna i en array. Har hittat lite lösningar med sizeof() men jag får det inte att fungera.

int test = sizeof(newPerson);

printf("%i", test);

Den där returnerar bara 0. newPerson är en array med typedefs om man nu kan säga så. Såhär:

typedef struct {
	char *name;
	int age;
	char *title;
} Person;

Person newPerson[0];

void create_person(char *name, int age, char *title) {
	
	newPerson[count].name = name;
	newPerson[count].age = age;
	newPerson[count].title = title;
	
	count = count + 1;	
}

Det är bara ett urdrag för jag kan inte förklara det bättre Hoppas ni förstår.

  • Medlem
  • Mölndal
  • 2006-12-23 15:37

Om du redan kan php och framför allt tycker jag inte det är speciellt nödvändigt att traggla gammaldags C först. Jag skulle istället gå direkt på objective-C som liksom javascript och i viss mån php är objektorienterat. Att kunna Cs syntax är förstås aldrig fel, däremot tycker jag inte du ska lägga speciellt mycket tid på t ex C-funktioner du ändå aldrig kommer använda (t ex printf, malloc) när du kodar i Cocoa.

Stränghanteringen anses (av mig och många därtill) vara ebarmligt dålig i C och du kanske börjar inse varför.

  • Medlem
  • Göteborg
  • 2006-12-23 16:11
Ursprungligen av memark:

Om du redan kan php och framför allt tycker jag inte det är speciellt nödvändigt att traggla gammaldags C först. Jag skulle istället gå direkt på objective-C som liksom javascript och i viss mån php är objektorienterat. Att kunna Cs syntax är förstås aldrig fel, däremot tycker jag inte du ska lägga speciellt mycket tid på t ex C-funktioner du ändå aldrig kommer använda (t ex printf, malloc) när du kodar i Cocoa.

Stränghanteringen anses (av mig och många därtill) vara ebarmligt dålig i C och du kanske börjar inse varför.

Jag håller delvis med. Jag tycker du skall lära dig C bra, helst med en riktig bok. Sedan kan du gå över till att använda Apples NSString objekt.

  • Medlem
  • Simrishamn
  • 2006-12-23 16:27

Lär dig C, men fäst ingen större vikt vid C's standardbibliotek. Att kunna använda pekare, typedefs osv är dock nödvändigt när du skriver ObjC.

  • Medlem
  • Stockholm
  • 2006-12-23 16:32
Ursprungligen av HannesP:

Lär dig C, men fäst ingen större vikt vid C's standardbibliotek.

Jo, jag tycker nog att kunskap om strcpy, strcat, malloc, memcpy, memset, memmove, etc hör till baskunskaperna om man ska kunna C.

  • Medlem
  • Stockholm
  • 2006-12-23 16:28

Poängen med C är att INGENTING (nästan) är automatiskt. Du kan (och måste ha) full koll på allt du gör. Du bör behärska pekare ordentligt om du ska jobba i C.

C kan betraktas som högnivåassembler. Om man behärskar C är stränghanteringen enkel, manuell och logisk.

Men jag håller med om att språket inte är idealiskt för slutapplikationer. På Mac är Objective-C ett intressant val. Annars skulle jag nog rekommendera ett "säkert" språk, typ Java. Eller kanske något "script-aktigt", typ Phyton eller Ruby.

Men för att förstå grunderna i hur datorer arbetar så är C ett bra val.

  • Medlem
  • 2006-12-23 17:46

arrayen newPerson har du ju satt till 0 element, så klart att size blir 0.

De olika formerna av alloc (minnesallokering) är grundläggande i C, så fort du har dynamiska datastrukturer så måste du själv tilldela minnesutrymme. Och, vilket närmast är ännu värre, frigöra minne när det inte används!

(char *)malloc( 10 * sizeof( char ) );

sizeof( char ) returnerar antalet bytes typen char använder
10 * sizeof( char ) ger alltså plats för 10 st variabler av typen char
malloc(...) reserverar minnesplats för dessa
(char *)... typecastar referensen till minnet till att bli en char-pekare

  • Medlem
  • Stockholm
  • 2006-12-23 18:45
Ursprungligen av SirN:

(char *)... typecastar referensen till minnet till att bli en char-pekare

...vilket är onödigt i C (men inte C++) eftersom malloc returnerar en void* och det är tillåtet att tilldela en void* till en char*

  • Medlem
  • Örebro
  • 2006-12-23 17:55

SirN:
Men jag har ju en funktion som dynamiskt lägger saker i newPersons. newPerson = [0] har jag bara för att göra den global. Man kanske inte ska göra så.

Vad händer om man inte frigör minne osv?

  • Medlem
  • Stockholm
  • 2006-12-23 18:53
Ursprungligen av oskob:

SirN:
Men jag har ju en funktion som dynamiskt lägger saker i newPersons. newPerson = [0] har jag bara för att göra den global. Man kanske inte ska göra så.

Vad händer om man inte frigör minne osv?

Nej, C har inte dynamiska arrayer alls. Du måste alltid själv hantera minne manuellt.

Du kan deklarera newPerson[10], t.ex. så skaffar du utrymme för exakt 10 personer. Men vill du ha dynamiskt antal personer bör du göra på ett helt annat sätt, och då blir det mer komplicerat och avancerat.

Du kan deklarera:
Person *newPerson;
..och sedan skaffa minne till t.ex. en person:

newPerson = malloc(1 * sizeof(Person));

...och sedan utöka minnet till två personer...

newPerson = realloc(newPerson, 2 * sizeof(Person));

Problemet med realloc är att den kan bli tvungen att kopiera hela arrayen, och då blir inte prestandan optimal. I så fall kan du välja någon annan datastruktur...

Om du inte kommer ihåg att friställa minne du allokerat så gör ingen annan det åt dig. Du läcker minne. Ditt program kommer att dra mer och mer minne för att till slut krasha.

  • Medlem
  • 2006-12-23 20:13
Ursprungligen av oskob:

Men jag har ju en funktion som dynamiskt lägger saker i newPersons. newPerson = [0] har jag bara för att göra den global. Man kanske inte ska göra så.

Arrayer i exempelvis PHP är inte "riktiga" arrayer. Arrayen är en grundläggande datastruktur i minnet, vilket innebär att du lägger ett antal variabler av samma typ efter varandra, rent fysiskt. Du kan inte bara lägga till nya element, av den enkla anledningen att utrymmet efter kanske används av en annan variabel.

Arrayer är i princip bara vettiga att använda när du på förhand vet hur många platser du behöver. För dynamiska datasamlingar brukar andra konstruktioner användas, vill du ha en vettig snabbkurs i C tycker jag du ska skapa metoder för att implementera en länkad lista. Detta är en av de vanligaste datasamlingarna, och innehåller trevliga övningar i pekare, malloc, och annat mysigt!

  • Medlem
  • Simrishamn
  • 2006-12-23 18:57

...och det är här

Person *newPerson = [Person personWithName:@oskob];
NSMutableArray *array = [NSMutableArray array];
[array addObject:newPerson];

kommer in i bilden

  • Medlem
  • Örebro
  • 2006-12-24 00:38

Okej jag får tänka om arrayer med andra ord. Är ju van vid att använda de just för att de är så dynamiska och fina.

Tack för alla svar! Ska simma vidare.

1
Bevaka tråden