c - केवल मानक पुस्तकालय का उपयोग कर गठबंधन स्मृति आवंटित करने के लिए कैसे?




memory-management (12)

मैंने नौकरी साक्षात्कार के हिस्से के रूप में अभी एक परीक्षण समाप्त किया, और एक प्रश्न ने मुझे रोक दिया - यहां तक ​​कि संदर्भ के लिए Google का उपयोग भी किया। मैं देखना चाहता हूं कि स्टैक ओवरफ्लो चालक दल इसके साथ क्या कर सकता है:

"Memset_16aligned" फ़ंक्शन को 16byte गठबंधन पॉइंटर को पास करने की आवश्यकता होती है, या यह क्रैश हो जाएगी।

ए) आप 1024 बाइट्स मेमोरी आवंटित कैसे करेंगे, और इसे 16 बाइट सीमा तक संरेखित करेंगे?
बी) memset_16aligned निष्पादित करने के बाद स्मृति को मुक्त करें।

{

   void *mem;

   void *ptr;

   // answer a) here

   memset_16aligned(ptr, 0, 1024);

   // answer b) here

}

मूल उत्तर

{
    void *mem = malloc(1024+16);
    void *ptr = ((char *)mem+16) & ~ 0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

निश्चित उत्तर

{
    void *mem = malloc(1024+15);
    void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

अनुरोध के रूप में स्पष्टीकरण

पहला कदम पर्याप्त स्पेस स्पेस आवंटित करना है, बस मामले में। चूंकि स्मृति 16-बाइट गठबंधन होनी चाहिए (जिसका अर्थ है कि प्रमुख बाइट पते को 16 का बहु होना चाहिए), 16 अतिरिक्त बाइट्स जोड़ना गारंटी देता है कि हमारे पास पर्याप्त स्थान है। पहले 16 बाइट्स में कहीं, 16-बाइट गठबंधन सूचक है। (ध्यान दें कि malloc() को एक ऐसे सूचक को वापस करना है जो किसी भी उद्देश्य के लिए पर्याप्त रूप से गठबंधन किया गया हो। हालांकि, 'किसी भी' का अर्थ मुख्य रूप से मूल प्रकारों - long , double , long double , long long और पॉइंटर्स जैसी चीजों के लिए है ऑब्जेक्ट्स और पॉइंटर्स फ़ंक्शंस। जब आप ग्राफिक्स सिस्टम के साथ खेलना पसंद करते हैं, तो उन्हें अधिक विशिष्ट चीजें कर रही हैं, इसलिए उन्हें शेष सिस्टम की तुलना में अधिक कड़े संरेखण की आवश्यकता हो सकती है - इसलिए इस तरह के प्रश्न और उत्तर।)

अगला चरण शून्य पॉइंटर को एक चार सूचक में परिवर्तित करना है; जीसीसी इसके बावजूद, आपको शून्य पॉइंटर्स पर पॉइंटर अंकगणित नहीं करना चाहिए (और जीसीसी में आपको इसका दुरुपयोग करने के लिए चेतावनी विकल्प हैं)। फिर प्रारंभ सूचक में 16 जोड़ें। मान लीजिए malloc() ने आपको एक असंभव बुरी तरह गठबंधन सूचक: 0x800001 वापस कर दिया। 16 जोड़ना 0x800011 देता है। अब मैं 16-बाइट सीमा तक गोल करना चाहता हूं - इसलिए मैं अंतिम 4 बिट्स को 0 0x0F पर रीसेट करना चाहता हूं, जिसमें पिछले 4 बिट्स सेट हैं; इसलिए, ~0x0F में पिछले चार को छोड़कर सभी बिट्स सेट किए गए हैं। और यह कि 0x800011 के साथ 0x800010 देता है। आप अन्य ऑफसेट पर फिर से शुरू कर सकते हैं और एक ही अंकगणितीय काम देख सकते हैं।

अंतिम चरण, free() , आसान है: आप हमेशा, और केवल, free() लौटते free() एक मान है कि malloc() , calloc() या realloc() से एक आपके पास लौटा - कुछ और आपदा है। आपने मूल्य को पकड़ने के लिए सही ढंग से mem प्रदान किया - धन्यवाद। मुफ्त इसे जारी करता है।

अंत में, यदि आप अपने सिस्टम के malloc पैकेज के आंतरिक के बारे में जानते हैं, तो आप अनुमान लगा सकते हैं कि यह 16-बाइट गठबंधन डेटा (या यह 8-बाइट गठबंधन हो सकता है) लौटा सकता है। यदि यह 16-बाइट गठबंधन था, तो आपको मूल्यों के साथ डूबने की आवश्यकता नहीं होगी। हालांकि, यह डोडी और गैर-पोर्टेबल है - अन्य malloc पैकेजों में अलग-अलग न्यूनतम संरेखण होते हैं, और इसलिए एक चीज मानते समय यह कुछ अलग होता है जिससे कोर डंप हो जाते हैं। व्यापक सीमाओं के भीतर, यह समाधान पोर्टेबल है।

किसी और ने posix_memalign() को गठबंधन स्मृति प्राप्त करने के लिए एक और तरीका बताया है; यह हर जगह उपलब्ध नहीं है, लेकिन इसे आधार के रूप में उपयोग करके अक्सर लागू किया जा सकता है। ध्यान दें कि यह सुविधाजनक था कि संरेखण 2 की शक्ति थी; अन्य संरेखण संदेशवाहक हैं।

एक और टिप्पणी - यह कोड जांच नहीं करता है कि आवंटन सफल हुआ।

संशोधन

विंडोज प्रोग्रामर ने बताया कि आप पॉइंटर्स पर बिट मास्क ऑपरेशंस नहीं कर सकते हैं, और, वास्तव में, जीसीसी (3.4.6 और 4.3.1 परीक्षण) इस तरह शिकायत करते हैं। तो, मूल कोड का एक संशोधित संस्करण - एक मुख्य कार्यक्रम में परिवर्तित, निम्नानुसार है। मैंने 16 की बजाय केवल 15 जोड़ने की स्वतंत्रता भी ली है, जैसा कि बताया गया है। मैं uintptr_t का उपयोग कर रहा हूं क्योंकि सी 99 अधिकांश प्लेटफार्मों पर पहुंचने के लिए काफी लंबा रहा है। यदि यह printf() कथन में PRIXPTR के उपयोग के लिए नहीं था, तो #include <inttypes.h> का उपयोग करने के बजाय #include <stdint.h> लिए पर्याप्त होगा। [इस कोड में C.R. द्वारा C.R. फिक्स शामिल है, जो कई साल पहले बिल के द्वारा किए गए बिंदु को दोहरा रहा था, जिसे मैंने अभी तक अनदेखा करने में कामयाब रहे।]

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

int main(void)
{
    void *mem = malloc(1024+15);
    void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
    return(0);
}

और यहां एक मामूली रूप से अधिक सामान्यीकृत संस्करण है, जो आकारों के लिए काम करेगा जो 2 की शक्ति है:

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

static void test_mask(size_t align)
{
    uintptr_t mask = ~(uintptr_t)(align - 1);
    void *mem = malloc(1024+align-1);
    void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
    assert((align & (align - 1)) == 0);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

int main(void)
{
    test_mask(16);
    test_mask(32);
    test_mask(64);
    test_mask(128);
    return(0);
}

test_mask() को सामान्य उद्देश्य आवंटन फ़ंक्शन में कनवर्ट करने के लिए, test_mask() से एकल रिटर्न मान को रिलीज़ पता एन्कोड करना होगा, क्योंकि कई लोगों ने अपने उत्तरों में संकेत दिया है।

साक्षात्कारकर्ताओं के साथ समस्याएं

Uri ने टिप्पणी की: शायद मुझे आज सुबह [ए] पढ़ने की समझ की समस्या हो रही है, लेकिन यदि साक्षात्कार प्रश्न विशेष रूप से कहता है: "आप स्मृति के 1024 बाइट आवंटित कैसे करेंगे" और आप स्पष्ट रूप से उससे अधिक आवंटित करते हैं। साक्षात्कारकर्ता से स्वचालित विफलता नहीं होगी?

मेरी प्रतिक्रिया 300-वर्ण टिप्पणी में फिट नहीं होगी ...

यह निर्भर करता है, मुझे लगता है। मुझे लगता है कि ज्यादातर लोगों (मेरे साथ) ने सवाल उठाया है कि "आप एक ऐसी जगह कैसे आवंटित करेंगे जिसमें 1024 बाइट डेटा संग्रहीत किया जा सके, और जहां आधार पता 16 बाइट्स का एक बहु है"। यदि साक्षात्कारकर्ता वास्तव में मतलब है कि आप 1024 बाइट्स (केवल) आवंटित कैसे कर सकते हैं और इसे 16-बाइट गठबंधन किया गया है, तो विकल्प अधिक सीमित हैं।

  • जाहिर है, एक संभावना 1024 बाइट आवंटित करना है और फिर उस पते को 'संरेखण उपचार' देना है; उस दृष्टिकोण के साथ समस्या यह है कि वास्तविक उपलब्ध स्थान उचित रूप से निर्धारित नहीं होता है (उपयोग योग्य स्थान 1008 और 1024 बाइट्स के बीच है, लेकिन कौन सा आकार निर्दिष्ट करने के लिए कोई तंत्र उपलब्ध नहीं था), जो इसे उपयोगी से कम प्रदान करता है।
  • एक और संभावना यह है कि आपको एक पूर्ण मेमोरी आवंटक लिखने की उम्मीद है और यह सुनिश्चित करें कि आपके द्वारा लौटाए गए 1024-बाइट ब्लॉक उचित रूप से गठबंधन हैं। यदि ऐसा है, तो संभवतः प्रस्तावित समाधान के समान ही आप एक ऑपरेशन कर रहे हैं, लेकिन आप इसे आवंटक के अंदर छिपाते हैं।

हालांकि, यदि साक्षात्कारकर्ता ने उन प्रतिक्रियाओं में से किसी एक की अपेक्षा की, तो मैं उनसे यह मानने की उम्मीद करता हूं कि यह समाधान निकट से संबंधित प्रश्न का उत्तर देगा, और उसके बाद वार्तालाप को सही दिशा में इंगित करने के लिए अपने प्रश्न को ठंडा करने के लिए। (इसके अलावा, अगर साक्षात्कारकर्ता वास्तव में बेवकूफ हो गया, तो मैं नौकरी नहीं चाहता; अगर अपर्याप्त सटीक आवश्यकता का उत्तर बिना सुधार किए आग में गोली मार दी जाती है, तो साक्षात्कारकर्ता वह व्यक्ति नहीं है जिसके लिए यह काम करने के लिए सुरक्षित है।)

दुनिया आगे बढ़ती है

प्रश्न का शीर्षक हाल ही में बदल गया है। यह सी साक्षात्कार के सवाल में स्मृति संरेखण को हल किया गया था जो मुझे रोक दिया । संशोधित शीर्षक ( केवल मानक लाइब्रेरी का उपयोग करके गठबंधन स्मृति आवंटित कैसे करें? ) थोड़ा संशोधित उत्तर मांगता है - यह परिशिष्ट इसे प्रदान करता है।

सी 11 (आईएसओ / आईईसी aligned_alloc() : 2011) जोड़ा गया कार्य aligned_alloc() :

7.22.3.1 aligned_alloc फ़ंक्शन

सार

#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);

विवरण
aligned_alloc फ़ंक्शन उस ऑब्जेक्ट के लिए स्थान आवंटित करता है जिसका संरेखण संरेखण द्वारा निर्दिष्ट किया गया alignment , जिसका आकार आकार द्वारा निर्दिष्ट किया गया size , और जिसका मान अनिश्चित है। alignment का मान कार्यान्वयन द्वारा समर्थित वैध संरेखण होगा और size का मान alignment का एक अभिन्न अंग होगा।

रिटर्न
aligned_alloc फ़ंक्शन आवंटित स्थान पर या तो शून्य सूचक या पॉइंटर देता है।

और POSIX posix_memalign() परिभाषित करता है posix_memalign() :

#include <stdlib.h>

int posix_memalign(void **memptr, size_t alignment, size_t size);

विवरण

posix_memalign() फ़ंक्शन alignment द्वारा निर्दिष्ट सीमा पर गठबंधन size बाइट आवंटित करेगा, और memptr में आवंटित स्मृति में एक सूचक वापस करेगा। alignment का मूल्य sizeof(void *) के दो एकाधिक sizeof(void *) की शक्ति होगी।

सफल समापन पर, memptr द्वारा इंगित मूल्य alignment का एक बहु होगा।

यदि अनुरोध की गई जगह का आकार 0 है, तो व्यवहार कार्यान्वयन-परिभाषित है; memptr में लौटाया मूल्य या तो एक शून्य सूचक या एक अद्वितीय सूचक होगा।

free() फ़ंक्शन स्मृति को posix_memalign() देगा जो पहले posix_memalign() द्वारा आवंटित किया गया था।

प्रतिलाभ की मात्रा

सफल समापन पर, posix_memalign() शून्य वापस आ जाएगा; अन्यथा, त्रुटि इंगित करने के लिए एक त्रुटि संख्या वापस कर दी जाएगी।

इनमें से किसी एक या दोनों का अब प्रश्न का उत्तर देने के लिए उपयोग किया जा सकता है, लेकिन जब प्रश्न मूल रूप से उत्तर दिया गया था तो केवल POSIX फ़ंक्शन एक विकल्प था।

दृश्यों के पीछे, नया गठबंधन मेमोरी फ़ंक्शन एक ही काम करता है जैसा कि प्रश्न में उल्लिखित है, सिवाय इसके कि उनके पास संरेखण को अधिक आसानी से मजबूर करने की क्षमता है, और आंतरिक रूप से गठबंधन स्मृति की शुरुआत का ट्रैक रखें ताकि कोड न हो विशेष रूप से निपटना होगा - यह केवल आवंटन समारोह द्वारा लौटाई गई स्मृति को मुक्त करता है।


'राउंड अप' भाग के लिए एक वैकल्पिक दृष्टिकोण यहां दिया गया है। सबसे शानदार कोड कोड नहीं है लेकिन यह काम पूरा हो जाता है, और इस प्रकार का वाक्यविन्यास याद रखना थोड़ा आसान है (प्लस संरेखण मानों के लिए काम करेगा जो 2 की शक्ति नहीं हैं)। कंपाइलर को खुश करने के लिए uintptr_t कास्ट आवश्यक था; सूचक अंकगणित विभाजन या गुणा का बहुत शौक नहीं है।

void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);


आप इस सवाल को कैसे देखते हैं इसके आधार पर तीन थोड़ा अलग उत्तर:

1) पूछे गए सटीक प्रश्न के लिए काफी अच्छा है जोनाथन लेफ्लर का समाधान है, सिवाय इसके कि 16-गठबंधन तक पहुंचने के लिए, आपको केवल 15 अतिरिक्त बाइट्स चाहिए, 16 नहीं।

ए:

/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;

बी:

free(mem);

2) एक और सामान्य मेमोरी आवंटन समारोह के लिए, कॉलर दो पॉइंटर्स (एक का उपयोग करने के लिए और एक से मुक्त) का ट्रैक रखना नहीं चाहता है। तो आप गठबंधन बफर के नीचे 'असली' बफर में एक पॉइंटर स्टोर करते हैं।

ए:

void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;

बी:

if (ptr) free(((void**)ptr)[-1]);

ध्यान दें कि (1) के विपरीत, जहां केवल 15 बाइट्स को याद में जोड़ा गया था, यह कोड वास्तव में संरेखण को कम कर सकता है यदि आपका कार्यान्वयन malloc से 32-बाइट संरेखण की गारंटी देता है (संभावना नहीं है, लेकिन सिद्धांत में सी कार्यान्वयन में 32-बाइट हो सकता है गठबंधन प्रकार)। इससे कोई फर्क नहीं पड़ता कि आप जो कुछ करते हैं उसे memset_16aligned कहते हैं, लेकिन यदि आप किसी संरचना के लिए स्मृति का उपयोग करते हैं तो इससे कोई फर्क नहीं पड़ता।

मुझे यकीन नहीं है कि इसके लिए क्या अच्छा फिक्स है (उपयोगकर्ता को यह चेतावनी देने के अलावा कि बफर वापस लौटाया गया है, अनिवार्य रूप से मनमानी structs के लिए उपयुक्त नहीं है) क्योंकि कार्यान्वयन-विशिष्ट संरेखण गारंटी क्या प्रोग्रामेटिक रूप से निर्धारित करने का कोई तरीका नहीं है। मुझे लगता है कि स्टार्टअप पर आप दो या दो से अधिक 1-बाइट बफर आवंटित कर सकते हैं, और मान लें कि आपके द्वारा देखे जाने वाले सबसे खराब संरेखण की गारंटीकृत संरेखण है। यदि आप गलत हैं, तो आप स्मृति बर्बाद कर देते हैं। कोई भी बेहतर विचार वाला कोई भी, कृपया ऐसा कहें ...

[ जोड़ा गया : 'मानक' चाल आवश्यक संरेखण निर्धारित करने के लिए 'अधिकतम रूप से गठबंधन प्रकार होने की संभावना' का एक संघ बनाना है। अधिकतम गठबंधन प्रकार (सी 99 में) ' long long ', ' long double ', ' void * ', या ' void (*)(void) ' होने की संभावना है; यदि आप <stdint.h> शामिल करते हैं, तो संभवतः आप long long ' intmax_t ' का उपयोग कर long long (और, पावर 6 ( intmax_t ) मशीनों पर, intmax_t आपको 128-बिट पूर्णांक प्रकार देगा)। उस संघ के लिए संरेखण आवश्यकताओं को एक एकल के साथ एक संरचना में एम्बेड करके संघ द्वारा अनुसरण किया जा सकता है:

struct alignment
{
    char     c;
    union
    {
        intmax_t      imax;
        long double   ldbl;
        void         *vptr;
        void        (*fptr)(void);
    }        u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;

फिर आप अनुरोधित संरेखण के बड़े (उदाहरण में, 16) और उपरोक्त गणना align मान का उपयोग करेंगे।

ऑन (64-बिट) सोलारिस 10, ऐसा प्रतीत होता है कि malloc() से परिणाम के लिए मूल संरेखण 32 बाइट्स का एक बहु है।
]

प्रैक्टिस में, गठबंधन आवंटक अक्सर हार्डवार्ड होने के बजाए संरेखण के लिए पैरामीटर लेते हैं। तो उपयोगकर्ता उस संरचना के आकार में गुजर जाएगा जिसकी वे परवाह करते हैं (या कम से कम 2 की उससे कम शक्ति या उसके बराबर) और सभी ठीक होंगे।

3) अपने प्लेटफ़ॉर्म द्वारा प्रदान किए जाने वाले उपयोग का उपयोग करें: posix_memalign लिए _aligned_malloc , _aligned_malloc पर _aligned_malloc

4) यदि आप सी 11 का उपयोग करते हैं, तो सबसे साफ पोर्टेबल और संक्षिप्त विकल्प मानक लाइब्रेरी फ़ंक्शन aligned_alloc का उपयोग करना है जिसे भाषा विनिर्देश के इस संस्करण में पेश किया गया था।


इस सवाल को पढ़ने पर पहली चीज जो मेरे सिर में चली गई थी, एक गठबंधन संरचना को परिभाषित करना था, इसे तुरंत चालू करना था, और फिर इसे इंगित करना था।

क्या कोई मौलिक कारण है जो मुझे याद नहीं है क्योंकि किसी और ने इसका सुझाव नहीं दिया है?

एक sidenote के रूप में, क्योंकि मैंने चार की एक सरणी का उपयोग किया (मानते हैं कि सिस्टम का चार 8 बिट्स (यानी 1 बाइट) है), मुझे विशेषता ((पैक) की आवश्यकता नहीं है) जरूरी है (अगर मैं गलत हूं तो मुझे सही करें ), लेकिन मैं इसे किसी भी तरह से डाल दिया।

यह दो प्रणालियों पर काम करता है जिस पर मैंने कोशिश की, लेकिन यह संभव है कि एक कंपाइलर अनुकूलन है कि मुझे कोड की प्रभावकारिता के साथ मुझे झूठी सकारात्मक जानकारी देने से अनजान है। मैंने ओएसएक्स पर जीसीसी 4.9.2 और उबंटू पर जीसीसी 5.2.1 का इस्तेमाल किया।

#include <stdio.h>
#include <stdlib.h>

int main ()
{

   void *mem;

   void *ptr;

   // answer a) here
   struct __attribute__((packed)) s_CozyMem {
       char acSpace[16];
   };

   mem = malloc(sizeof(struct s_CozyMem));
   ptr = mem;

   // memset_16aligned(ptr, 0, 1024);

   // Check if it's aligned
   if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
   else printf("Rubbish.\n");

   // answer b) here
   free(mem);

   return 1;
}

दुर्भाग्यवश, सी 99 में किसी भी तरह के संरेखण की गारंटी देना बहुत मुश्किल लगता है जो सी 99 के अनुरूप किसी भी सी कार्यान्वयन में पोर्टेबल होगा। क्यूं कर? चूंकि एक पॉइंटर को "बाइट एड्रेस" होने की गारंटी नहीं है, तो एक फ्लैट मेमोरी मॉडल के साथ कल्पना कर सकता है। न तो uintptr_t का प्रतिनिधित्व इतना गारंटी है, जो स्वयं भी एक वैकल्पिक प्रकार है।

हम कुछ कार्यान्वयनों के बारे में जानते हैं जो शून्य * (और परिभाषा के अनुसार, char * ) के लिए एक प्रतिनिधित्व का उपयोग करते हैं जो कि एक साधारण बाइट पता है, लेकिन सी 99 द्वारा यह हमारे लिए अपर्याप्त है, प्रोग्रामर। एक कार्यान्वयन एक सेट { सेगमेंट , ऑफ़सेट } द्वारा पॉइंटर का प्रतिनिधित्व कर सकता है, जहां ऑफसेट में कौन-सी जानता है-वास्तविकता में "संरेखण" क्या है। क्यों, एक सूचक भी हैश टेबल लुकअप मान, या यहां तक ​​कि एक लिंक-सूची लुकअप मान का कुछ रूप भी हो सकता है। यह सीमाओं की जानकारी को एन्कोड कर सकता है।

सी मानक के लिए हाल ही में सी 1 एक्स ड्राफ्ट में, हम _Aignignas कीवर्ड देखते हैं। इससे थोड़ा सा मदद मिल सकती है।

एकमात्र गारंटी सी 99 हमें देता है कि स्मृति आवंटन फ़ंक्शंस किसी ऑब्जेक्ट प्रकार पर इंगित करने वाले पॉइंटर को असाइनमेंट के लिए उपयुक्त सूचक को वापस कर देगा। चूंकि हम वस्तुओं के संरेखण को निर्दिष्ट नहीं कर सकते हैं, इसलिए हम अपने आवंटन कार्यों को एक अच्छी तरह से परिभाषित, पोर्टेबल तरीके से संरेखण की ज़िम्मेदारी के साथ लागू नहीं कर सकते हैं।

इस दावे के बारे में गलत होना अच्छा होगा।


मुझे आश्चर्य है कि किसी ने के share वोट नहीं दिया है, जैसा कि मैं इसे समझता हूं, मानक सी 99 में जो कुछ पूछा जाता है, वह करना असंभव है, क्योंकि एक सूचक को औपचारिक रूप से एक अभिन्न प्रकार में परिवर्तित करना अनिर्धारित व्यवहार है। ( uintptr_t <-> void* के मानक अनुमति के मानक के अलावा, लेकिन मानक uintptr_t मान के किसी भी uintptr_t को करने की अनुमति नहीं देता है और फिर इसे वापस परिवर्तित कर देता है।)


मैकोज़ एक्स विशिष्ट:

  1. मॉलोक के साथ आवंटित सभी पॉइंटर्स 16 बाइट गठबंधन होते हैं।
  2. सी 11 समर्थित है, इसलिए आप केवल aligned_malloc (16, आकार) को कॉल कर सकते हैं।

  3. मैकोज़ एक्स उस कोड को चुनता है जो मेमसेट, मेम्पी और मेममोव के लिए बूट समय पर अलग-अलग प्रोसेसर के लिए अनुकूलित किया जाता है और वह कोड उन चालों का उपयोग करता है जिन्हें आपने कभी तेज़ बनाने के लिए कभी नहीं सुना है। 99% मौका है कि स्मृति किसी भी हाथ से लिखित मेमसेट 16 की तुलना में तेज़ी से चलता है जो पूरे प्रश्न को व्यर्थ बनाता है।

यदि आप 100% पोर्टेबल समाधान चाहते हैं, तो सी 11 से पहले कोई भी नहीं है। क्योंकि सूचक के संरेखण का परीक्षण करने के लिए कोई पोर्टेबल तरीका नहीं है। यदि यह 100% पोर्टेबल नहीं है, तो आप इसका उपयोग कर सकते हैं

char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;

यह मानता है कि पॉइंटर को संरेखित int में कनवर्ट करते समय पॉइंटर का संरेखण निम्नतम बिट्स में संग्रहीत होता है। हस्ताक्षरित int में कनवर्ट करना जानकारी खो देता है और कार्यान्वयन परिभाषित किया जाता है, लेकिन इससे कोई फर्क नहीं पड़ता क्योंकि हम परिणाम को वापस सूचक में परिवर्तित नहीं करते हैं।

भयानक हिस्सा निश्चित रूप से है कि मूल सूचक को कहीं भी मुफ्त () कॉल करने के लिए सहेजा जाना चाहिए। तो सब कुछ मैं वास्तव में इस डिजाइन के ज्ञान पर संदेह होगा।


शायद वे memalign ज्ञान से संतुष्ट हो गए memalign ? और जोनाथन लेफ्लर बताते हैं, इसके बारे में जानने के लिए दो नए बेहतर कार्य हैं।

ओह, फ्लोरिन ने मुझे मार दिया। हालांकि, अगर आप उस मैन पेज को पढ़ते हैं जिसे मैंने लिंक किया है, तो आप पहले पोस्टर द्वारा प्रदान किए गए उदाहरण को समझ सकते हैं।


हम इस तरह की चीज एक्सेलेरेट.फ्रेमवर्क के लिए हर समय करते हैं, एक भारी वेक्टरीकृत ओएस एक्स / आईओएस लाइब्रेरी, जहां हमें हर समय संरेखण पर ध्यान देना होता है। वहां कुछ विकल्प हैं, जिनमें से एक या दो जिनमें से ऊपर वर्णित नहीं देखा गया है।

इस तरह की एक छोटी सी सरणी के लिए सबसे तेज़ तरीका सिर्फ स्टैक पर चिपका हुआ है। जीसीसी / क्लैंग के साथ:

 void my_func( void )
 {
     uint8_t array[1024] __attribute__ ((aligned(16)));
     ...
 }

कोई मुफ्त () आवश्यक है। यह आम तौर पर दो निर्देश होते हैं: स्टैक पॉइंटर से 1024 घटाएं, फिर और हस्ताक्षर के साथ स्टैक पॉइंटर। संभावित रूप से अनुरोधकर्ता को ढेर पर डेटा की आवश्यकता होती है क्योंकि सरणी का जीवनकाल स्टैक या रिकर्सन से अधिक होता है या स्टैक स्पेस एक गंभीर प्रीमियम पर होता है।

ओएस एक्स / आईओएस पर सभी मॉलोक / कॉलोक / आदि को कॉल करते हैं। हमेशा 16 बाइट गठबंधन होते हैं। यदि आपको AVX के लिए 32 बाइट गठबंधन की आवश्यकता है, उदाहरण के लिए, तो आप posix_memalign का उपयोग कर सकते हैं:

void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
   RunInCirclesWaivingArmsWildly();
...
free(buf);

कुछ लोगों ने सी ++ इंटरफेस का उल्लेख किया है जो समान रूप से काम करता है।

यह नहीं भूलना चाहिए कि पृष्ठों को दो की बड़ी शक्तियों के साथ गठबंधन किया गया है, इसलिए पृष्ठ-संरेखित बफर भी 16 बाइट गठबंधन हैं। इस प्रकार, mmap () और valloc () और अन्य समान इंटरफेस भी विकल्प हैं। mmap () का लाभ यह है कि यदि आप चाहते हैं तो बफर को गैर-शून्य के साथ पूर्वनिर्धारित आवंटित किया जा सकता है। चूंकि इनमें पृष्ठ गठबंधन आकार है, इसलिए आपको इनसे न्यूनतम आवंटन नहीं मिलेगा, और यह पहली बार जब आप इसे स्पर्श करेंगे तो यह एक वीएम गलती के अधीन होगा।

चीसी: गार्ड मॉलोक या इसी तरह चालू करें। ऐसे बफर जो आकार में एन * 16 बाइट हैं, जैसे एन * 16 बाइट गठबंधन होंगे, क्योंकि वीएम का उपयोग ओवररन्स को पकड़ने के लिए किया जाता है और इसकी सीमाएं पृष्ठ सीमाओं पर होती हैं।

कुछ त्वरण। फ्रेमवर्क फ़ंक्शंस उपयोगकर्ता द्वारा प्रदान किए गए टेम्पल बफर में स्क्रैच स्पेस के रूप में उपयोग करने के लिए लेते हैं। यहां हमें यह मानना ​​है कि हमारे पास पारित बफर को जंगली तरीके से गलत तरीके से गलत तरीके से ग़लत किया गया है और उपयोगकर्ता सक्रिय रूप से अपने जीवन को मुश्किल से बाहर करने की कोशिश कर रहा है। (हमारे टेस्ट केस स्पैम को रेखांकित करने के लिए अस्थायी बफर से पहले और बाद में एक गार्ड पेज चिपकते हैं।) यहां, हम उस न्यूनतम आकार को वापस लौटते हैं जिसे हमें 16-बाइट गठबंधन सेगमेंट में कहीं भी गारंटी देने की आवश्यकता होती है, और उसके बाद मैन्युअल रूप से बफर को संरेखित करें। यह आकार वांछित_साइज + संरेखण है - 1. इसलिए, इस मामले में 1024 + 16 - 1 = 1039 बाइट्स है। फिर इस तरह संरेखित करें:

#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
    uint8_t *alignedBuf = (uint8_t*) 
                          (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) 
                                        & -((uintptr_t) alignment));
    ...
}

संरेखण -1 जोड़ना पॉइंटर को पहले गठबंधन पते से पहले ले जाएगा और फिर एंडलाइन के साथ एंडिंग (उदाहरण के लिए 0xfff ... संरेखण के लिए ff0 = 16) इसे वापस गठबंधन पते पर लाता है।

जैसा कि अन्य पदों द्वारा वर्णित है, 16-बाइट संरेखण गारंटी के बिना अन्य ऑपरेटिंग सिस्टम पर, आप बड़े आकार के साथ मॉलोक को कॉल कर सकते हैं, बाद में पॉइंटर को मुफ्त में सेट कर सकते हैं, फिर ऊपर वर्णित अनुसार संरेखित करें और गठबंधन पॉइंटर का उपयोग करें, जितना अधिक हमारे अस्थायी बफर मामले के लिए वर्णित है।

Aligned_memset के लिए, यह बल्कि मूर्ख है। आपको एक गठबंधन पते तक पहुंचने के लिए केवल 15 बाइट्स तक लूप करना होगा, और इसके बाद अंत में कुछ संभावित क्लीनअप कोड के साथ गठबंधन स्टोर के साथ आगे बढ़ना होगा। आप वेक्टर कोड में क्लीनअप बिट्स भी कर सकते हैं, या तो असाइन किए गए स्टोर्स के रूप में जो गठबंधन क्षेत्र को ओवरलैप करते हैं (लंबाई प्रदान करना कम से कम वेक्टर की लंबाई है) या movmaskdqu जैसे कुछ का उपयोग करना। कोई सिर्फ आलसी है। हालांकि, यह शायद एक उचित साक्षात्कार प्रश्न है यदि साक्षात्कारकर्ता यह जानना चाहता है कि आप stdint.h, bitwise ऑपरेटरों और मेमोरी बुनियादी सिद्धांतों के साथ सहज हैं, तो प्रदूषित उदाहरण को क्षमा किया जा सकता है।


If there are constraints that, you cannot waste a single byte, then this solution works: Note: There is a case where this may be executed infinitely :D

   void *mem;  
   void *ptr;
try:
   mem =  malloc(1024);  
   if (mem % 16 != 0) {  
       free(mem);  
       goto try;
   }  
   ptr = mem;  
   memset_16aligned(ptr, 0, 1024);

long add;   
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);




memory-management