memcpy () vs memmove ()


157

ฉันพยายามที่จะเข้าใจความแตกต่างระหว่างmemcpy()และmemmove()และฉันได้อ่านข้อความที่memcpy()ไม่ดูแลแหล่งที่มาและปลายทางที่ทับซ้อนกันในขณะที่memmove()ทำ

อย่างไรก็ตามเมื่อฉันรันทั้งสองฟังก์ชั่นนี้บนบล็อคหน่วยความจำที่ทับซ้อนกันทั้งคู่ก็ให้ผลลัพธ์เหมือนกัน ตัวอย่างเช่นใช้ตัวอย่าง MSDN ต่อไปนี้ในmemmove()หน้าวิธีใช้: -

มีตัวอย่างที่ดีกว่าที่จะเข้าใจข้อเสีย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 มี memcpy ที่ปลอดภัย () มาระยะหนึ่งแล้ว
Hans Passant

32
ฉันไม่คิดว่า "ปลอดภัย" เป็นคำที่เหมาะสมสำหรับมัน มีตู้นิรภัยที่memcpyจะassertว่าภูมิภาคไม่ทับซ้อนกันมากกว่าจงใจปกปิดข้อบกพร่องในรหัสของคุณ
.. GitHub หยุดช่วยน้ำแข็ง

6
ขึ้นอยู่กับว่าคุณหมายถึง "ปลอดภัยสำหรับนักพัฒนา" หรือ "ปลอดภัยสำหรับผู้ใช้ปลายทาง" ฉันจะยืนยันว่าการทำตามที่บอกไว้แม้ว่าจะไม่เป็นไปตามมาตรฐานเป็นตัวเลือกที่ปลอดภัยสำหรับผู้ใช้ปลายทาง
kusma

ตั้งแต่ glibc 2.19 - ไม่ทำงาน The string: aabbcc New string: aaaaaa The string: aabbcc New string: aaaabb
askovpen

นอกจากนี้คุณยังสามารถดูได้ที่นี่
Ren

คำตอบ:


124

ฉันไม่แปลกใจเลยที่ตัวอย่างของคุณไม่แสดงพฤติกรรมแปลก ๆ ลองคัดลอกstr1ไปที่str1+2แทนและดูว่าเกิดอะไรขึ้น (อาจไม่สร้างความแตกต่างขึ้นอยู่กับคอมไพเลอร์ / ไลบรารี)

โดยทั่วไป memcpy ถูกนำมาใช้ในลักษณะที่เรียบง่าย (แต่เร็ว) แบบง่ายๆมันวนรอบข้อมูล (ตามลำดับ) คัดลอกจากที่หนึ่งไปยังอีกที่หนึ่ง สิ่งนี้สามารถส่งผลให้แหล่งที่มาถูกเขียนทับในขณะที่มันกำลังอ่าน

Memmove ทำงานได้มากขึ้นเพื่อให้แน่ใจว่าจะจัดการการทับซ้อนได้อย่างถูกต้อง

แก้ไข:

(น่าเสียดายที่ฉันไม่สามารถหาตัวอย่างที่เหมาะสมได้ แต่สิ่งเหล่านี้จะทำ) ตัดกันการใช้memcpyและmemmove ที่แสดงที่นี่ memcpy จะวนซ้ำในขณะที่ memmove ทำการทดสอบเพื่อกำหนดทิศทางที่จะวนซ้ำเพื่อหลีกเลี่ยงการทำลายข้อมูล การใช้งานเหล่านี้ค่อนข้างง่าย การใช้งานที่มีประสิทธิภาพสูงส่วนใหญ่มีความซับซ้อนมากขึ้น


2
+1 นอกจากนี้ในการใช้งานต่อไปนี้ให้memmoveเรียกใช้memcpyในหนึ่งสาขาหลังจากทดสอบตัวชี้: student.cs.uwaterloo.ca/~cs350/common/os161-src-html/…
Pascal Cuoq

เป็นความคิดที่ดี. ดูเหมือนว่า Visual Studio ใช้ memcpy "ปลอดภัย" (พร้อมกับ gcc 4.1.1 ฉันทดสอบบน RHEL 5 เช่นกัน) การเขียนเวอร์ชันของฟังก์ชั่นเหล่านี้จาก clc-wiki.net จะให้ภาพที่ชัดเจน ขอบคุณ
user534785

3
memcpy ไม่ได้ดูแลปัญหาที่ทับซ้อนกัน แต่ memmove ทำ ถ้าอย่างนั้นทำไมไม่กำจัด memcpy ออกจาก lib?
Alcott

37
@Alcott: เพราะmemcpyเร็วกว่า
Billy ONeal

ลิงก์คงที่ / webarchive จาก Pascal Cuoq ด้านบน: 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. 

การประยุกต์ใช้ memcpy บางอย่างอาจยังคงใช้งานได้กับอินพุตที่ทับซ้อนกัน แต่คุณไม่สามารถนับจำนวนการทำงานนั้นได้ ในขณะที่ memmove จะต้องอนุญาตให้มีการทับซ้อนกัน


3
มันช่วยฉันได้จริงๆ +1 สำหรับข้อมูลของคุณ
Muthu Ganapathy Nathan

33

เพียงเพราะmemcpyไม่ต้องจัดการกับภูมิภาคที่ทับซ้อนกันไม่ได้หมายความว่ามันไม่ได้จัดการกับมันอย่างถูกต้อง การโทรที่มีพื้นที่ทับซ้อนกันจะสร้างพฤติกรรมที่ไม่ได้กำหนด พฤติกรรมที่ไม่ได้กำหนดสามารถทำงานได้อย่างสมบูรณ์ตามที่คุณคาดหวังบนแพลตฟอร์มเดียว นั่นไม่ได้หมายความว่ามันถูกต้องหรือถูกต้อง


10
โดยเฉพาะอย่างยิ่งทั้งนี้ขึ้นอยู่กับแพลตฟอร์มเป็นไปได้ว่าจะดำเนินการตรงเช่นเดียวกับmemcpy memmoveนั่นคือใครก็ตามที่เขียนคอมไพเลอร์ไม่ได้รบกวนการเขียนmemcpyฟังก์ชั่นที่ไม่เหมือนใคร
แคม

19

ทั้ง memcpy และ memove ทำสิ่งที่คล้ายกัน

แต่จะเห็นความแตกต่างหนึ่ง:

#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 ไบต์) ข้อผิดพลาดนอกขอบเขตส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด ความแตกต่างในผลลัพธ์ที่แสดงของการเรียก memcpy () / memmove () เป็นการใช้งานเฉพาะ และตัวอย่างผลลัพธ์ไม่ตรงกับโปรแกรมด้านบน ... นอกจากนี้ strcpy_s () ไม่ได้เป็นส่วนหนึ่งของ C AFAIK มาตรฐาน (เฉพาะ MS โปรดดู: stackoverflow.com/questions/36723946/… ) - โปรดแก้ไขให้ถูกต้องถ้าฉัน มันผิด
rel

7

การสาธิตของคุณไม่ได้เปิดเผยข้อเสีย memcpy เนื่องจากคอมไพเลอร์ "ไม่ดี" คุณชอบในเวอร์ชัน Debug อย่างไรก็ตามรุ่นที่วางจำหน่ายให้ผลลัพธ์เดียวกัน แต่เนื่องจากการปรับให้เหมาะสม

    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  

นี่คือเหตุผลที่ฉันเลือกเสมอmemmoveเมื่อพยายามคัดลอกบล็อกหน่วยความจำที่ซ้อนกัน 2 แผ่น


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 - ไม่มีข้อกำหนดสำหรับ memmove ในการคัดลอกข้อมูลลงในบัฟเฟอร์ที่แยกต่างหาก
จริงๆคือ

ตัวอย่างนี้ไม่ได้ช่วยในการทำความเข้าใจแนวคิด .... เนื่องจากคอมไพเลอร์ส่วนใหญ่จะให้ผลเหมือนกับ mem move output
Jasdeep Singh Arora

1
@jjwchoy ในความคิดมัน โดยปกติบัฟเฟอร์จะได้รับการออปติไมซ์ออก
MM

ผลลัพธ์เดียวกันบน Linux
CodyChan

2

ตามที่ระบุไว้แล้วในคำตอบอื่น ๆmemmoveมีความซับซ้อนมากกว่าmemcpyที่มันจะทับซ้อนหน่วยความจำ ผลจากการ memmove ถูกกำหนดให้เป็นถ้าถูกคัดลอกลงในบัฟเฟอร์แล้วคัดลอกลงในบัฟเฟอร์src dstนี่ไม่ได้หมายความว่าการใช้งานจริงจะใช้บัฟเฟอร์ใด ๆ แต่อาจเป็นตัวชี้ทางคณิตศาสตร์


1

คอมไพเลอร์สามารถเพิ่มประสิทธิภาพ memcpy ตัวอย่างเช่น:

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

memcpy นี้อาจปรับให้เหมาะสมเป็น: x = *(int*)some_pointer;


3
การเพิ่มประสิทธิภาพดังกล่าวอนุญาตให้ใช้ได้เฉพาะกับสถาปัตยกรรมที่อนุญาตการintเข้าถึงที่ไม่ได้จัดแนว ในสถาปัตยกรรมบางอย่าง (เช่น Cortex-M0) การพยายามดึง 32- บิตintจากที่อยู่ซึ่งไม่ใช่หลายในสี่จะทำให้เกิดความผิดพลาด (แต่memcpyจะใช้งานได้) หากใครจะใช้ CPU ซึ่งอนุญาตให้เข้าถึงแบบไม่กำหนดแนวร่วมหรือใช้คอมไพเลอร์ที่มีคีย์เวิร์ดที่สั่งให้คอมไพเลอร์รวบรวมจำนวนเต็มออกจากไบต์ที่ดึงข้อมูลแยกต่างหากเมื่อจำเป็นก็สามารถทำได้เช่น#define UNALIGNED __unalignedนั้น `x = * (int UNALIGNED * ) some_pointer;
supercat

2
ตัวประมวลผลบางตัวไม่อนุญาตให้มีการเข้าถึง int ที่ผิดพลาดchar x = "12345"; int *i; i = *(int *)(x + 1);แต่บางตัวทำเพราะพวกเขาแก้ไขสำเนาในระหว่างที่เกิดข้อผิดพลาด ฉันทำงานในระบบเช่นนี้และใช้เวลาสักครู่เพื่อทำความเข้าใจว่าทำไมประสิทธิภาพจึงไม่ดี
user3431262

*(int *)some_pointerเป็นการละเมิดนามแฝงที่เข้มงวด แต่คุณอาจหมายถึงว่าคอมไพเลอร์จะส่งออกแอสเซมบลีซึ่งคัดลอก int
MM

1

รหัสที่ระบุในลิงก์http://clc-wiki.net/wiki/memcpyสำหรับ 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

แต่ตอนนี้คุณสามารถเข้าใจได้ว่าทำไมmemmoveจะดูแลปัญหาที่ทับซ้อนกัน


1

ร่างมาตรฐาน C11

ร่างมาตรฐาน C11 N1570พูดว่า:

7.24.2.1 "ฟังก์ชั่น memcpy":

2 ฟังก์ชั่น memcpy คัดลอกตัวละคร n จากวัตถุที่ชี้ไปโดย s2 ลงในวัตถุที่ชี้ไปโดย s1 หากการคัดลอกเกิดขึ้นระหว่างวัตถุที่ทับซ้อนกันพฤติกรรมจะไม่ได้กำหนดไว้

7.24.2.2 "ฟังก์ชั่น memmove":

2 ฟังก์ชั่น memmove คัดลอกตัวละคร n จากวัตถุที่ชี้ไปโดย s2 ลงในวัตถุที่ชี้ไปโดย s1 การคัดลอกเกิดขึ้นราวกับว่าอักขระ n จากวัตถุที่ชี้ไปยัง s2 ถูกคัดลอกไปยังอาร์เรย์ชั่วคราวที่มีอักขระ n ตัวที่ไม่ซ้อนทับวัตถุที่ชี้ไปที่ s1 และ s2 และคัดลอกอักขระ n จากอาร์เรย์ชั่วคราวลงใน วัตถุที่ชี้ไปโดย s1

ดังนั้นการทับซ้อนใด ๆ ที่memcpyนำไปสู่พฤติกรรมที่ไม่ได้กำหนดและสิ่งที่สามารถเกิดขึ้นได้: ไม่ดีไม่มีอะไรหรือแม้แต่ดี สิ่งที่ดีนั้นหาได้ยาก :-)

memmove อย่างไรก็ตามกล่าวอย่างชัดเจนว่าทุกอย่างเกิดขึ้นราวกับว่ามีการใช้บัฟเฟอร์ระดับกลางดังนั้นการซ้อนทับอย่างชัดเจนจึงโอเค

อย่างไรก็ตาม C ++ std::copyนั้นให้อภัยมากกว่าและอนุญาตให้มีการทับซ้อน: std :: copy จัดการช่วงที่ทับซ้อนกันหรือไม่


memmoveใช้อาร์เรย์ชั่วคราวเป็นพิเศษของ n ดังนั้นจะใช้หน่วยความจำเพิ่มเติมหรือไม่ แต่จะทำได้อย่างไรถ้าเราไม่ให้สิทธิ์การเข้าถึงหน่วยความจำใด ๆ (มันใช้หน่วยความจำ 2 เท่า)
clmno

@clmno มันจัดสรรใน stack หรือ malloc เช่นฟังก์ชั่นอื่น ๆ ที่ฉันคาดหวัง:
Ciro Santilli 郝海东冠状病病六四事件法轮功

1
ฉันถามคำถามที่นี่ได้คำตอบที่ดีเช่นกัน ขอบคุณ. เห็นโพสต์แฮ็กข่าวของคุณที่ติดไวรัส (x86 อันใดอันหนึ่ง) :)
23419

-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
ยินดีต้อนรับสู่ Stack Overflow โปรดอ่านหน้าเกี่ยวกับในไม่ช้า มีปัญหาต่าง ๆ ที่อยู่ ก่อนอื่นคุณต้องเพิ่มคำตอบสำหรับคำถามที่มีหลายคำตอบจาก 18 เดือนหรือมากกว่านั้น เพื่อรับประกันการเพิ่มคุณจะต้องให้ข้อมูลใหม่ที่น่าตกใจ ประการที่สองคุณระบุ Eclipse แต่ Eclipse เป็น IDE ที่ใช้คอมไพเลอร์ C แต่คุณไม่ได้ระบุแพลตฟอร์มที่โค้ดของคุณกำลังทำงานหรือ C คอมไพเลอร์ Eclipse ใช้อยู่ ฉันสนใจที่จะทราบว่าคุณมั่นใจได้อย่างไรว่าmemmove()สำเนาเหล่านั้นไปยังตำแหน่งกลาง มันควรคัดลอกในสิ่งที่ตรงกันข้ามเมื่อจำเป็น
Jonathan Leffler

ขอบคุณ เกี่ยวกับคอมไพเลอร์ดังนั้นฉันใช้ gcc compiler บน linux มี man page ใน linux สำหรับ memove ซึ่งระบุไว้อย่างชัดเจนว่า memove จะคัดลอกข้อมูลในตัวแปรชั่วคราวเพื่อหลีกเลี่ยงการทับซ้อนของข้อมูล นี่คือลิงค์ของ man page นั้นlinux.die.net/man/3/memmove
Pratik Panchal

3
จริงๆแล้วมันบอกว่า "ราวกับว่า" ซึ่งไม่ได้หมายความว่ามันเป็นสิ่งที่เกิดขึ้นจริง จริงอยู่ที่มันสามารถทำแบบนั้นได้ (แม้ว่าจะมีคำถามเกี่ยวกับที่ที่มันได้รับความทรงจำสำรอง) แต่ฉันจะแปลกใจมากกว่านี้เล็กน้อยถ้านั่นคือสิ่งที่มันทำจริง หากที่อยู่ต้นทางมากกว่าที่อยู่เป้าหมายก็เพียงพอที่จะคัดลอกตั้งแต่ต้นจนจบ (คัดลอกไปข้างหน้า); หากที่อยู่ต้นทางมีค่าน้อยกว่าที่อยู่เป้าหมายก็เพียงพอที่จะคัดลอกจากจุดเริ่มต้นไปยังจุดเริ่มต้น (คัดลอกไปข้างหลัง) ไม่จำเป็นต้องใช้หรือใช้หน่วยความจำเสริม
Jonathan Leffler

พยายามอธิบายคำตอบของคุณด้วยข้อมูลจริงในโค้ดซึ่งจะเป็นประโยชน์มากกว่า
HaseeB เมียร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.