Dynamiczna tablica lancuchow znakow i zwalnianie pamieci

0

Witam,
Probuje napisac program w C99 i potrzebuje ogarnac tablice lancuchow znakow (czyli tablice wskaznikow). Program wywala sie na free w peli for. Na chwile obecna nie rozumiem jaki blad popelniam zwalniajac pamiec. Sposob rozumowania przedstawilem w ostatnich komentarzach, bede wdzieczny za podpowiedz.

Pozdrawiam,

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main ()
{
  char str[] ="test kolejny nastepny token token token";
  char* tmp; // do tej zmiennej kopiuje lancuch str w celu przeliczenia
  char* pch; // w celu operacji rozbijania na tokeny
  tmp = malloc(sizeof(strlen(str)+1));
  strcpy(tmp, str);
  
  //
  // poczatek liczenia tokenow
  //

  // informacja potrzebna do tego ile elementow tablicy
  // dwuwymiarowej nalezy zwolnic
  int ilosc_tokenow = 0;
  pch = strtok(tmp," "); // spacja i tabulator
  ilosc_tokenow++;

  while (tmp != NULL)
  {
    tmp = strtok (NULL, " ");
    ilosc_tokenow++;
  }

  //
  // koniec liczenia tokenow
  //
  printf("Ilosc tokenow: %d\n", ilosc_tokenow);

  //
  // zapis tokenow do dynamicznej tablicy
  //

  // w tej tablicy argumentow trzymam swoje tokeny
  // terminatorem, ostatnim elementem jest NULL
  char** tablica_argumentow;

  // dopiero teraz wiemy ile jest elementow i mozna zaczac wlasciwie alokowac pamiec
  tablica_argumentow = malloc(ilosc_tokenow*sizeof(char)+1); 
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," "); // spacja i tabulator
  printf("%s", str);

  // alokuje pamiec
  *tablica_argumentow = malloc(sizeof(char)*(strlen(pch)+1));
  // kopiuje token do zaalokowanej pamieci
  strcpy(*tablica_argumentow, pch);
  *tablica_argumentow++; // przejdz do nastepnego elementu w tablicy lancuchow

  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ");
    // alokuje pamiec
    if(pch != NULL) {
    	 *tablica_argumentow = malloc(sizeof(char)*strlen(pch)+1);
	 // kopiuje token do zaalokowanej pamieci
   	 strcpy(*tablica_argumentow, pch);
   	 printf("Wczytany element: %s\n", *tablica_argumentow);
    }
    // przechodzi do nastepnego elementu w tablicy tokenow
    *tablica_argumentow++;
  }

  // ostatni element
  // dopisujemy NULL
  *tablica_argumentow = NULL;
  printf("NULL dopisany.\n");

  //
  // koniec zapisu tokenow do dynamicznej tablicy
  //


  // zwolnij wczesniej zaalokowana pamiec
  for(int i = 0, *tablica_argumentow; i < ilosc_tokenow; i++, *tablica_argumentow--) {
	  // dla kazdego zaalokowanego tokena wykonaj free
	  free(*tablica_argumentow);
  }
  // na koniec zwolnij glowny wskaznik
//  free(tablica_argumentow);

  return 0;
}
0

Zwykle piszę w C++ więc tylko powiem tyle:

int i = 0, *tablica_argumentow;

To oznacza, że tworzysz zmienną int z początkową wartością 0 i tworzysz lokalny dla pętli for wskaźnik tablica_argumentow

0

Dzieki, to juz poprawilem. Zastanawia mnie teraz czemu to mi nie dziala:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main ()
{
  char str[] ="test kolejny kolejny kolejny kolejny kolejny";
  char* tmp; // do tej zmiennej kopiuje lancuch str w celu przeliczenia
  char* pch; // w celu operacji rozbijania na tokeny
  tmp = malloc(sizeof(strlen(str)+1));
  strcpy(tmp, str);
  
  //
  // poczatek liczenia tokenow
  //

  // informacja potrzebna do tego ile elementow tablicy
  // dwuwymiarowej nalezy zwolnic
  int ilosc_tokenow = 0;
  pch = strtok(tmp," "); // spacja i tabulator
  ilosc_tokenow++;

  while (tmp != NULL)
  {
    tmp = strtok (NULL, " ");
    ilosc_tokenow++;
  }

  //
  // koniec liczenia tokenow
  //
  printf("Ilosc tokenow: %d\n", ilosc_tokenow);

  //
  // zapis tokenow do dynamicznej tablicy
  //

  // w tej tablicy argumentow trzymam swoje tokeny
  // terminatorem, ostatnim elementem jest NULL
  char** tablica_argumentow;

  // dopiero teraz wiemy ile jest elementow i mozna zaczac wlasciwie alokowac pamiec
  tablica_argumentow = malloc(ilosc_tokenow*sizeof(char)+1); 
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," "); // spacja i tabulator
  printf("%s", str);

  // alokuje pamiec
  *tablica_argumentow = malloc(sizeof(char)*(strlen(pch)+1));
  // kopiuje token do zaalokowanej pamieci
  strcpy(*tablica_argumentow, pch);
  *tablica_argumentow++; // przejdz do nastepnego elementu w tablicy lancuchow

  while (pch != NULL)
  {
    //printf ("%s\n",pch);
    pch = strtok (NULL, " ");
    // alokuje pamiec
    if(pch != NULL) {
    	 *tablica_argumentow = malloc(sizeof(char)*strlen(pch)+1);
	 // kopiuje token do zaalokowanej pamieci
   	 strcpy(*tablica_argumentow, pch);
   	 printf("Wczytany element: %s\n", *tablica_argumentow);
    }
    // przechodzi do nastepnego elementu w tablicy tokenow
    *tablica_argumentow++;
  }

  // ostatni element
  // dopisujemy NULL
  *tablica_argumentow = NULL;
  printf("NULL dopisany.\n");

  //
  // koniec zapisu tokenow do dynamicznej tablicy
  //
  //
 
  // przesun wskaznik na poczatek
  for(int i = ilosc_tokenow; ilosc_tokenow > 0; ilosc_tokenow--) {
	  printf("%s ", *tablica_argumentow);
	  *tablica_argumentow--;
  }

  // wyswietl wszystkie tokeny w dynamicznej tablicy
  while(*tablica_argumentow != NULL) {
	  //printf("%s ", *tablica_argumentow);
	  tablica_argumentow++;
  }

  printf("Ilosc tokenow: %d\n", ilosc_tokenow);

  // zwolnij wczesniej zaalokowana pamiec
  for(int i = 0; i < ilosc_tokenow; i++, *tablica_argumentow--) {
	  // wyswietlenie tokena przed zwolnieniem z niego pamieci
//	  printf("Token: %s\n", *tablica_argumentow);
	  // dla kazdego zaalokowanego tokena wykonaj free
//	  free(*tablica_argumentow);
  }
  // na koniec zwolnij glowny wskaznik
//  free(tablica_argumentow);

  return 0;
}

Mowie konkretnie o fragmencie // przesun wskaznik na poczatek.
Wydaje mi sie, ze wykonujac *tablica_argumentow--; na elemencie bedacym tablica wskaznikow zmniejszam o 1 adres komorki pamieci, czyli wskazuje na poprzedni element zapisany w tablicy. Program wyrzuca SIGSEGV na tej linii:
81 printf("%s ", *tablica_argumentow);
Nie rozumiem. :-/

0

<url></url>

0

1 blad polega na przechodzeniu 2 razy petli ze strtok, raz zeby policzyc drugi raz zeby skopiowac.
2 blad to rezerwacja pamieci dla tmp a potem jeszcze rezerwacja pamieci dla tablica_tokenow, tmp juz jest buforam w ktorym sa ladnie pociete przez strtok kawalki tekstu.
3 malloc+strcpy=>strdup
4 skoro i tak korzystasz z ilosc_tokenow, to dodatkowa komorka w tablicy_tokenow wypelniona terminatorem (NULL) jest marnowaniem pamieci.. to tak jakby nosic do spodni jednoczesnie pasek i szelki.

mozna jeszcze zadbac, zeby pojedyncze strtok ladnie umiescic w warunku petli, ale to juz moja fanaberia.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main (){
    char str[] ="ala ma kota i psa";

    int i;

    int ilosc_tokenow = 0;
    int zarezerwowane_tokenow = 0;
    char **tablica_tokenow = NULL; // dla realloc, realloc dla NULL dziala jak malloc

    char *tokentmp = strdup(str); // malloc + strcpy;
    char *s=tokentmp;
    char *token;
    while(( token = strtok(s," \t") )){  // strtok nie jest thread friendly, lepiej uzyc strtok_r - ma zewnetrzny bufor.
      // podwojny nawias chroni przed rzuceniem warningiem "parenthesis ..." - inaczej: wiem co robie, ma byc przypisanie a nie porownanie
        s=NULL; // przy drugim przejsciu strtok dostanie NULL
        if(ilosc_tokenow>=zarezerwowane_tokenow){ // skonczyla sie zarezerwowana przestrzen w tablicy; wystarczy == ale >= daje wieksza pewnosc
            if(zarezerwowane_tokenow){ // !=0
                zarezerwowane_tokenow*=2;  // 2,4,8,16,32,... kolejno tyle alokujesz;
            }else zarezerwowane_tokenow=1; // 1
            tablica_tokenow=realloc(tablica_tokenow,sizeof(char*)*zarezerwowane_tokenow);
        }
        tablica_tokenow[ilosc_tokenow++]=token;
    }

    // zwalniamy nadmiar
    // caly pomysl na przydzielaniu pamieci 2x tyle ile poprzednio polega m.in. na tym,
    // ze na koniec masz maksymalnie zarezerwowane wiecej: 1/2 pamieci - 1 element
    // czyli zawsze masz wypelnienie przynajmnmniej w 50%
    // i nie trzeba wczesniej liczyc ile elementow bedzie.
    if(ilosc_tokenow<zarezerwowane_tokenow)tablica_tokenow=realloc(tablica_tokenow,sizeof(char*)*ilosc_tokenow);

    printf("Tokenów: %d\n", ilosc_tokenow);
    i = ilosc_tokenow;
    while (i--)printf("Token %d: %s\n", i, tablica_tokenow[i]));

    free(tablica_tokenow);
    free(tokentmp);

    return 0;
}
0

Dzieki za wszystko. Przepraszam, ze sie nie odzywalem ale wypadlo mi w ostatnim czasie kilka pilnych spraw i dopiero teraz moge wrocic do C99.

1 użytkowników online, w tym zalogowanych: 0, gości: 1