जवाबों:
memcpy
गंतव्य के साथ , स्रोत स्रोत को ओवरलैप नहीं कर सकता है। इसके साथ memmove
कर सकते हैं। इसका मतलब यह है कि memmove
की तुलना में थोड़ा धीमा हो सकता हैmemcpy
, क्योंकि यह समान धारणा नहीं बना सकता है।
उदाहरण के लिए, memcpy
हमेशा निम्न से उच्च पते को कॉपी कर सकते हैं। यदि स्रोत के बाद गंतव्य ओवरलैप होता है, तो इसका मतलब है कि कुछ पते कॉपी किए जाने से पहले ओवरराइट हो जाएंगे। memmove
इस मामले का पता लगाएगा और इस दिशा में कॉपी करेगा - इस मामले में उच्च से निम्न तक। हालाँकि, इसे जांचना और दूसरे पर स्विच करना (संभवतः कम कुशल) एल्गोरिदम में समय लगता है।
i = i++ + 1
अपरिभाषित है; संकलक आपको उस कोड को लिखने के लिए मना नहीं करता है, लेकिन उस निर्देश का परिणाम कुछ भी हो सकता है और अलग-अलग संकलक या सीपीयू यहां अलग-अलग मान दिखाएंगे।
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
के बीच मुख्य अंतर memmove()
और memcpy()
में वह यह है कि memmove()
एक बफर अस्थायी स्मृति - - प्रयोग किया जाता है, इसलिए वहाँ ओवरलैपिंग का कोई खतरा नहीं है। दूसरी ओर, memcpy()
सीधे उस स्थान से डेटा की प्रतिलिपि बनाता है जो स्रोत द्वारा इंगित किए गए स्थान पर गंतव्य द्वारा इंगित किया गया है । ( http://www.cplusplus.com/reference/cstring/memcpy/ )
निम्नलिखित उदाहरणों पर विचार करें:
#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
लेकिन इस उदाहरण में, परिणाम समान नहीं होंगे:
#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
memmove()
बफर का उपयोग करने के लिए कार्यान्वयन की आवश्यकता है। यह पूरी तरह से अंदर जाने के लिए हकदार है (जब तक कि प्रत्येक पढ़ने को उसी पते पर किसी भी लिखने से पहले पूरा न हो जाए)।
यह मानते हुए कि आपको दोनों को लागू करना होगा, कार्यान्वयन इस तरह दिख सकता है:
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
हमेशा "फ्रंट टू बैक" या "बैक टू फ्रंट" की प्रतिलिपि बनाई जाती है,memmove
memcpy
वह ओवरलैपिंग के मामलों में से एक में भी उपयोग कर सकता है लेकिन memcpy
यह भी एक अलग तरीके से कॉपी कर सकता है कि डेटा कैसे और / या कितना डेटा के आधार पर है नकल की है, तो भी अगर तुम कैसे परीक्षण कियाmemcpy
आपके सिस्टम पर प्रतियां , आप हमेशा सही होने के लिए उस परीक्षा परिणाम पर भरोसा नहीं कर सकते।
यह तय करने के लिए कि आपके लिए क्या मतलब है, जिसे कॉल करना है
जब तक आप यह सुनिश्चित करने के लिए जानते हैं कि src
औरdst
ओवरलैप नहीं करते हैं, कॉल memmove
यह हमेशा की तरह सही परिणाम के लिए नेतृत्व और के रूप में है कि नकल के मामले की आवश्यकता के लिए संभव है के रूप में तेजी से आम तौर पर है जाएगा।
यदि आप निश्चित रूप से जानते हैं src
और dst
ओवरलैप नहीं करते हैं, तो कॉल करें memcpy
क्योंकि इससे कोई फर्क नहीं पड़ेगा कि आप किस परिणाम के लिए कॉल करते हैं, दोनों उस स्थिति में सही तरीके से काम करेंगे, लेकिन memmove
कभी भी तेजी से नहीं होगा memcpy
और यदि आप अशुभ हैं, तो यह भी हो सकता है धीमे रहिए, जिससे आप केवल कॉलिंग जीत सकते हैं memcpy
।
बस आईएसओ / आईईसी से: 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()
करता है।
लागू करने के दो स्पष्ट तरीके हैं mempcpy(void *dest, const void *src, size_t n)
(वापसी मूल्य की अनदेखी):
for (char *p=src, *q=dest; n-->0; ++p, ++q)
*q=*p;
char *p=src, *q=dest;
while (n-->0)
q[n]=p[n];
पहले कार्यान्वयन में, प्रतिलिपि निम्न से उच्च पते तक जाती है, और दूसरे में, उच्च से निम्न तक। यदि कॉपी की जाने वाली सीमा ओवरलैप हो जाती है (जैसा कि एक फ्रेमबार को स्क्रॉल करते समय मामला होता है, उदाहरण के लिए), तो ऑपरेशन की केवल एक दिशा सही है, और दूसरा उन स्थानों को अधिलेखित कर देगा जिन्हें बाद में पढ़ा जाएगा।
एक memmove()
कार्यान्वयन, अपने सरलतम पर, dest<src
(कुछ प्लेटफ़ॉर्म-निर्भर तरीके से) परीक्षण करेगा , और उपयुक्त दिशा का निष्पादन करेगा memcpy()
।
उपयोगकर्ता कोड निश्चित रूप से ऐसा नहीं कर सकता है, क्योंकि कास्टिंग src
और dst
कुछ ठोस सूचक प्रकार के बाद भी , वे (सामान्य रूप से) एक ही वस्तु में नहीं दिखाई देते हैं और इसलिए उनकी तुलना नहीं की जा सकती है। लेकिन मानक पुस्तकालय में अपरिभाषित व्यवहार के कारण ऐसी तुलना करने के लिए पर्याप्त मंच ज्ञान हो सकता है।
ध्यान दें कि वास्तविक जीवन में, बड़े हस्तांतरणों (जब संरेखण परमिट) और / या अच्छे डेटा कैश उपयोग से अधिकतम प्रदर्शन प्राप्त करने के लिए कार्यान्वयन में काफी अधिक जटिल होते हैं। उपरोक्त कोड केवल बिंदु को यथासंभव संभव बनाने के लिए है।
मेम्मोव ओवरलैपिंग स्रोत और गंतव्य क्षेत्रों के साथ सौदा कर सकता है, जबकि मेमसीपी नहीं कर सकता। दोनों के बीच, मेम्ची बहुत अधिक कुशल है। इसलिए, यदि आप कर सकते हैं, तो USE को याद रखना बेहतर होगा।
संदर्भ: https://www.youtube.com/watch?v=Yr1YnOVG-4g डॉ। जेरी कैन, (स्टैनफोर्ड इंट्रो सिस्टम लेक्चर - 7) समय: 36:00