Pokazywanie postów oznaczonych etykietą gcc. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą gcc. Pokaż wszystkie posty

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/04/02

64bit vs 32bit

Dzisiaj poprawiałem bugi w Dbe które ujawniły sie dopiero na 64bitowej maszynie. Dla treningu popełniłem też taki program:

#include
#include

int main()
{
    int i = -1;
    long l = i;
    unsigned long ul = i;
    enum {
        zero = 0
    };

    printf("sizeof(i) = %zi, sizeof(l) = %zi\n", sizeof(i), sizeof(l));
    printf("sizeof(zero) = %zi\n", sizeof(zero));
    printf("sizeof(size_t) = %zi\n", sizeof(size_t));
    printf("l = i -> %lx\n", l);
    printf("(long)i -> %lx\n", (long)i);
    printf("(long)-1 -> %lx\n", (long)-1);
    printf("-1L -> %lx\n", -1L);
    printf("ul -> %lx\n", ul);

    i = l;
    printf("i = l -> %x\n", i);

    return 0;
}


Wyniki:

sizeof(i) = 4, sizeof(l) = 8
sizeof(zero) = 4
sizeof(size_t) = 8
l = i -> ffffffffffffffff
(long)i -> ffffffffffffffff
(long)-1 -> ffffffffffffffff
-1L -> ffffffffffffffff
ul -> ffffffffffffffff
i = l -> ffffffff

Natrafiłem też na taki dokument: http://www.ibm.com/developerworks/library/l-port64.html. Oto ciekawsze fragmenty:

Declarations
To enable your code to work on both 32-bit and 64-bit systems, note the following regarding declarations:
  • Declare integer constants using "L" or "U", as appropriate.
  • Ensure that an unsigned int is used where appropriate to prevent sign extension.
  • If you have specific variables that need to be 32-bits on both platforms, define the type to be int.
  • If the variable should be 32-bits on 32-bit systems and 64-bits on 64-bit systems, define them to be long.
  • Declare numeric variables as int or long for alignment and performance. Don’t try to save bytes using char or short.
  • Declare character pointers and character bytes as unsigned to avoid sign extension problems with 8-bit characters.
Expressions
In C/C++, expressions are based upon associativity, precedence of operators and a set of arithmetic promotion rules. To enable your expression to work correctly on both 32-bit and 64-bit systems, note the following rules:
  • Addition of two signed ints results in a signed int.
  • Addition of an int and a long results in a long.
  • If one of the operands is unsigned and the other is a signed int, the expression becomes an unsigned.
  • Addition of an int and a double results in a double. Here, the int is converted to a double before addition.

Bit shifting
Untyped integral constants are of type (unsigned) int. This might lead to unexpected truncation while shifting.
For example, in the following code snippet, the maximum value for a can be 31. This is because the type of 1 << a is int.
long t = 1 << a;
To get the shift done on a 64-bit system, 1L should be used as shown below:
long t = 1L << a; 


Oraz taki dokument http://www.unix.org/version2/whatsnew/lp64_wp.html gdzie można znaleźć uzasadnienie dla modelu LP64:

2010/03/12

Pierwszy post

Czy zawsze aktywujesz ostrzeżenia kompilatora ? Poniższy przykład pokazuje dlaczego są one niezbędne:


int i;
...
if( i < sizeof(int) ) {
   ...
}


Powyższy kod będzie działał tak jak zamierzamy, jeśli "i" będzie przyjmować jedynie wartości dodatnie. Jeśli w "i" znajdzie się liczba ujemna, warunek nie zostanie spełniony, choć matematycznie wszystko wygląda OK. Wynika to z faktu że porównywanie typow signed i unsigned wymusza na kompilatorze rzutowanie do typu unsigned. Z tąd liczba ze znakiem int jest rzutowana do unsigned int. Jak łatwo się domyśleć z -1 robi się 4294967295, co jasno pokazuje ze -1 > 4 :)

Aby otrzymac ostrzeżenie przed tego typu pomyłkami należy w gcc dodać opcje -Wsign-compare lub najlepiej -Wextra która ostrzega nas przed kilkoma innymi błędami. -Wextra jest rozszerzeniem -Wall, samo -Wall nie zawiera -Wsign-compare.

Po dodaniu Wextra prawdopodobnie otrzymamy szereg ostrzeżeń które będziemy chcieli usunąć. Jednym z takich ostrzeżeń są nieużywane parametry funkcji. Jest to dość irytujące ponieważ często zdarza się że celowo nie chcemy wykorzystywać wszystkich parametrów, choćby ze względu na wymuszony format funkcji potrzebnej do wywołania zwrotnego (callback). W takim wypadku możemy posłużyć się poniższym makrem:


#ifdef unused
#elif defined(__GNUC__)
# define unused(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define unused(x) /*@unused@*/ x
#else
# define unused(x) x
#endif


np:


int fkt(int unused(opt_param)) {
}


Makro można rozszerzyć o obsługę dodatkowych kompilatorów, tak jak zostało to wykonane dla parsera składni LINT.