tetszőleges méretű típusra mutató

 ( BimbaLaszlo | 2010. június 14., hétfő - 22:30 )

Tudom, nagyon elvetélt ötlet, de kíváncsi vagyok, hogy lehet-e például egy '3 bájtos mutatót létrehozni' (azaz olyan mutatót, amit ha növelek, 3 bájttal magasabb címre mutat)? Hasznos lenne például 24 bites BMP képek manipulálásához, mert (azt hiszem) gyorsabban és egyszerűbben elérném vele az adatot, mintha bájtonként pakolgatnám a pixeleket. (1 pixel = 3 bájt)

Alternatív ötletek is jöhetnek persze, de kérlek vegyétek figyelembe, hogy az ANSI C szabványnak megfelelően szeretném megírni a kódot.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Jó ötlet, de nincs ötletem, így feliratkozom. :)

------
3 fajta matematikus létezik. Aki tud számolni, és aki nem.

Mi van? Nem értem.

unsigned char *pixelbuff;
pixelbuff+=3;

Hol itt a probléma?!?

Ez by design így működik. Egy int* esetén a ++ az 4 bájtot léptet, egy char* esetén 1-et.

Pont harom bajtosat meg ugyse fog kapni, a 3 az nem kerek szam, 2 vagy 4.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

// sizeof( RBG ) == 3 byte
struct RGB
{
char R;
char G;
char B;
};
RGB * rgbBuf;
// inicializalas, etc. [ ... ]
++rgbBuf; // 1 elemet, azaz 3 bajtot leptet

Mi ezzel a gond?

----------------------
while (!sleep) sheep++;

a struct igazitas lehet a gond, mert R es G koze filler byte-ok kerulhetnek
egyebkent ugyanakkor kuldtuk be hehe

A "__packed__" megoldja ezt a problémát, de az nincs benne az ANSI C szabványban tudtommal.

jaja, de lehetne bitfieldet csinálni, akkor nem alignolja.
az meg ansi c.

typedef struct {
unsigned char R : 8;
unsigned char G; 8;
unsigned char B: 8;
} tRGBpixel;

Ha minden igaz, ez így pont 24 bit lesz, és nem lesz alignolva.
tRGBPixel *rgbBuffer = malloc(....);


#include <stdio.h>

struct RGB_t {
char rgb[3];
};

int main()
{
RGB_t buf[100];

RGB_t *p1 = buf;

printf( "%d %d %d\n", sizeof(RGB_t), p1, p1+1 );

return 0;
}

nem tudom, hogy a struct igazitas / minimum struct meret bezavarhat-e
talan nem

Mondjuk mi mintha C++-t irtunk volna,

// sizeof( RBG ) == 3 byte
struct RGB
{
char R;
char G;
char B;
};

int main( )
{
struct RGB * rgbBuf;
// inicializalas, etc. [ ... ]
++rgbBuf; // 1 elemet, azaz 3 bajtot leptet
}

Talan ez a korrekt C, de igazabol kevesse emlekszem a pontos szintaktikara a C structok eseteben.

----------------------
while (!sleep) sheep++

A struktúra így valóban C-szerűbb, mint ahogy az előbb írtad.

[TROLL ON]
A "//" komment viszont nem ANSI C szabvány. :P
[TROLL OFF]

BMP feldolgozasnal (binaris adat) erdemes lenne unsigned char-ral dolgozni, igy nem csak 128-ig latna a szineket.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

A C99-es szabvány (habár az már nem ANSI...) stdint.h headerében van 1,2,4 és 8 bájtos garantált méretű típus, de sajnos 3 bájtos nincs. Amúgy én is a struct-ra gondoltam még, de azt már írták előttem.

Köszönöm a hozzászólásokat!
Kicsit ködösen fogalmaztam, mert igaz, hogy a címzés megkönnyítésére kerestem ( és találtam ) választ, de igazán a cél az lenne, hogy ne bájtról-bájtra kelljen adatot másolni ( skaláris típusokkal ), hanem közvetlenül, mint pl. az
( int ) egyik_valtozo = ( int ) masik_valtozo
esetén egyszerre ugye 4 bájtot mozgat. Én hasonlót szeretnék, de 3 bájtos típusokkal megoldani, hogy az ilyen jellegű hosszas megoldásokat elkerüljem:
http://www.libsdl.org/cgi/docwiki.cgi/Pixel_Access
( a 3 bpp-vel kapcsolatos részeket kell figyelni )

Köztes megoldás ugyan van ( lásd a példa ), csak ezzel az a gond, hogy az utolsó pixel értékadásánál nem kívánt memóriaterületre is írunk:

#include stdio.h
#include stdlib.h

int main( void )
{
    int
        szelesseg       = 3,
        magassag        = 3,
        x               = 0,
        y               = 0;

    void
        *buff           = (unsigned char *)
                           malloc( (size_t) magassag * szelesseg*3 );

    printf( "buff kezdete   : %p\n"
            "buff vege      : %p\n\n",
            buff,
            buff + (magassag * szelesseg*3) - 1
    );

    for( y = 0 ; y < magassag ; ++y )
    {
        /* Ertekadas. */
        for( x = 0 ; x < szelesseg ; ++x )
            *(int *)(buff + (y * szelesseg*3) + (x*3)) = 0x58424752;
                                                        /* X B G R
                                                         * az X csak egy jel*/

        /* Kiiratas. */
        for( x = 0 ; x < szelesseg*3 ; ++x )
            printf( "%p : %c\n",
                    (buff + (y * szelesseg*3) + x),
                    *(char *)(buff + (y * szelesseg*3) + x)
            );

        printf( "\n" );
    }

    printf( "%p : %c\n",
            (buff + ((y-1) * szelesseg*3) + x),
            *(char *)(buff + ((y-1) * szelesseg*3) + x)
    );

    free( buff );

    return( 0 );
}

--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

hogy ne bájtról-bájtra kelljen adatot másolni ( skaláris típusokkal ), hanem közvetlenül

Ilyen nincs.

Bár nem tudom, hogy miért akarsz mást, mint bájtról-bájtra másolni.

Ha a hatékonyság a gond, akkor nem tudod elkerülni a kézzel, jól kiszámított módon megírt memcpy-t, legfeljebb becsomagolod valamibe, hogy ne legyen annyira randa.

Ha a "látványra" gyúrsz, akkor tessék függvényt írni (vagy c++ esetén objektum + metódus), és akkor a függvény/metódus hívása szép lesz, azon belül viszont a csúnyaság megmarad.

És engedtessék meg még egy megjegyzés: nagyon sok cpu van (nagyjából már egy 486-os is ez a kategória, onnan fölfele meg pláne), ahol jó esélyed van rá, hogy a 4-bájtos struct/int kezelése fényévekkel gyorsabb, mint ha összepakolod 3-bájtos távolságra, egymás mellé a rekordokat, és próbálsz 3 bájtonként szívni. A cpu ugyanis rendelkezik olyan utasítással, ami egy 4-bájtos wordöt egyben beolvas, kiír, viszont 3 bájt mozgatása több utasítást fog igényelni.

A gyorsaság miatt szeretném kiváltani a bájtok egyenkénti másolását, például azzal, hogy egy int típust mozgatok egy char helyett (lásd a kódot).

nagyon sok cpu van (...), ahol jó esélyed van rá, hogy a 4-bájtos struct/int kezelése fényévekkel gyorsabb

Erre konkrét kódot tudnál mutatni, mert nem egészen értem.
A függvényhívásokkal az a bajom, hogy az plusz órajelekbe kerül, ezért nem tudom, mennyire hatékony, de most kipróbálom és legalább olyan gyors, mint a helyben megírt kód, akkor van egy újabb megértendő dolog a C-ben, aminek utánna kell járnom...

--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

Mivel a procid 32b-es ezért 32b-es adatokkal érzi magát igazán jól, de 24b-essel semmiképpen. Általában egyszerűbb és hatékonyabb 32b-es változót használni arra is.
Gyakorlati példa: van egy for ciklusod ami csak - hasraütve - 112* kell, hogy lefusson, mégsem fogsz byte típust használni, mert az int gyakorlatilag gyorsabb és a memória használatot ebben az esetben elfelejtheted (főleg, hogy jó esetben a processzor regiszteréből soha sem fog kikerülni).

A függvényhívásokról csak annyit, hogy ha úgy optimálisabb (kevés helyen van használva, nagyon rövid, stb.) úgyis lerendezi a fordító. Ha meg mindenhova raksz egy rakás különféle kódot akkor kis szerencsével a processzor cache megtelése miatt elveszted az előnyt. Sajnos az optimalizálás nem is olyan egyszerű...

arra utal a kollega, hogy a processorok csak szohatarokrol tudnak altalaban olvasni gyorsan (risc gepek neha meg lassan sem)

--
NetBSD - Simplicity is prerequisite for reliability

Erre konkrét kódot tudnál mutatni, mert nem egészen értem.

A >=386-os cpu tud egyszerre 1, 2 vagy 4 byte-ot mozgatni egy assembly utasítással. A normál 32-bites üzemmódokban a 4 byte-os mozgatásos utasítás X byte hosszú (ez az alap), az 1 byte-os mozgatásos szintén, de a 2-byte-os mozgatás egy byte-tal hosszabb assembly kód. 3 byte-ot két utasításban lehet mozgatni (egy 1 és egy 2 byte-os mozgatás), a két utasítás 2*X + 1 byte hosszú kód lesz, végrehajtási időben pedig min. 2-szer annyi idő (az 1 és a 2 byte-os művelet ugyanazt a cache sort érinti, azokat csak egymás után lehet végrehajtani). Tehát több mint duplájára sikerült növelni a kód méretét (és így a beolvasásához szükséges időt is) és a végrehajtási időt is.

Azert akad itt néhány probléma:

  1. a kód épit a proci byte sorrendjére. Egy big-endian procin a kódod furcsa dolgokat fog művelni.
  2. sok CPU (pl ARM) nem képes 32 bit széles értékeket nem 4 byte -ra igazitott cimről olvasni. Jó esetben kapsz egy data abort exceptiont, rossz esetben csak hibás adatot olvas a proci. Azér az emlitésre méltó, hogy az X86 is sokkal gyorsabban olvas, ha megfelelően igazitott cimrol olvasol. Legalabbis a 486 -nal ez még számitott.
  3. a kód feltételezi hogy az int 32 bit széles. Próbáld meg leforditani 64 bites CPU -ra. Használj inkább uint32_t tipust.

1 és (részben) 2 tárgytalan, ha PC -re irod a kódot, és sosem akarod majd már CPU -un futtatni. 3. ponttal azonban PC -n is problémád lehet.

A sima int 32 bit-es x86_64-en. Ami változott az a long int és a pointer mérete.

"hogy ne bájtról-bájtra kelljen adatot másolni ( skaláris típusokkal ), hanem közvetlenül, mint pl. az
( int ) egyik_valtozo = ( int ) masik_valtozo"
memcpy?

Kicsit lassabb az értékadáshoz képest...
--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

Bitmaszkolásra gondoltál már 4 bytes változóval, ahol az utolsó byte kihasználatlan? Így van egy 4 bytes egységed, amit tudsz makrókkal manipulálni. Mondjuk valami ilyesmi:

uint32_t rgb; /* UUUUUUUURRRRRRRRGGGGGGGGBBBBBBBB */

#define GET_RED (rgb >> 16);
#define SET_RED(x) ((x << 16) & rgb);

...

A sima uint_32_t változót meg egyszerűen tudod másolni. Ha pointerekkel dolgozol, vagy több ilyen változó kell, akkor meg a makróknak adhatod paraméterként a változót is.

Menj 4 bájtra, akkor RGBA-ra is megvan a megoldás.

+1. Nameg gyorsabb azzal dolgozni.

----------------
Lvl86 Troll

Ez sokat dobott a sebességen, köszi! És köszönet az előző hozzászólóknak is, mert teljes mértékben igazatok van, hogy a 2**n bájtokkal könnyebben bánik a gép.
--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

A logika egyebkent egyszeru. Minden szamitogep binaris rendszerben gondolkodik, tehat mindig ketto hatvanyaival tud a legkonnyebben dolgozni. Amint nem ketto hatvanyaival kell szamolni, egy csomo workaroundot kell tennie, igy romlik a teljesitmeny.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.