2010/12/22

Niewinne static

Czym różną się zmienne globalne oznaczone jako static:
- ich definicja nie jest dołączana do tablicy exportu
- są inicjalizowane tylko raz (podczas uruchomienia kodu startowego, domyślnie 0 (sekcja bss) lub wartością podana przy deklaracji (sekcja .data))

O ile pierwsza właściwość jest mało inwazyjna to druga ma pewne konsekwencje jeśli o niej zapomnimy.
Gdybyśmy np chcieli pisać pseudo obiektowo wykorzystując czyste C, i zapomnieli o konstruktorze statycznym a uprościli sprawę inicjalizujac zmienne współdzielone w miejscu deklaracji.

Np załóżmy ze mamy do napisania prosta klasę która ma jedna zmienna współdzielona przez wszystkie instancje oraz funkcje Create, Destroy i DoSome.

static int mod_state_variable = 10; //dla uproszczenia pomijam mutexy
typedef struct Mod_Tag
{
      int some_var;
} Mod_T;

void Mod_Creat(Mod_T *mod)
{

    mod->some_var = 0;
}

void Mod_Destroy(Mod_T *mod)
{
}

void Mod_DoSome(Mod_T *mod)
{
   if( 10 == mod_state_variable )
       mod_state_variable = 50;
   mod_state_variable++;
   mod->some_var++;
}

sekwencja
Mod_Init();
Mod_DoSome();
Mod_DoSome();
Mod_Finalize();

da wynik
mod_state_variable = 51 mod->some_var = 1 po pierwszym DoSome
mod_state_variable = 52 mod->some_var = 2 po drugim DoSome

powtórzenie sekwencji
Mod_Init();
Mod_DoSome();
Mod_DoSome();
Mod_Finalize();

da nam
mod_state_variable = 53 mod->some_var = 1 po pierwszym DoSome
mod_state_variable = 54 mod->some_var = 2 po drugim DoSome

ponieważ zmienna statyczna mod_state_variable inicjalizowana jest tylko raz.
Jeśli chcemy przywrócić pierwotny stan zmiennej współdzielonej musieli byśmy użyć coś na wzór konstruktora statycznego.

Podobnie, inicjalizacja w miejscu deklaracji nie spełni swojego zadania np w przypadku bibliotek dynamicznych które posiadają tak inicjalizowane zmienne statyczne. Dopiero odłączenie biblioteki i ponowne jej podłączenie spowoduje przeinicjalizowanie takich zmiennych.

2010/10/14

Amnezja

Czasami mam wrażenie że metodą optymalizacji zapominam co mniej istotne aspekty. Kiedyś już chyba głowiłem się nad zagadnieniem przekazywania tablic w parametrach funkcji.

Przywykłem do tego ze tablice deklarujemy w parametrach funkcji jako
fkt(int tab[])

W kodzie kolegi zobaczyłem
fkt(int tab[10])

Pierwsze skojarzenie jakie mi się nasunęło, to że ta tablica przejdzie przez stos jako kopia pamięci (40bajtow)...

Okazalo sie ze jest inaczej:
#include

struct ss {
    int field1;
    int field2;
    int tab[100];
};

void fkt(char tab[10])
{
    printf("%p\n", tab);
}

void fkt2(struct ss param)
{
    printf("%p\n", &param);
}

int main()
{
    char tab[10] = { 0, 1, 2 };
    char tab2[] = { 1, 1, 1};
    struct ss param;

    fkt(tab);
    printf("%p\n", tab);
    fkt(tab2);
    printf("%p\n", tab2);

    fkt2(param);
    printf("%p\n", &param);

    return 0;
}


Wynik:
[xzs6ts@TVBuildAlfa BWL]$ ./a
0xbfa01916
0xbfa01916
0xbfa01913
0xbfa01913
0xbfa015d0
0xbfa01778


Dlaczego przekazywanie struktur przez stos jest wykonywane jako kopia a tablic jako referencja ?
"nazwa zmiennej tablicowej jest jednoczenie wskaźnikiem do pierwszego elementu"
Z tego tez powodu w języku C nie możemy powiedzieć "przekaz tablice", bo "się po prostu nie da" :)
Za to możemy powiedzieć "przekaz strukturę" co tez kompilator czyni.

2010/09/10

Jak zawiesic programiste ?

Uff było ciężko ...

Problem jest taki, mamy taki niewinny kod:

#include

int get_val(const int **pp)
{
    return **pp;
}

int main()
{
    int v = 10;
    int *t[] = { &v };

    printf("%i\n", get_val(t) );
}


Kompilator w linii z printf zgłasza błąd ? Dlaczego, przecież nie robię nic złego :). Deklaruje tablice wskaźników do int o wielkości jednego elementu (jeden wskaźnik), i chce go przekazać wskaźnik do tej tablicy do funkcji która zwraca wartość; Funkcja nie modyfikuje zawartości elementów wiec dodajemy const, tak dla zasady :) (bo w firmie wymagają żeby dawać const)

Poniższa strona nie za bardzo mnie przekonała:
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17

Wolałem sobie to wytłumaczyć następująco:
#include

const v = 0;
const int *p = &v;

int get_val(const int **pp)
{
    *pp = p;
    return **pp;
}

int main()
{
    int v = 10;
    int *t[] = { &v };

    //printf("%i\n", get_val(t) );

    *(t[0]) = 20; //tutaj teoretycznie modyfikujemy lokalna zmienna, a tak naprawdę funkcja podmieniła wskaźnik i modyfikujemy const ... otrzymujemy SIGSEG
    printf("%i\n", v );
}


Teraz ewidentnie widać dlaczego rzutowanie int ** na const int ** jest zabronione :)
Bład w stwierdzeniu jest następujący:
Deklaruje tablice wskaźników do int o wielkości jednego elementu (jeden wskaźnik), i chce go przekazać wskaźnik do tej tablicy do funkcji która zwraca wartość. Funkcja nie modyfikuje zawartości elementów ALE MOŻNE MODYFIKOWAĆ ZAWARTOŚĆ TABLICY :), w szczególności podmienić wskaźnik na coś czego zmieniać nie wolno (a co jest poprawnym adresem procesu).

PS: A mówią ze stałe wskaźniki są niepopularne
PS2: Teraz już wiem gdzie są wykorzystywane :P. Wykorzystywane są do męczenia biednych developerów :D
PS3: Wymiękam ... dlaczego poniższe jest problemem ? HELP!!!
int main()
{
    int v;
    int * const p = &v;
    const * const **pp = &p;
}

2010/08/25

Rozmowa kwalifikacyjna

Pytania z serii "rozmowa kwalifikacyjna"

jaka jest różnica między poniższymi deklaracjami tab

int function(int tab[])
{
...
}

struct SomeStruct_T
{
   int tab[];
};

Odpowiedz:
deklaracja w parametrze funkcji nie alokuje pamięci na stosie, jest ona równoważna z deklaracja function(int *tab)
deklaracja w strukturze również nie alokuje pamięci ale dostęp do pola tab będzie się odbywał poprzez referencje do pamięci znajdującej sie bezpośrednio za struktura, w ten sposób można tworzyć np tablice o zmiennej długości pamiętając o alokowaniu odpowiedniej ilosci pamięci, np na stercie.

jaka jest roznica miedzy poniższmi deklaracjami field

struct SomeStruct
{
   union field
   {
      int a;
   };
}

struct SomeStruct
{
   union
   {
      int a;
   } field;

}

Odpowiedz:
pierwsza to czysta deklaracja unii o nazwie field, struktura nie allokuje przestrzeni na zadne pole
druga to deklaracja pola field o typie unii anonimowej, struktura będzie miała rozmiar sizeof(int)

2010/07/06

Dlaczego zamkniete oprogramowanie jest passé

Oto odpowiedz dlaczego producenci oprogramowania powinni gwarantować bezpieczeństwo swoich produktów. Kto wie ... może kiedyś :)

http://www.heise-online.pl/newsticker/news/item/Luki-Microsoftu-raz-Full-Disclosure-a-raz-Null-Disclosure-1033694.html

2010/06/29

SIGSEGV

Gdyby ktoś potrzebował kodu obsługującego SIGSEGV w aplikacji:

gcc -rdynamic

#define _GNU_SOURCE
#include
#include
#include
#include

----------- SIGSEG!!! -----------
Fault addr [0x00000001] in function [0x00416d94] FKT_BL_Creat+0x32
-------- Backtrace start --------
Obtained 8 stack frames.
0 : [0x00408018] pthread_create+0x2d2
1 : [0x2956e420] __kernel_rt_sigreturn+0x0
2 : [0x00416d94] FKT_BL_Creat+0x32
3 : [0x00415fcc] Fkt_Tuning_Init+0x90
4 : [0x00408018] pthread_create+0x8d8
5 : [0x00408018] pthread_create+0x654
6 : [0x0043123c] unresolved
7 : [0x2958559e] unresolved
Aborted

static void sigseg_hndlr(int unused(signum), siginfo_t* siginfo, void* ucont)
{
   void * array[50];
   size_t size;
   size_t i;
   ucontext_t *ucontext = (ucontext_t*)ucont;
   void* pc = (void*)(ucontext->uc_mcontext.gregs[REG_EIP]); /* for x86 */
   Dl_info dlinfo;

   printf("----------- SIGSEG!!! -----------\n");
   if( dladdr(pc, &dlinfo) && (NULL != dlinfo.dli_sname) )
      printf("Fault addr [0x%08x] in function [0x%08x] %s+0x%x\n", (unsigned int)(siginfo->si_addr), (unsigned int)(dlinfo.dli_saddr), dlinfo.dli_sname, pc - dlinfo.dli_saddr );
   else
      printf("Fault addr [0x%08x] in function [0x%08x] unresolved\n", (unsigned int)(siginfo->si_addr), (unsigned int)(pc));

   printf("-------- Backtrace start --------\n");
   size = backtrace(array, 50);
   printf ("Obtained %zd stack frames.\n", size);
   for (i = 0; i < size; i++)
   {
      if( dladdr(array[i], &dlinfo) && (NULL != dlinfo.dli_sname) )
         printf("%d : [0x%08x] %s+0x%x\n", i, (unsigned int)(dlinfo.dli_saddr), dlinfo.dli_sname, array[i] - dlinfo.dli_saddr );
      else
         printf("%d : [0x%08x] unresolved\n", i, (unsigned int)(array[i]));
   }

   fflush(stdout);
   abort();
}


int main(int argc, char* argv[])
{
   SAL_Config_T sal_config;
   SAL_Thread_Attr_T thread_attr;
   int retv = -1;

   sigaction(
      SIGSEGV, /* int signum, */
      &sigsegact, /* const struct sigaction *act, */
      NULL /* struct sigaction *oldact */ );
}

2010/06/25

.Niet

Wyobraź sobie świat, w którym nie mógł byś naprawiać rzeczy które cię otaczają ? Weźmy np twój samochód, wyobraź sobie że właśnie się zepsuł i jedynym sposobem aby go naprawić, jest oddanie do warsztatu. Nie jesteś w stanie zdobyć żadnych rysunków czy schematów. Zepsuł ci się odkurzacz? masz pecha, bo nie da się go otworzyć i naprawić, musisz czekać 2 tygodnie na serwis.

Co w tym dziwnego, skoro tak właśnie zaczyna działać rynek? Moim zdaniem to wygodnictwo, przyzwyczaja nas do "niemyślenia". Nie musisz wiedzieć jak coś jest skonstruowane, żeby tego używać, ale gdy skończy się gwarancja, skazany jesteś na zakup nowego produktu.

Po co ten wywód ? Ot tak właśnie działa obecny rynek oprogramowania. Od 4 kwietnia nie istnieje wsparcie dla Windows Xp a łatki bezpieczeństwa wydawane będą do 4 sierpnia 2014r. Długo ? Weźmy pod uwagę nowy sprzęt. Już teraz widać jak Xp odstaje. Nie zobaczymy na nim DirectX 10 ani sterowników USB3.0. Zapomnij więc np o nowych kartach graficznych i szybkich pamięciach przenośnych. Tak właśnie M$ zmusza nas do kupowania nowych produktów, aby zachować ciągłość przychodów.
Niepokojące jest też to, że firmy takie jak M$ wypuszczają własne produkty przeznaczone dla programistów: biblioteki i narzędzia. Przykładem niech będzie Apple i ich framework do iPhone. Chcesz sprzedawać swoje produkty ? musisz kupić nasze biblioteki? To jest naprawdę chore, nie dość że dobrowolnie zgadzam się na wsparcie waszej technologi, to jeszcze muszę opłacać jakiś haracz ? To samo jest z .Niet Mobile. Albo kupujesz M$ Visual Studio w wersji Professional, albo piszesz w notatniku :). Masakra ... niedługo może będę musiał płacić za poprawki bezpieczeństwa ?

Zmierzając do meritum, dlaczego nie lubię .Niet ? Podobnie jak z oprogramowaniem użytkowym i porównaniem do obecnego trendu rynku konsumenckiego jesteś skazany na używanie "jednego słusznego API". Dokumentacja też pozostawia wiele do życzenia. Pomijając cała masę bugów które uprzykrzały mi życie przez ostanie 2 lata, .Niet jest strasznie niewygodny (API jest jakieś skrzywione). Cokolwiek chcesz zrobić "po swojemu" wbrew "jedynej słusznej drodze", napotykasz problemy.

Dlaczego lubię OpenSource ? Ponieważ nigdy nie czujesz się skrepowany, masz pełny dostęp do źródeł. Jeśli coś nie działa tak jak chcesz, zawsze możesz przerobić API, poprawić błąd w bibliotece lub poprostu "zobaczyć pod maskę". O to właśnie chodzi w programowaniu, o radość płynąca z uczenia się na błędach, modyfikacji i wzorowaniu się na pracy innych. Do niczego nie jesteś zmuszany. Nie odpowiada ci jakaś biblioteka ? jest tysiące innych. Tak właśnie powstają dobre standardy, nikt ich nie narzuca, one same "wypływają na wierzch" bo są po prostu dobre. To nie jest .Niet "jedynie słuszna droga". Co więcej, jeśli masz odmienny pomysł na rozwiązanie danego problemu, możesz podzielić się nim z innymi. Ciągłe doskonalenie i wymiana doświadczeń jest podstawa idei OpenSource.

Ludzie którzy głoszą "nie da się zarobić na otwartym oprogramowaniu" są dla mnie fanatykami. Moja firma już od kilku lat współpracuje z firmami oferującymi support dla produktów OpenSource, a nawet zatrudniamy programistów ze świata OpenSource, aby dla nas przygotowali modyfikacje ich kodu. OpenSource nie oznacza FreeSoftware jak większości się wydaje.

Dlaczego o tym pisze ? Bo dziwi mnie ze tak wielu programistów dobrowolnie oddaje swoje "samochody" i "odkurzacze" do serwisu. Godzą się na to, zamiast być ciekawym (moim zdaniem podstawowa cecha dobrego programisty). Nie zadają sobie pytania "dlaczego?" tylko "kiedy?". "Kiedy ten problem zniknie żebym mógł skończyć moją robotę" albo "Jak ominąć ten BUG żeby to jednak działało" i tak od przypadku do przypadku.
Niepokojące jest też to, że duże firmy IT zachęcają studentów do uczenia się i używania swoich produktów, aby już na starcie ich kariery zapewnić sobie przyszłych klientów. Oni przecież będą używać tych bibliotek, produkować swoje oprogramowanie bazując na tych technologiach. Przez takie programy jak MSDNAA, M$ niskim kosztem zapewnia sobie bardzo dobrą pozycje na rynku. Uważam to za złą praktykę jakiekolwiek są z tego korzyści dla studentów. To samonapędzający się mechanizm. .Niet jest popularny, więc znać go wypada (może przyda się w przyszłej pracy). A później chcąc nie chcąc, wracamy do tego co już znamy i popularyzujemy dalej, napędzając kasę do kieszeni M$. Nie wiem dlaczego, ale na moich studiach nie było zajęć, na których omawiano by biblioteki/rozwiązania OpenSource. Dlatego właśnie uważam to za szkodę dla samych studentów.

Na studiach wydawało mi się że, OpenSource to coś lepszego. To słowo miało w sobie jakąś wyższą idee, którą (czasami na siłę) starałem się przekazać innym :). Dzisiaj mogę z czystym sumieniem powiedzieć że na OpenSource nie trzeba "nawracać", tym się po prostu zaraża :). Wszystkie moje znaczące projekty które oparłem na OpenSource zaliczam do sukcesów, te oparte na rozwiązaniach komercyjnych do mniejszych lub większych porażek. Moja ocena wynika z czystej kalkulacji czasu i nerwów jakie poświęciłem.

Słowo kończące: możesz się nie zgadzać z opiniami tu wyrażonymi, jeśli wygodnie ci z M$ po prostu zostań tam gdzie jesteś :)

EDIT: heh ... nie tylko ja jestem zdania ze .Niet :D, oto wersja multimedialna ... .Not https://www.youtube.com/watch?v=RnqAXuLZlaE