memcpy () बनाम मेम्मोव ()


157

मैं memcpy()और के बीच के अंतर को समझने की कोशिश कर रहा हूं memmove(), और मैंने उस पाठ को पढ़ा memcpy()है जो ओवरलैपिंग स्रोत और गंतव्य का ध्यान नहीं रखता memmove()है।

हालाँकि, जब मैं इन दो कार्यों को ओवरलैपिंग मेमोरी ब्लॉक पर निष्पादित करता हूं, तो वे दोनों एक ही परिणाम देते हैं। उदाहरण के लिए, memmove()मदद पृष्ठ पर निम्नलिखित MSDN उदाहरण लें : -

क्या कमियां समझने का एक बेहतर उदाहरण है memcpyऔर memmoveयह कैसे हल करता है?

// crt_memcpy.c
// Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle
// it correctly.

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[7] = "aabbcc";

int main( void )
{
    printf( "The string: %s\n", str1 );
    memcpy( str1 + 2, str1, 4 );
    printf( "New string: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "aabbcc" );   // reset string

    printf( "The string: %s\n", str1 );
    memmove( str1 + 2, str1, 4 );
    printf( "New string: %s\n", str1 );
}

आउटपुट:

The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb

1
Microsoft CRT में काफी समय से सुरक्षित मेमसीपी () है।
हंस पसंत

32
मुझे नहीं लगता कि "सुरक्षित" इसके लिए सही शब्द है। एक सुरक्षित memcpyयह है assertकि क्षेत्र जानबूझकर आपके कोड में बग को कवर करने के बजाय ओवरलैप नहीं करते हैं।
आर .. गिटहब स्टॉप हेल्पिंग ICE

6
इस बात पर निर्भर करता है कि आप "डेवलपर के लिए सुरक्षित" या "एंड-यूज़र के लिए सुरक्षित" हैं। मैं तर्क देता हूं कि जैसा कहा गया है, भले ही यह मानकों के अनुरूप न हो, अंत उपयोगकर्ता के लिए सुरक्षित विकल्प है।
कुसमा

चूँकि glibc 2.19 - काम नहीं The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
askovpen 20

आप यहां भी देख सकते हैं ।
रेन

जवाबों:


124

मैं पूरी तरह से आश्चर्यचकित नहीं हूं कि आपका उदाहरण कोई अजीब व्यवहार नहीं दिखाता है। इसके बजाय कॉपी str1करने की कोशिश करें str1+2और देखें कि तब क्या होता है। (वास्तव में कोई फर्क नहीं पड़ता, संकलक / पुस्तकालयों पर निर्भर करता है।)

सामान्य तौर पर, मेमसीपी को सरल (लेकिन तेज) तरीके से लागू किया जाता है। सरलीकृत रूप से, यह केवल डेटा पर (क्रम में) लूप करता है, एक स्थान से दूसरे स्थान पर प्रतिलिपि बनाता है। इसके परिणामस्वरूप स्रोत को अधिलेखित किया जा सकता है जबकि इसे पढ़ा जा रहा है।

मेम्मोव यह सुनिश्चित करने के लिए अधिक काम करता है कि यह ओवरलैप को सही ढंग से संभालता है।

संपादित करें:

(दुर्भाग्य से, मैं सभ्य उदाहरण नहीं ढूँढ सकता, लेकिन ये करेंगे)। कंट्रास्ट memcpy और memmove यहाँ दिखाया गया है कार्यान्वयन। मेम्कॉपी सिर्फ लूप्स, जबकि मेम्मोव डेटा को भ्रष्ट करने से बचने के लिए लूप को किस दिशा में निर्धारित करने के लिए एक परीक्षण करता है। ये कार्यान्वयन बल्कि सरल हैं। अधिकांश उच्च-प्रदर्शन कार्यान्वयन अधिक जटिल होते हैं (बाइट्स के बजाय एक समय में शब्द-आकार ब्लॉक को कॉपी करना)।


2
+1 इसके अलावा, निम्नलिखित कार्यान्वयन में, memmovememcpy
बिंदुओं के

वह बहुत अच्छा लगता है। विज़ुअल स्टूडियो की तरह लगता है कि एक "सुरक्षित" मेमसीपी लागू होता है (जीसीसी 4.1.1 के साथ, मैंने आरएचईएल 5 पर भी परीक्षण किया है)। Clc-wiki.net से इन फ़ंक्शन के संस्करण लिखना एक स्पष्ट तस्वीर देता है। धन्यवाद।
user534785

3
मेमेकपी ओवरलैपिंग-इश्यू का ध्यान नहीं रखते हैं, लेकिन मेमोव करता है। फिर मेमबरी को लिबास से दूर क्यों नहीं किया जाता?
अल्कोट

37
@ अलकोट: क्योंकि memcpyतेज हो सकता है।
बिली ओनेल

उपरोक्त पास्कल कूक से फिक्स्ड / वेबरेटिव लिंक: web.archive.org/web/20130722203254/http://…
JWCS

94

स्मृति ओवरलैप memcpy नहीं हो सकती है या आप अपरिभाषित व्यवहार का जोखिम उठा memmoveसकते हैं , जबकि स्मृति ओवरलैप कर सकती है।

char a[16];
char b[16];

memcpy(a,b,16);           // valid
memmove(a,b,16);          // Also valid, but slower than memcpy.
memcpy(&a[0], &a[1],10);  // Not valid since it overlaps.
memmove(&a[0], &a[1],10); // valid. 

मेमकोपी के कुछ कार्यान्वयन अभी भी अतिव्यापी आदानों के लिए काम कर सकते हैं, लेकिन आप उस व्यवहार की गिनती नहीं कर सकते। जबकि मेम्मोव को ओवरलैपिंग की अनुमति देनी चाहिए।


3
यह वास्तव में मुझे मदद की thaks! आपकी जानकारी के लिए +1
मुथु गणपति नाथन

33

सिर्फ इसलिए memcpyकि अतिव्यापी क्षेत्रों से निपटने के लिए नहीं है, इसका मतलब यह नहीं है कि यह उनके साथ सही ढंग से नहीं निपटता है। अतिव्यापी क्षेत्रों के साथ कॉल अपरिभाषित व्यवहार पैदा करता है। अपरिभाषित व्यवहार पूरी तरह से काम कर सकता है जैसा कि आप एक मंच पर उम्मीद करते हैं; इसका मतलब यह नहीं है कि यह सही है या वैध है।


10
विशेष रूप से, प्लेटफ़ॉर्म के आधार पर, यह संभव है कि memcpyठीक उसी तरह लागू किया जाए जैसे memmove। यही है, जिसने भी कंपाइलर लिखा है वह एक अद्वितीय memcpyफ़ंक्शन लिखने से परेशान नहीं है।
कैम

19

मेमकी और मेमोव दोनों समान काम करते हैं।

लेकिन एक अंतर देखने के लिए:

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[7] = "abcdef";

int main()
{

   printf( "The string: %s\n", str1 );
   memcpy( (str1+6), str1, 10 );
   printf( "New string: %s\n", str1 );

   strcpy_s( str1, sizeof(str1), "aabbcc" );   // reset string


   printf("\nstr1: %s\n", str1);
   printf( "The string: %s\n", str1 );
   memmove( (str1+6), str1, 10 );
   printf( "New string: %s\n", str1 );

}

देता है:

The string: abcdef
New string: abcdefabcdefabcd
The string: abcdef
New string: abcdefabcdef

IMHO, इस उदाहरण कार्यक्रम में कुछ खामियां हैं, चूंकि str1 बफर सीमा से बाहर पहुँचा जाता है (कॉपी करने के लिए 10 बाइट्स, बफर आकार में 7 बाइट्स है)। सीमा त्रुटि के कारण अपरिभाषित व्यवहार होता है। मेम्पी () / मेमोव () कॉल के दिखाए गए परिणामों में अंतर कार्यान्वयन विशिष्ट हैं। और उदाहरण आउटपुट बिल्कुल ऊपर दिए गए प्रोग्राम से मेल नहीं खाता है ... इसके अलावा, strcpy_s () मानक C AFAIK (MS विशिष्ट) का हिस्सा नहीं है, यह भी देखें: stackoverflow.com/questions/36723946/… ) - कृपया मुझे सही करें : I 'मैं गलत हूं।
rel

7

"खराब" संकलक की वजह से आपके डेमो ने यादगार कमियों को उजागर नहीं किया, यह आपको डीबग संस्करण में एक एहसान करता है। एक रिलीज़ संस्करण, हालांकि, आपको एक ही आउटपुट देता है, लेकिन अनुकूलन के कारण।

    memcpy(str1 + 2, str1, 4);
00241013  mov         eax,dword ptr [str1 (243018h)]  // load 4 bytes from source string
    printf("New string: %s\n", str1);
00241018  push        offset str1 (243018h) 
0024101D  push        offset string "New string: %s\n" (242104h) 
00241022  mov         dword ptr [str1+2 (24301Ah)],eax  // put 4 bytes to destination
00241027  call        esi  

यहां रजिस्टर %eaxएक अस्थायी भंडारण के रूप में खेलता है, जो "सुरुचिपूर्ण ढंग से" ओवरलैप मुद्दे को ठीक करता है।

6 बाइट्स की नकल करते समय कमियां निकलती हैं, अच्छी तरह से, कम से कम इसका हिस्सा।

char str1[9] = "aabbccdd";

int main( void )
{
    printf("The string: %s\n", str1);
    memcpy(str1 + 2, str1, 6);
    printf("New string: %s\n", str1);

    strcpy_s(str1, sizeof(str1), "aabbccdd");   // reset string

    printf("The string: %s\n", str1);
    memmove(str1 + 2, str1, 6);
    printf("New string: %s\n", str1);
}

आउटपुट:

The string: aabbccdd
New string: aaaabbbb
The string: aabbccdd
New string: aaaabbcc

अजीब लग रहा है, यह अनुकूलन के कारण भी है।

    memcpy(str1 + 2, str1, 6);
00341013  mov         eax,dword ptr [str1 (343018h)] 
00341018  mov         dword ptr [str1+2 (34301Ah)],eax // put 4 bytes to destination, earlier than the above example
0034101D  mov         cx,word ptr [str1+4 (34301Ch)]  // HA, new register! Holding a word, which is exactly the left 2 bytes (after 4 bytes loaded to %eax)
    printf("New string: %s\n", str1);
00341024  push        offset str1 (343018h) 
00341029  push        offset string "New string: %s\n" (342104h) 
0034102E  mov         word ptr [str1+6 (34301Eh)],cx  // Again, pulling the stored word back from the new register
00341035  call        esi  

यही कारण है कि मैं हमेशा memmove2 अतिव्यापी मेमोरी ब्लॉकों को कॉपी करने का प्रयास करता हूं ।


3

के बीच का अंतर है memcpyऔर memmoveवह है

  1. में memmove, निर्दिष्ट आकार की स्रोत मेमोरी को बफर में कॉपी किया जाता है और फिर गंतव्य पर ले जाया जाता है। इसलिए यदि स्मृति अतिव्यापी है, तो कोई दुष्प्रभाव नहीं हैं।

  2. के मामले में memcpy(), स्रोत मेमोरी के लिए कोई अतिरिक्त बफर नहीं लिया गया है। प्रतिलिपि सीधे मेमोरी पर की जाती है ताकि जब मेमोरी ओवरलैप हो, तो हमें अप्रत्याशित परिणाम मिले।

इन्हें निम्नलिखित कोड द्वारा देखा जा सकता है:

//include string.h, stdio.h, stdlib.h
int main(){
  char a[]="hare rama hare rama";

  char b[]="hare rama hare rama";

  memmove(a+5,a,20);
  puts(a);

  memcpy(b+5,b,20);
  puts(b);
}

आउटपुट है:

hare hare rama hare rama
hare hare hare hare hare hare rama hare rama

6
-1 - वास्तव में एक अलग बफर में डेटा को कॉपी करने के लिए मेमोव की कोई आवश्यकता नहीं है
jjwchoy

यह उदाहरण अवधारणा को समझने में मदद नहीं करता है .... जैसा कि अधिकांश संकलक मेम चाल आउटपुट के रूप में बाहर देंगे
जसदीप सिंह अरोड़ा

1
@jjwchoy वैचारिक रूप से यह करता है। बफर को आम तौर पर अनुकूलित किया जाएगा
MM

एक ही परिणाम, लिनक्स पर।
CodyChan

2

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


1

संकलक उदाहरण के लिए, मेम्पी को अनुकूलित कर सकता है:

int x;
memcpy(&x, some_pointer, sizeof(int));

इस यादगार को इस रूप में अनुकूलित किया जा सकता है: x = *(int*)some_pointer;


3
इस तरह का अनुकूलन केवल आर्किटेक्चर पर अनुमत है, जो अनलगनेटेड intएक्सेस की अनुमति देता है । कुछ आर्किटेक्चर पर (जैसे कॉर्टेक्स-एम 0), intएक पते से 32-बिट लाने का प्रयास करता है जो चार में से एक नहीं है, एक दुर्घटना का कारण होगा (लेकिन memcpyकाम करेगा)। यदि कोई या तो एक सीपीयू का उपयोग कर रहा होगा, जो बिना कीवर्ड के कंपाइलर का उपयोग कर सकता है या एक कंपाइलर का उपयोग कर सकता है जो आवश्यक होने पर अलग-अलग-अलग बाइट्स से पूर्णांक को इकट्ठा करने के लिए कंपाइलर को निर्देश देता है, तो कुछ ऐसा कर सकता है #define UNALIGNED __unalignedऔर फिर `x = * (int UNALIGNED * ) some_pointer;
सुपरकैट

2
कुछ प्रोसेसर अनलग्टेड इंट एक्सेस क्रैश की अनुमति नहीं देते char x = "12345"; int *i; i = *(int *)(x + 1);हैं, लेकिन कुछ करते हैं, क्योंकि वे गलती के दौरान कॉपी को ठीक करते हैं। मैंने इस तरह की प्रणाली पर काम किया, और यह समझने में थोड़ा समय लगा कि प्रदर्शन इतना खराब क्यों था।
user3431262

*(int *)some_pointerएक सख्त अलियासिंग उल्लंघन है, लेकिन आप शायद इसका मतलब यह है कि कंपाइलर असेंबली का उत्पादन करेगा जो एक int की नकल करता है
MM

1

मेम्पी के लिए लिंक http://clc-wiki.net/wiki/memcpy में दिए गए कोड मुझे थोड़ा भ्रमित करते हैं, क्योंकि यह उसी आउटपुट को नहीं देता है जब मैंने इसे नीचे दिए गए उदाहरण का उपयोग करके लागू किया था।

#include <memory.h>
#include <string.h>
#include <stdio.h>

char str1[11] = "abcdefghij";

void *memcpyCustom(void *dest, const void *src, size_t n)
{
    char *dp = (char *)dest;
    const char *sp = (char *)src;
    while (n--)
        *dp++ = *sp++;
    return dest;
}

void *memmoveCustom(void *dest, const void *src, size_t n)
{
    unsigned char *pd = (unsigned char *)dest;
    const unsigned char *ps = (unsigned char *)src;
    if ( ps < pd )
        for (pd += n, ps += n; n--;)
            *--pd = *--ps;
    else
        while(n--)
            *pd++ = *ps++;
    return dest;
}

int main( void )
{
    printf( "The string: %s\n", str1 );
    memcpy( str1 + 1, str1, 9 );
    printf( "Actual memcpy output: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "abcdefghij" );   // reset string

    memcpyCustom( str1 + 1, str1, 9 );
    printf( "Implemented memcpy output: %s\n", str1 );

    strcpy_s( str1, sizeof(str1), "abcdefghij" );   // reset string

    memmoveCustom( str1 + 1, str1, 9 );
    printf( "Implemented memmove output: %s\n", str1 );
    getchar();
}

आउटपुट:

The string: abcdefghij
Actual memcpy output: aabcdefghi
Implemented memcpy output: aaaaaaaaaa
Implemented memmove output: aabcdefghi

लेकिन अब आप समझ सकते हैं कि मेमोव ओवरलैपिंग मुद्दे का ध्यान क्यों रखेगा


1

C11 मानक ड्राफ्ट

C11 N1570 मानक प्रारूप का कहना है:

7.24.2.1 "यादगार समारोह":

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

7.24.2.2 "मेमोव फ़ंक्शन":

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

इसलिए, कोई भी ओवरलैप memcpyअपरिभाषित व्यवहार की ओर जाता है, और कुछ भी हो सकता है: बुरा, कुछ भी या अच्छा भी नहीं। अच्छा हालांकि दुर्लभ है :-)

memmove हालांकि स्पष्ट रूप से कहते हैं कि सब कुछ होता है जैसे कि एक मध्यवर्ती बफर का उपयोग किया जाता है, इसलिए स्पष्ट रूप से ओवरलैप्स ठीक हैं।

C ++ std::copyअधिक क्षमाशील है, और ओवरलैप की अनुमति देता है: क्या std :: copy हैंडल ओवरलैपिंग रेंज है?


memmoven के एक अतिरिक्त अस्थायी सरणी का उपयोग करें, तो क्या यह अतिरिक्त मेमोरी का उपयोग करता है? लेकिन यह कैसे हो सकता है अगर हमने इसे किसी मेमोरी में एक्सेस नहीं दिया है। (यह 2x मेमोरी का उपयोग कर रहा है)।
clmno

@clmno इसे किसी अन्य फ़ंक्शन की तरह स्टैक या मालॉक पर आवंटित करता है, जिसकी मुझे उम्मीद है :-)
Ciro Santilli 郝海东 alloc alloc alloc

1
मैंने यहाँ एक प्रश्न पूछा , एक अच्छा उत्तर भी मिला। धन्यवाद। अपने हैकर्न्यूज़ पोस्ट को देखा, जो वायरल हो गया (x86 one) :)
clmno

-4

मैंने ग्रहण का उपयोग करके एक ही कार्यक्रम चलाने की कोशिश की है और यह memcpyऔर के बीच स्पष्ट अंतर दिखाता है memmovememcpy()स्मृति स्थान के ओवरलैपिंग के बारे में परवाह नहीं करता है, जिसके परिणामस्वरूप डेटा का भ्रष्टाचार होता है, जबकि memmove()डेटा को पहले अस्थायी चर में कॉपी करेगा और फिर वास्तविक मेमोरी स्थान में कॉपी करेगा।

स्थान से डेटा कॉपी करने की कोशिश में str1करने के लिए str1+2, उत्पादन की memcpyहै " aaaaaa"। सवाल यह होगा कि कैसे? memcpy()बाएं से दाएं एक समय में एक बाइट की नकल करेगा। जैसा कि आपके कार्यक्रम में दिखाया गया है " aabbcc" तब सभी कॉपीिंग नीचे की तरह होगी,

  1. aabbcc -> aaabcc

  2. aaabcc -> aaaacc

  3. aaaacc -> aaaaac

  4. aaaaac -> aaaaaa

memmove() पहले अस्थायी चर पर डेटा की प्रतिलिपि बनाएंगे और फिर वास्तविक मेमोरी लोकेशन पर कॉपी करेंगे।

  1. aabbcc(actual) -> aabbcc(temp)

  2. aabbcc(temp) -> aaabcc(act)

  3. aabbcc(temp) -> aaaacc(act)

  4. aabbcc(temp) -> aaaabc(act)

  5. aabbcc(temp) -> aaaabb(act)

आउटपुट है

memcpy : aaaaaa

memmove : aaaabb


2
ढेर अतिप्रवाह में आपका स्वागत है। कृपया पृष्ठ के बारे में जल्द ही पढ़ें । संबोधित करने के लिए विभिन्न मुद्दे हैं। सबसे पहले और सबसे महत्वपूर्ण, आपने 18 महीने या उससे पहले के कई उत्तरों के साथ एक प्रश्न का उत्तर जोड़ा। इसके अलावा वारंट करने के लिए, आपको नई जानकारी प्रदान करने की आवश्यकता होगी। दूसरा, आप ग्रहण को निर्दिष्ट करते हैं, लेकिन ग्रहण एक आईडीई है जो सी संकलक का उपयोग करता है, लेकिन आप उस मंच की पहचान नहीं करते हैं जहां आपका कोड चल रहा है या सी संकलक ग्रहण का उपयोग कर रहा है। मुझे यह जानने में रुचि होगी कि आप कैसे पता लगाते हैं कि memmove()प्रतियां एक मध्यवर्ती स्थान पर हैं। जब आवश्यक हो तो बस रिवर्स में कॉपी करना चाहिए।
जोनाथन लेफ्लर

धन्यवाद। संकलक के बारे में, इसलिए मैं लिनक्स पर gcc संकलक का उपयोग कर रहा हूँ। मेमोव के लिए लिनक्स में एक मैन पेज है जो स्पष्ट रूप से निर्दिष्ट करता है कि मेमो डेटा के अतिव्यापीकरण से बचने के लिए अस्थायी चर में डेटा की प्रतिलिपि बनाएगा। यहाँ उस आदमी पेज linux.die.net/man/3/memmove
प्रतीक पंचाल

3
यह वास्तव में "जैसा है" कहता है, जिसका अर्थ यह नहीं है कि वास्तव में ऐसा होता है। दी यह वास्तव में इसे इस तरह से कर सकता है (हालांकि इस बारे में प्रश्न होंगे कि इसे अतिरिक्त मेमोरी कहां से मिलती है), लेकिन मुझे थोड़ा आश्चर्य होगा कि अगर यह वास्तव में होता है तो क्या होगा। यदि स्रोत पता लक्ष्य पते से अधिक है, तो यह शुरू से अंत तक कॉपी करने के लिए पर्याप्त है (फॉरवर्ड कॉपी); यदि स्रोत पता लक्ष्य पते से कम है, तो यह अंत से प्रारंभ (पीछे की ओर कॉपी) के लिए कॉपी करने के लिए पर्याप्त है। कोई ऑक्ज़िलरी मेमोरी की आवश्यकता नहीं है या इसका उपयोग नहीं किया जाता है।
जोनाथन लेफ्लर

कोड में वास्तविक डेटा के साथ अपना उत्तर समझाने की कोशिश करें, जो अधिक उपयोगी होगा।
हसीबी मीर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.