Кіріспе
Деректер компьютер жадында қалай орналасатыны және оларға қалай қол жеткізілетіні. Деректер құрылымының реттелуі – компьютер жадында мәліметтердің орналасуы мен оларға қол жеткізу тәсілі. Ол үш жеке, бірақ байланысты мәселеден тұрады: деректерді реттеу, деректер құрылымын толтыру және жинақылау. Қазіргі заманғы компьютерлік жабдықтағы процессор (CPU) деректер табиғи түрде реттелген кезде жадтан оқу және жазу операцияларын тиімді орындайды, яғни деректердің жад адресі деректердің мөлшеріне еселенген болады. Мысалы, 32 биттік архитектурада деректер төрт байттың тізбегімен сақталса және бірінші байт 4 байттық шекарада орналасса, деректер реттелген болып саналады. Деректерді реттеу – элементтерді олардың табиғи реттелуіне сәйкес келтіру. Табиғи реттелуді қамтамасыз ету үшін құрылым элементтерінің арасына немесе құрылымның соңғы элементінен кейін толтыру енгізу қажет болуы мүмкін. Мысалы, 32 биттік машинада 16 биттік мәннен кейін 32 биттік мән келетін деректер құрылымында 32 биттік мәнді 32 биттік шекарада реттеу үшін 16 биттік толтыру қажет болуы мүмкін. Балама ретінде, құрылымды толтыруды жойып, жинақылауға болады, бұл қол жеткізу жылдамдығын төмендетуі мүмкін, бірақ жадты үштен бір бөлікке аз пайдаланады. Деректер құрылымын реттеу барлық заманауи компьютерлер үшін маңызды мәселе болса да, көптеген компьютерлік тілдер мен компьютерлік тілдерді іске асыру деректерді автоматты түрде реттейді. Fortran, Ada, PL/I, Pascal, C және C++ тілдерінің кейбір іске асырылулары, D, Rust, C# және ассемблер тілі деректер құрылымын толтыруды ішінара басқаруға мүмкіндік береді, бұл кейбір ерекше жағдайларда пайдалы болуы мүмкін.
Data structure alignment is the way data is arranged and accessed in computer memory. It consists of three separate but related issues: data alignment, data structure padding, and packing. The CPU in modern computer hardware performs reads and writes to memory most efficiently when the data is naturally aligned, which generally means that the data's memory address is a multiple of the data size. For instance, in a 32 bit architecture, the data may be aligned if the data is stored in four consecutive bytes and the first byte lies on a 4 byte boundary. Data alignment is the aligning of elements according to their natural alignment. To ensure natural alignment, it may be necessary to insert some padding between structure elements or after the last element of a structure. For example, on a 32 bit machine, a data structure containing a 16 bit value followed by a 32 bit value could have 16 bits of padding between the 16 bit value and the 32 bit value to align the 32 bit value on a 32 bit boundary. Alternatively, one can pack the structure, omitting the padding, which may lead to slower access, but uses three quarters as much memory. Although data structure alignment is a fundamental issue for all modern computers, many computer languages and computer language implementations handle data alignment automatically. Fortran, Ada, PL/I, Pascal, certain C and C++ implementations, D, Rust, C#, and assembly language allow at least partial control of data structure padding, which may be useful in certain special circumstances.
Анықтамалар
Жад адресі a, n байтқа сәйкестірілген деп есептеледі, егер a, n-нің есесі болса (мұнда n – 2-нің дәрежесі). Бұл контексте, байт – жадқа қол жеткізудің ең кішкентай бірлігі, яғни әр жад адресі әртүрлі байтты көрсетеді. n байтқа сәйкестірілген адресте, екілік түрінде көрсетілгенде, ең кем маңызды нөлдердің саны log2(n) болады. b бит сәйкестірілген термині, b/8 байтқа сәйкестірілген адресті білдіреді (мысалы, 64 бит сәйкестірілген – 8 байтқа сәйкестірілген). Жадқа қол жеткізу, егер қол жеткізіліп жатқан дерек n байтқа тең болса және дерек адресі n байтқа сәйкестірілген болса, сәйкестірілген деп есептеледі. Егер жадқа қол жеткізу сәйкестірілмеген болса, ол ретсіз сәйкестірілген деп аталады. Байқаңыз, байттық жадқа қол жеткізу әрқашан сәйкестірілген болады. n байттық бастапқы деректерге сілтеме жасайтын жад көрсеткіші, егер ол тек n байтқа сәйкестірілген адрестерді ғана қамтуға рұқсат етілсе, сәйкестірілген деп есептеледі, әйтпесе ол сәйкестірілмеген болып саналады. Деректер жиынтығына (деректер құрылымы немесе массив) сілтеме жасайтын жад көрсеткіші, егер жиынтықтағы барлық бастапқы деректер сәйкестірілген болса (және тек сонда ғана) сәйкестірілген болып есептеледі. Жоғарыдағы анықтамалар, әрбір бастапқы деректің ұзындығы екінің дәрежесіне тең деп есептейді. Егер бұл жағдай орындалмаса (мысалы, x86 архитектурасындағы 80 биттік қалқымалы нүктелік сандардағыдай), деректің сәйкестірілгені немесе сәйкестірілмегені анықталуы контекстке байланысты болады. Деректер құрылымдары жадта статикалық өлшеммен, шектеулі деп аталатын стекте немесе динамикалық өлшеммен, шексіз деп аталатын үймеде сақталуы мүмкін.
Қиындықтар
Процессор жадқа бір уақытта бір сөз арқылы кіреді. Егер жад сөзінің мөлшері компьютер қолдайтын ең үлкен бастапқы дерек типінің мөлшерінен кем болмаса, тураланған кіру әрқашан бір жад сөзіне қол жеткізеді. Бұл тураланбаған деректерге қол жеткізу кезінде дұрыс болмауы мүмкін. Егер деректің ең жоғары және ең төменгі байттары бір жад сөзінің ішінде болмаса, компьютер дерекке кіруді бірнеше жад кірулеріне бөлуі керек. Бұл жад кірулерін жасау және оларды үйлестіру үшін күрделі схемаларды қажет етеді. Егер жад сөздері әртүрлі жад беттерінде болса, процессор нұсқауды орындау алдында екі беттің де бар екенін тексеруі керек немесе нұсқау орындалу кезінде кез келген жад кіруінде TLB қатесін немесе бет қатесін өңдеуі керек. Кейбір процессорлар мұндай күрделіліктен қасақана бас тартады және тураланбаған жад кіру жағдайында баламалы мінез-құлық көрсетеді. Мысалы, ARMv6 ISA нұсқасынан бұрынғы ARM архитектурасының іске асырылымдары барлық көп байттық жүктеу және сақтау нұсқаулары үшін міндетті түрде тураланған жад кіруін талап етеді. Берілген нақты нұсқауға байланысты, тураланбаған кіруге тырысудың нәтижесі ретінде кінәлі мекенжайдың ең кіші маңызды биттері төмен қарай дөңгеленуі мүмкін, осылайша тураланған кіруге айналады (кейде қосымша шарттармен), немесе MMU ерекшелігі тууы мүмкін (егер MMU аппараттық құралы болса), немесе басқа да болжауға қиын нәтижелер пайда болуы мүмкін. ARMv6 және одан кейінгі архитектуралар көптеген жағдайларда тураланбаған кіруді қолдайды, бірақ міндетті түрде емес. Бір жад сөзіне кіргенде, операция атомдық болып табылады, яғни бүкіл жад сөзі бірден оқылады немесе жазылады, ал басқа құрылғылар оқу немесе жазу операциясы аяқталғанға дейін күтуі керек. Бұл бірнеше жад сөздеріне тураланбаған кірулер үшін дұрыс болмауы мүмкін, мысалы, бірінші сөзді бір құрылғы оқи алады, екі сөзді де басқа құрылғы жазады, содан кейін екінші сөзді бірінші құрылғы оқиды, нәтижесінде оқылған мән бастапқы мән де, жаңартылған мән де емес. Мұндай қателер сирек кездеседі, бірақ оларды анықтау өте қиын болуы мүмкін.
Деректер құрылымының толтыруы
Компилятор (немесе интерпретатор) әдетте жеке деректерді туралау шекаралары бойынша бөледі, бірақ дерек құрылымдарында әртүрлі туралау талаптары бар мүшелер жиі кездеседі. Дұрыс туралауды сақтау үшін аудармашы әдетте қосымша атаусыз дерек мүшелерін қосады, осылайша әрбір мүше дұрыс тураланады. Сонымен қатар, дерек құрылымының өзі соңғы атаусыз мүшемен толтырылуы мүмкін. Бұл құрылымдар тізіміндегі әрбір мүшенің дұрыс туралануын қамтамасыз етеді. Толтыру тек қана құрылым мүшесі үлкенірек туралау талаптары бар мүшеден кейін немесе құрылым соңында орналасқанда қосылады. Құрылымдағы мүшелердің ретін өзгерту арқылы туралауды сақтау үшін қажетті толтыру мөлшерін өзгертуге болады. Мысалы, мүшелер туралау талаптарының кемуі бойынша сұрыпталған болса, ең аз толтыру қажет болады. Қажетті ең аз толтыру әрқашан құрылымдағы ең үлкен туралаудан кіші болады. Қажетті толтырудың ең көп мөлшерін есептеу күрделірек, бірақ ол әрқашан барлық мүшелердің туралау талаптарының қосындысынан кем және құрылым мүшелерінің ең аз тураланған жартысының туралау талаптарының қосындысынан екі есе кем. C және C++ тілдері компиляторға орынды үнемдеу үшін құрылым мүшелерін қайта реттеуге рұқсат бермейді, бірақ басқа тілдерде мұндай мүмкіндік бар. Сонымен қатар, көптеген C және C++ компиляторлары құрылым мүшелерін белгілі бір деңгейде "жинақтауға" мүмкіндік береді, мысалы, "жинақтау(2)" дегеніміз бір байттан үлкен дерек мүшелерін екі байттық шекараға туралау, сондықтан кез келген толтыру мүшесі ең көп дегенде бір байтты құрайды. Сол сияқты, PL/I-де біт тізбектерінен басқа барлық толтыруды жою үшін құрылым UNALIGNED деп жариялануы мүмкін. Мұндай "жинақталған" құрылымдардың бір пайдасы – жадты үнемдеу. Мысалы, бір байттық (мысалы, char) және төрт байттық бүтін санды (мысалы, uint32_t) қамтитын құрылымға үш қосымша байт толтыру қажет болады. Мұндай құрылымдардың үлкен массиві жинақталған жағдайда 37,5%-ға аз жадты пайдаланады, бірақ әрбір құрылымға қол жеткізу уақыты ұзаруы мүмкін. Бұл шартты кеңістік-уақыт айырбасының бір түрі деп санауға болады. "Жинақталған" құрылымдарды пайдалану көбінесе жадты үнемдеу үшін қолданылса да, оны стандартты протокол арқылы беру үшін дерек құрылымын пішімдеу үшін де пайдалануға болады. Алайда, мұндай жағдайда құрылым мүшелерінің мәндері протокол талап ететін енділікпен (әдетте желілік байт ретімен) сақталуын қамтамасыз етуге көңіл бөлу қажет, бұл хост машинасының жергілікті енділігінен өзгеше болуы мүмкін.
Қалыптастырылған қаптама және
Кейбір Microsoft компиляторларында, әсіресе RISC процессорларында, жобаның әдепкі жинақталуы (/Zp директивасы) мен `#pragma pack` директивасы арасында күтпеген байланыс бар. `#pragma pack` директивасын құрылымның жинақталу өлшемін жобаның әдепкі жинақталуынан азайту үшін ғана қолдануға болады. Егер жобаның жинақталуы одан кіші болса, мысалы, кітапхана тақырыптарында `#pragma pack` қолданылған жағдайда, өзара іс-қимыл проблемалары туындауы мүмкін. Осы себепті, жобаның жинақталуын 8 байттан басқа кез келген мәнге орнату кітапхана тақырыптарында қолданылатын `#pragma pack` директиваларын бұзып, құрылымдар арасында екілік үйлесімсіздікке алып келеді. Бұл шектеу x86 үшін компиляцияланғанда байқалмайды.