lapsus alumni

czerwiec 3, 2007

Znajdź błąd #1: Dziedziczenie

Zaszufladkowany do: Programowanie — elmopl @ 7:03 pm

Tak w ramach przygotowań do kolokwium wymyślałem sobie problemy i wyszło mi to co poniżej prezentuję.
Poniższy kod powoduje (a przynajmniej powinien) naruszenie ochrony pamięci:

class A {
 protected:
   char *t;
 public:
   A(int i = 1) : t(new char[i]) {};
   ~A(){ delete [] t; };
};

class B : virtual public A {
 private:
   int m_ElementsCount;
 public:
   B(int elementsCount) : A(elementsCount), m_ElementsCount(elementsCount) {};

   int writeElement(unsigned int i, unsigned int k){
      if(i < m_ElementsCount){
         t[i] = k;
         return 0;
      }
      else return 1;
   };
};

class C : public B{
 public:
   C(int elementsCount) : B(elementsCount) {};
};

int main(int argc, char **argv){
   C myC(100);

   myC.writeElement(99, 2);

   return 0;
}

Żeby go poprawić wystarczy usunąć jeden wyraz (blok funkcji main zostawiamy w spokoju).
Odpowiedź po kliknięciu “więcej”.
(więcej…)

luty 4, 2007

wxWidgets 2.8.0

Zaszufladkowany do: Programowanie, Sieć — elmopl @ 4:00 pm

Kolejne stabilne wydanie (moim zdaniem) bardzo wygodnej biblioteki do pisania przenośnych aplikacji okienkowych.
Link

styczeń 21, 2007

Enter Valhalla

Zaszufladkowany do: C, Programowanie — elmopl @ 8:49 pm

Jedną z ważniejszych umiejętności przy pisaniu programów jest sprawdzanie czy aby program działa tak jak powinien. Bywają w końcu błędy, które nie powodują wyłożenia się programu od razu tylko musi pochodzić dłuższy czas, a nawet i wtedy wcale nie musi być jednoznaczne, że zatkanie się systemu jest efektem działania naszego programu, a nie jakiegoś innego.
W końcu przy bardziej rozbudowanych programach odkryć wyciek pamięci wcale nie jest łatwo. Szczególnie jak mamy małe strukturki po kilka bajtów przez co nie zwracają na siebie uwagi. Zapomnieć o zwolnieniu takiego ustrojstwa jest dość łatwo. Czasem też się zdarza skorzystać ze zmiennej automatycznej bez jej inicjalizacji co może prowadzić do bardzo nieoczekiwanych rezultatów. Na szczęście mądrzy ludzie wymyślili program, który pomaga w radzeniu sobie w dużym stopniu z wieloma błędami. A nazwali go Valgrind
(więcej…)

styczeń 13, 2007

Wskaźnik na wskaźnik. A po co?

Zaszufladkowany do: C, Programowanie — elmopl @ 5:40 pm

#include
#include 

void func1(int *b){
   b = malloc(sizeof(int));
   *b = 1;

   return;
}

void func2(int **c){
   *c = malloc(sizeof(int));
   **c = 2;

   return;
}

int main(int argc, char **argv){
   int *a = NULL;

   a = (int*) malloc(sizeof(int));
   *a = 0;

   printf("%d\n", *a);

   func1(a);
   printf("%d\n", *a);

   func2(&a);
   printf("%d\n", *a);

   free(a);

   return 0;
}

Odpowiedzi dostarczy analiza tego krótkiego programiku.
Przy okazji ten program zawiera błąd w postaci wycieku pamięci. W drugiej funkcji tracimy wskaźnik na zaalokowaną pamięć nie zwalniając jej.

styczeń 12, 2007

Self-DoS

Zaszufladkowany do: Programowanie, Sieć — elmopl @ 1:06 pm

W jaki zręczny sposób zablokować własną stronę? Wystarczy robić drobne poprawki w ramach odpoczynku od nauki. Mniej więcej w taki sposób wykonałem atak DoS na własną stronę.
Na nowym silniku strony Interfaksu zrobiłem system automatycznego odświeżania przy użyciu JavaScript’u. Aktualnie częstotliwość odświeżania to minuta (co którą skrypt w tle sobie ściąga co trzeba zaktualizować i odpowiednie elementy strony modyfikuje). Jednak przy wszelkich przeróbkach na witrynie testowej nie widocznej dla całego świata zewnętrznego ustawiłem sobie ten interwał na 3 sekundy co bym nie musiał czekać minuty po każdej zmianie linijki kodu. W ten sposób po jakiejś drobnej poprawce w ramach mojego wrodzonego roztargnienia na główną stronę poszedł skrypt z tym nadwyraz krótkim (jak się okazało) czasem odświeżania.
Tutaj się zaczęła ta “zabawna” część. Strona co prawda odpowiadała na żądania, ale bardzo wolno. Edycja samego pliku ze skryptem sprawy nie załatwia, bo dopóki człowiek przed przeglądarką nie zmieni/odświeży ręcznie strony to nie ma szans na ściągnięcie zaktualizowanego skryptu i dalej będzie ściągał praktycznie non-stop. Ba, żeby było ciekawiej przeglądarki sobie cache’ują te pliki i jakoś mało je obchodzi zmiana czasu ostatniej modyfikacji czy nawet rozmiaru pliku (jak narazie najbardziej oporna na zmiany była Opera). To jest trochę sytuacja bez wyjścia, bo nie mam jak zatrzymać wykonywania się skryptu w czyjejś przeglądarce.
Skończyło się więc na zablokowaniu funkcji odświeżania po stronie serwera do końca dnia, edycji i zmianie nazwy pliku (to ostatnie już wymusiło na wszystkich przeglądarkach ściągnięcie nowej wersji pliku).

styczeń 3, 2007

Zmienne globalne w C

Zaszufladkowany do: C, Programowanie — elmopl @ 10:48 pm

Tak jak wcześniej napisałem opiszę swoje doświadczenia z korzystaniem ze zmiennych globalnie dostępnych.
Jeżeli planujemy pisać dłuższe programy dobrze jest wyrobić sobie odpowiednie nawyki. Jednym z nich jest rozważne używanie zmiennych globalnych. To co mnie dziwi w przykładach dawanych na wykładach jest wykorzystywanie do wszystkiego zmiennych globalnych.
Weźmy na przykład jeden z projektów na ćwiczenia z programowania. Napisać stos przechowujący elementy dowolnego typu.
Według podejścia “wykładowego” w programie takim pojawiłaby się zmienna globalna np.

struct {
   int pole1;
   int pole2;
   [...]
} stack;

int stackPop(void *elem){
   /* coś tam pobierającego, wybierającego i usuwającego */

   elem = lastElement
   return 0;
}

int stackPush(void *elem, size_t size){
   /* dodawanie */

   return 0;
}

int main(int argc, char **argv){
   /* tutaj może jakaś inicjalizacja by się pojawiła */

   stackPush(to_add, sizeof(to_add));

   if(stackPop(getEelem) == 0){
      free(getEelem);
   }

   return 0;
}

Teraz wyobraźmy sobie, że przydałoby się w tym samym albo w innym programie stworzyć kilka stosów. Ale jak to zrobić jeżeli funkcje korzystają wprost ze zmiennej globalnej? Ano trzeba wszystko przeedytować na to jak można było na samym początku napisać:

struct stack_struct {
   int pole1;
   int pole2;
   [...]
};

int stackPop(struct stack_struct *stack, void *elem){
   /* coś tam pobierającego, wybierającego i usuwającego */
   elem = lastElement;
   return 0;
}

int stackPush(struct stack_struct *stack, void *elem){
   /* dodawanie */

   return 0;
}

int main(int argc, char **argv){
   /* tutaj może jakaś inicjalizacja by się pojawiła */

   stackPush(stack1, to_add1, sizeof(to_add1));
   stackPush(stack1, to_add2, sizeof(to_add2));
   stackPush(stack2, to_add3, sizeof(to_add3));

   if(stackPop(stack1, getEelem) == 0){
      free(getEelem);
   }

   return 0;
}

Używanie zmiennych globalnych naprawdę bardzo sporadycznie jest przydatne, a jeszcze rzadziej jest potrzebne.
Jeszcze jedna różnica z wykładowymi programami to tak, że ja nie używam typedef’ów. Jedno co moge o takowych powiedzieć to to, że tworzenie typów wskaźnikowych jest głupotą/masochizmem. A jedyna różnica o jakiej wiem pomiędzy

typedef struct {
[...]
} aaa;

a

struct aaa {
[...]
};


jest przyzwyczajenie. Dla mnie czytelniejszy i wygodniejszy jest drugi zapis. Choć może typedef ma jeszcze jakieś ukryte właściwości, o których nie było mi dane do tej pory się dowiedzieć.

grudzień 30, 2006

Przykłady wxWidgets

Zaszufladkowany do: Programowanie — elmopl @ 12:25 am

Dodałem stronę zasoby, a w niej trochę przykładów do wxWidgets.

grudzień 29, 2006

Porgram “find” z wykładu

Zaszufladkowany do: C, Programowanie, Studia, Wykłady — elmopl @ 4:46 pm

Na wykładzie został przedstawiony program w ramach tematu o obsłudze plików.
Skopiowane niezbędne minimum

#define MAXLINE 1000
#define MAXSEARCH 64
void search(FILE *file, char *searchstr, int ignore){
   char li_cpy[MAXLINE];
   char line[MAXLINE];
   long linum = 0;
   char *found;

   if(ignore) _strupr(searchstr);
   while( fgets(line, MAXLINE-1, file) ){
      ++linum;
      if(ignore)
         found = strstr(_strupr(strcpy(li_cpy, line)), searchstr);
      else
         found = strstr(line, searchstr);
      if( found != NULL ){
         if(number) printf("%8d: ", linum);
         printf("%s", line);
      }
   }
}

Błąd znajduje się w części merytorycznej nie związanej bezpośrednio z głównym tematem wykładu.

Rozważmy plik

aaabaa
baaaba
a...ab

W którym w ostatniej linii znajduje się 1000 znaków “a”. Funkcja zadziała poprawnie dla pierwszych dwu linii, ale dla ostatniej pominie wszystko po 999 ‘a’ włącznie.
Z tego przykładu idzie bardzo prosta nauka: trzeba uwzględniać, że dane użytkownika zawsze mogą być “złośliwe”.

Ogólnie od siebie mogę powiedzieć, że używanie zmiennych globalnych (w tym define’ów) przy większych (pisanych dłużej jak tydzień i z ewentualnymi potrzebami późniejszych zmian) projektach jest kiepskim pomysłem. Jak znajdę jakiś zwięzły i przejrzysty przykład to dokładniej opiszę czemu.

#define TEXT “jakiś tam napis”, a co z pamięcią?

Zaszufladkowany do: C, Programowanie, Studia, Wykłady — elmopl @ 1:15 pm

Na wykładach jednym z przykładów, który mnie zaintrygował były takie oto linijki kodu:

char *tab_w[5];
tab_w[0] = "To jest tekst na pozycji 0";
#define TKST1 "To jest tekst na pozycji 1";
tab_w[1] = (char *) malloc(strlen(TKST1)+1);
strcpy(tab_w[1], TKST1);

Zastanowiło mnie czy to aby nie zajmie dwa razy za dużo pamięci. Po krótkich testach w domu okazuje się, że (przynajmniej przy kompilacji gcc) jest to zgrabnie optymalizowane.
textAsMacro.c:

#include
#include
#include 

#define TEXT "aaa"

int main(int argc, char **argv){
   printf("%d\n", (int) TEXT);
   printf("%d\n", (int) TEXT);

   printf("%d\n", (int) "aaa");

   return 0;
}

Po wywołaniu:

$ gcc -o textAsMacro.e textAsMacro.c
$ ./textAsMacro.e
134513800
134513800
134513800

Działa to w obrębie tylko jednego pliku z dość oczywistych względów.

Blog na WordPress.com.