मेम्मोव और मेम्ची के बीच अंतर क्या है?


120

बीच क्या अंतर है memmoveऔर memcpy? आप आमतौर पर किसका उपयोग करते हैं और कैसे करते हैं?


जो समस्याएं उत्पन्न हो सकती हैं उन पर ध्यान दें: lwn.net/Articles/414467
Zan Lynx

जवाबों:


162

memcpyगंतव्य के साथ , स्रोत स्रोत को ओवरलैप नहीं कर सकता है। इसके साथ memmoveकर सकते हैं। इसका मतलब यह है कि memmoveकी तुलना में थोड़ा धीमा हो सकता हैmemcpy , क्योंकि यह समान धारणा नहीं बना सकता है।

उदाहरण के लिए, memcpyहमेशा निम्न से उच्च पते को कॉपी कर सकते हैं। यदि स्रोत के बाद गंतव्य ओवरलैप होता है, तो इसका मतलब है कि कुछ पते कॉपी किए जाने से पहले ओवरराइट हो जाएंगे। memmoveइस मामले का पता लगाएगा और इस दिशा में कॉपी करेगा - इस मामले में उच्च से निम्न तक। हालाँकि, इसे जांचना और दूसरे पर स्विच करना (संभवतः कम कुशल) एल्गोरिदम में समय लगता है।


1
मेम्ची का उपयोग करते समय, मैं कैसे गारंटी दे सकता हूं कि src और भाग्य पते ओवरलैप नहीं हैं? क्या मुझे व्यक्तिगत रूप से यह सुनिश्चित करना चाहिए कि src और desta ओवरलैप नहीं है?
Alcott

6
@ अलकोट, मेमकी का उपयोग न करें यदि आप नहीं जानते हैं कि वे ओवरलैप नहीं करते हैं - इसके बजाय मेमोव का उपयोग करें। जब कोई ओवरलैप नहीं होता है, तो मेमोव और मेम्पी बराबर होते हैं (हालांकि मेम्ची बहुत, बहुत, बहुत थोड़ा तेज हो सकता है)।
bdonlan

यदि आप लंबे सरणियों के साथ काम कर रहे हैं तो आप 'प्रतिबंधित' कीवर्ड का उपयोग कर सकते हैं और अपनी प्रतिलिपि बनाने की प्रक्रिया को सुरक्षित रखना चाहते हैं। उदाहरण के लिए यदि आप विधि पैरामीटर इनपुट और आउटपुट सरणियों के रूप में लेते हैं और आपको यह सत्यापित करना होगा कि उपयोगकर्ता इनपुट और आउटपुट के समान पते को पारित नहीं करता है। यहाँ और अधिक पढ़ें stackoverflow.com/questions/776283/…
डैनियलएचएसएच

10
@DanielHsH 'प्रतिबंधित' एक वादा है जिसे आप संकलक बनाते हैं; यह संकलक द्वारा लागू नहीं किया गया है। यदि आप अपने तर्कों पर 'प्रतिबंध' लगाते हैं, और वास्तव में, ओवरलैप करते हैं (या आमतौर पर, कई स्थानों से प्राप्त सूचक से प्रतिबंधित डेटा तक पहुंचते हैं), कार्यक्रम का व्यवहार अपरिभाषित है, अजीब बग होगा, और कंपाइलर आमतौर पर आपको इसके बारे में चेतावनी नहीं देगा।
bdonlan

@bdonlan यह केवल कंपाइलर का वादा नहीं है, यह आपके कॉलर के लिए एक आवश्यकता है। यह एक ऐसी आवश्यकता है जिसे लागू नहीं किया जाता है लेकिन यदि आप किसी आवश्यकता का उल्लंघन करते हैं, तो अप्रत्याशित परिणाम मिलने पर आप शिकायत नहीं कर सकते। आवश्यकता का उल्लंघन करना अपरिभाषित व्यवहार है, ठीक उसी तरह जैसे i = i++ + 1अपरिभाषित है; संकलक आपको उस कोड को लिखने के लिए मना नहीं करता है, लेकिन उस निर्देश का परिणाम कुछ भी हो सकता है और अलग-अलग संकलक या सीपीयू यहां अलग-अलग मान दिखाएंगे।
मेक्की

33

memmoveओवरलैपिंग मेमोरी संभाल सकते हैं, memcpyनहीं।

विचार करें

char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up

जाहिर है कि स्रोत और गंतव्य अब ओवरलैप हो रहे हैं, हम "बार" के साथ "-bar" को अधिलेखित कर रहे हैं। memcpyयदि स्रोत और गंतव्य ओवरलैप करते हैं तो इस तरह के मामलों में हमें जो आवश्यकता होती है, उसका उपयोग करते हुए यह अपरिभाषित व्यवहार है memmove

memmove(&str[3],&str[4],4); //fine

5
पहले क्यों फूटेगा?

4
@ सुल्तान: क्योंकि यह MAY को निम्न स्तर की अस्मिता का उपयोग करके लागू किया गया है जिसके लिए यह आवश्यक है कि मेमोरी ओवरलैप न हो। यदि ऐसा होता है तो आप उदाहरण के लिए प्रोसेसर को सिग्नल या हार्डवेयर अपवाद उत्पन्न कर सकते हैं जो एप्लिकेशन को निरस्त करता है। दस्तावेज़ीकरण निर्दिष्ट करता है कि यह स्थिति को संभालता नहीं है, लेकिन मानक निर्दिष्ट नहीं करता है कि क्या होगा जब इन शर्तों को वायलेट किया जाता है (इसे अपरिभाषित व्यवहार के रूप में जाना जाता है)। अपरिभाषित व्यवहार कुछ भी कर सकता है।
मार्टिन

4.8.2 जीसी के साथ, यहां तक ​​कि मेमकोपी भी अतिव्यापी स्रोत और गंतव्य बिंदुओं और ठीक काम करने को स्वीकार करता है।
गीकेज

4
@jagsgediya यकीन है कि यह हो सकता है। लेकिन चूंकि मेम्ची को इसका समर्थन नहीं करने के लिए प्रलेखित किया गया है, इसलिए आपको उस कार्यान्वयन विशिष्ट व्यवहार पर भरोसा नहीं करना चाहिए, इसीलिए मेमोव () मौजूद है। यह gcc के दूसरे संस्करण में भिन्न हो सकता है। यह अलग हो सकता है अगर glccc में मेमसीपी () को कॉल करने के बजाय gcc मेम्पी को सुखा देता है, यह ग्लिबक के पुराने या नए संस्करण पर अलग हो सकता है।
nos

अभ्यास से, ऐसा लगता है कि मेमकी और मेम्मोव ने एक ही काम किया। ऐसा गहरा अपरिभाषित व्यवहार।
जीवन

22

से memcpy आदमी पेज।

मेमसीपी () फंक्शन कॉपी n बाइट्स मेमोरी एरिया src से मेमोरी एरिया डेस्ट तक होती है। स्मृति क्षेत्रों को ओवरलैप नहीं करना चाहिए। यदि मेमोरी क्षेत्र ओवरलैप करते हैं तो मेमोव (3) का उपयोग करें ।


12

के बीच मुख्य अंतर memmove()और memcpy()में वह यह है कि memmove()एक बफर अस्थायी स्मृति - - प्रयोग किया जाता है, इसलिए वहाँ ओवरलैपिंग का कोई खतरा नहीं है। दूसरी ओर, memcpy()सीधे उस स्थान से डेटा की प्रतिलिपि बनाता है जो स्रोत द्वारा इंगित किए गए स्थान पर गंतव्य द्वारा इंगित किया गया है । ( http://www.cplusplus.com/reference/cstring/memcpy/ )

निम्नलिखित उदाहरणों पर विचार करें:

  1. #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
        char string [] = "stackoverflow";
        char *first, *second;
        first = string;
        second = string;
    
        puts(string);
        memcpy(first+5, first, 5);
        puts(first);
        memmove(second+5, second, 5);
        puts(second);
        return 0;
    }

    जैसा कि आपने उम्मीद की थी, यह पता चलेगा:

    stackoverflow
    stackstacklow
    stackstacklow
  2. लेकिन इस उदाहरण में, परिणाम समान नहीं होंगे:

    #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
        char string [] = "stackoverflow";
        char *third, *fourth;
        third = string;
        fourth = string;
    
        puts(string);
        memcpy(third+5, third, 7);
        puts(third);
        memmove(fourth+5, fourth, 7);
        puts(fourth);
        return 0;
    }

    आउटपुट:

    stackoverflow
    stackstackovw
    stackstackstw

ऐसा इसलिए है क्योंकि "मेमसीपी ()" निम्नलिखित करता है:

1.  stackoverflow
2.  stacksverflow
3.  stacksterflow
4.  stackstarflow
5.  stackstacflow
6.  stackstacklow
7.  stackstacksow
8.  stackstackstw

2
लेकिन, ऐसा लगता है कि आपके द्वारा उल्लिखित आउटपुट उलट है !!
कुमार

1
जब मैं एक ही कार्यक्रम चलाता हूं, तो मुझे निम्न परिणाम मिलते हैं: स्टैकओवरफ़्लो स्टैकस्टैकस्ट स्टैकस्टैकस्टॉ // // इसका मतलब है कि मेम्पी और मेमोव के बीच आउटपुट में कोई अंतर नहीं है
कुमार

4
"यह है कि" मेमोव () "में, एक बफर - एक अस्थायी मेमोरी - का उपयोग किया जाता है;" यह सच नहीं है। यह कहता है "जैसे कि" तो इसे बस इतना व्यवहार करना है, न कि यह उस तरह से होना है। वास्तव में सबसे ज्ञापन कार्यान्वयन के रूप में प्रासंगिक थॉट्स केवल एक XOR- स्वैप करते हैं।
धेन

2
मुझे नहीं लगता कि memmove()बफर का उपयोग करने के लिए कार्यान्वयन की आवश्यकता है। यह पूरी तरह से अंदर जाने के लिए हकदार है (जब तक कि प्रत्येक पढ़ने को उसी पते पर किसी भी लिखने से पहले पूरा न हो जाए)।
टोबी स्पाइट

12

यह मानते हुए कि आपको दोनों को लागू करना होगा, कार्यान्वयन इस तरह दिख सकता है:

void memmove ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src < (uintptr_t)dst) {
        // Copy from back to front

    } else if ((uintptr_t)dst < (uintptr_t)src) {
        // Copy from front to back
    }
}

void mempy ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src != (uintptr_t)dst) {
        // Copy in any way you want
    }
}

और यह बहुत अच्छी तरह से अंतर स्पष्ट करना चाहिए। memmoveहमेशा इस तरह से कॉपी करता है, कि यह अभी भी सुरक्षित है अगर srcऔर dstओवरलैप हो, जबकि memcpyबस परवाह नहीं है क्योंकि प्रलेखन का उपयोग करते समय कहता है memcpy, दो मेमोरी क्षेत्र नहीं होना चाहिए ओवरलैप ।

उदाहरण के लिए यदि memcpyप्रतियां "फ्रंट टू बैक" और मेमोरी ब्लॉक इस तरह संरेखित हैं

[---- src ----]
            [---- dst ---]

पहले srcसे dstही पहले बाइट की प्रतिलिपि बनाने से अंतिम बाइट्स की सामग्री नष्ट हो जाती हैsrc से पहले इन कॉपी किया गया है। केवल "बैक टू फ्रंट" की नकल करने से सही परिणाम सामने आएंगे।

अब स्वैप srcऔर dst:

[---- dst ----]
            [---- src ---]

उस स्थिति में "फ्रंट टू बैक" की प्रतिलिपि बनाना केवल सुरक्षित है क्योंकि "बैक टू फ्रंट" कॉपी करना नष्ट कर देगा src कॉपी करते हुए पहले मोर्चे को कॉपी करते समय इसके फ्रंट के पास ही ।

आपने देखा होगा कि memmoveयदि वे वास्तव में ओवरलैप करते हैं, तो ऊपर दिए गए कार्यान्वयन का परीक्षण भी नहीं होता है, यह सिर्फ उनके सापेक्ष पदों की जांच करता है, लेकिन यह अकेले प्रतिलिपि को सुरक्षित बना देगा। जैसाmemcpy आमतौर पर किसी भी सिस्टम पर मेमोरी को कॉपी करने के लिए संभव सबसे तेज़ तरीके का उपयोग करता है, memmoveआमतौर पर इसके रूप में लागू किया जाता है:

void memmove ( void * dst, const void * src, size_t count ) {
    if ((uintptr_t)src < (uintptr_t)dst
        && (uintptr_t)src + count > (uintptr_t)dst
    ) {
        // Copy from back to front

    } else if ((uintptr_t)dst < (uintptr_t)src
        && (uintptr_t)dst + count > (uintptr_t)src
    ) {
        // Copy from front to back

    } else {
        // They don't overlap for sure
        memcpy(dst, src, count);
    }
}

कभी-कभी, यदि memcpyहमेशा "फ्रंट टू बैक" या "बैक टू फ्रंट" की प्रतिलिपि बनाई जाती है,memmovememcpy वह ओवरलैपिंग के मामलों में से एक में भी उपयोग कर सकता है लेकिन memcpyयह भी एक अलग तरीके से कॉपी कर सकता है कि डेटा कैसे और / या कितना डेटा के आधार पर है नकल की है, तो भी अगर तुम कैसे परीक्षण कियाmemcpy आपके सिस्टम पर प्रतियां , आप हमेशा सही होने के लिए उस परीक्षा परिणाम पर भरोसा नहीं कर सकते।

यह तय करने के लिए कि आपके लिए क्या मतलब है, जिसे कॉल करना है

  1. जब तक आप यह सुनिश्चित करने के लिए जानते हैं कि srcऔरdst ओवरलैप नहीं करते हैं, कॉल memmoveयह हमेशा की तरह सही परिणाम के लिए नेतृत्व और के रूप में है कि नकल के मामले की आवश्यकता के लिए संभव है के रूप में तेजी से आम तौर पर है जाएगा।

  2. यदि आप निश्चित रूप से जानते हैं srcऔर dstओवरलैप नहीं करते हैं, तो कॉल करें memcpyक्योंकि इससे कोई फर्क नहीं पड़ेगा कि आप किस परिणाम के लिए कॉल करते हैं, दोनों उस स्थिति में सही तरीके से काम करेंगे, लेकिन memmoveकभी भी तेजी से नहीं होगा memcpyऔर यदि आप अशुभ हैं, तो यह भी हो सकता है धीमे रहिए, जिससे आप केवल कॉलिंग जीत सकते हैं memcpy


+1 क्योंकि आपकी "एससीआई ड्रॉइंग" यह समझने के लिए उपयोगी थीं कि डेटा को भ्रष्ट किए बिना ओवरलैपिंग क्यों नहीं हो सकती है
स्टाइलर जूल

10

एक ओवरलैपिंग गंतव्यों को संभालता है दूसरा नहीं।


6

बस आईएसओ / आईईसी से: 9899 मानक यह अच्छी तरह से वर्णित है।

7.21.2.1 यादगार समारोह

[...]

2 memcpy फ़ंक्शन s1 द्वारा इंगित ऑब्जेक्ट में s2 द्वारा इंगित ऑब्जेक्ट से n वर्णों की प्रतिलिपि बनाता है। यदि ओवरलैप करने वाली वस्तुओं के बीच नकल होती है, तो व्यवहार अपरिभाषित है।

तथा

7.21.2.2 मेमोव फ़ंक्शन

[...]

2 मेमोव फ़ंक्शन s1 द्वारा इंगित ऑब्जेक्ट में s2 द्वारा इंगित ऑब्जेक्ट से n वर्णों की प्रतिलिपि बनाता है। प्रतिलिपि बनाना इस तरह से होता है जैसे कि s2 द्वारा बताई गई वस्तु के n वर्णों को पहले n वर्णों के एक अस्थायी सरणी में कॉपी किया जाता है जो s1 और s2 द्वारा इंगित वस्तुओं को ओवरलैप नहीं करता है , और फिर अस्थायी सरणी से n वर्णों की प्रतिलिपि बनाई जाती है s1 द्वारा इंगित की गई वस्तु।

आमतौर पर मैं प्रश्न के लिए कौन सी रिकॉर्डिंग का उपयोग करता हूं, इस बात पर निर्भर करता है कि मुझे किस कार्यशीलता की आवश्यकता है।

सादे पाठ में memcpy()अनुमति नहीं है s1और s2ओवरलैप करने के लिए, जबकि memmove()करता है।


0

लागू करने के दो स्पष्ट तरीके हैं mempcpy(void *dest, const void *src, size_t n)(वापसी मूल्य की अनदेखी):

  1. for (char *p=src, *q=dest;  n-->0;  ++p, ++q)
        *q=*p;
  2. char *p=src, *q=dest;
    while (n-->0)
        q[n]=p[n];

पहले कार्यान्वयन में, प्रतिलिपि निम्न से उच्च पते तक जाती है, और दूसरे में, उच्च से निम्न तक। यदि कॉपी की जाने वाली सीमा ओवरलैप हो जाती है (जैसा कि एक फ्रेमबार को स्क्रॉल करते समय मामला होता है, उदाहरण के लिए), तो ऑपरेशन की केवल एक दिशा सही है, और दूसरा उन स्थानों को अधिलेखित कर देगा जिन्हें बाद में पढ़ा जाएगा।

एक memmove()कार्यान्वयन, अपने सरलतम पर, dest<src(कुछ प्लेटफ़ॉर्म-निर्भर तरीके से) परीक्षण करेगा , और उपयुक्त दिशा का निष्पादन करेगा memcpy()

उपयोगकर्ता कोड निश्चित रूप से ऐसा नहीं कर सकता है, क्योंकि कास्टिंग srcऔर dstकुछ ठोस सूचक प्रकार के बाद भी , वे (सामान्य रूप से) एक ही वस्तु में नहीं दिखाई देते हैं और इसलिए उनकी तुलना नहीं की जा सकती है। लेकिन मानक पुस्तकालय में अपरिभाषित व्यवहार के कारण ऐसी तुलना करने के लिए पर्याप्त मंच ज्ञान हो सकता है।


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


0

मेम्मोव ओवरलैपिंग स्रोत और गंतव्य क्षेत्रों के साथ सौदा कर सकता है, जबकि मेमसीपी नहीं कर सकता। दोनों के बीच, मेम्ची बहुत अधिक कुशल है। इसलिए, यदि आप कर सकते हैं, तो USE को याद रखना बेहतर होगा।

संदर्भ: https://www.youtube.com/watch?v=Yr1YnOVG-4g डॉ। जेरी कैन, (स्टैनफोर्ड इंट्रो सिस्टम लेक्चर - 7) समय: 36:00


यह उत्तर कहता है "संभवत: एक तेजी से ठंडा" और मात्रात्मक डेटा केवल मामूली अंतर का संकेत देता है। यह उत्तर बताता है कि "बहुत अधिक कुशल" है। कितनी तेजी से आप एक कुशल मिल गया है? BTW: मुझे लगता है कि तुम मतलब है memcpy()और नहीं memcopy()
chux -

यह टिप्पणी डॉ। जेरी कैन के व्याख्यान पर आधारित है। मैं आपसे अनुरोध करूंगा कि आप उनके व्याख्यान को 36:00 बजे सुनें, केवल 2-3 मिनट पर्याप्त होंगे। और पकड़ने के लिए धन्यवाद। : डी
एहसान
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.