方大侠 发表于 2019-4-3 18:54:38

一个选择题

数组data最终的数据是哪个()。
#define MAX_DATA_SIZE (10)

int main()
{
        unsigned int i = 0;
      unsigned char data = { '1','2','3','4','5','6','7','8','9','0' };
        memcpy(&data, data, MAX_DATA_SIZE / 2);

        return 0;
}

1234567890

1212121890

1212345890

其他结果都不对
KEY:C???

不应该是B、1212121890吗??
拷贝过来data里的数据就应该变了呀??

人造人 发表于 2019-4-3 18:54:39

我们以1212345890为例,具体看一看memcpy内部是怎么做的(在vs2017中)

通过分析memcpy的源代码,我发现vs是先复制后4个字节,然后再复制最前面那1个字节

先把 复制到 (一次性复制4个字节)
最后把复制到

如果你有能力研究memcpy的源代码,那么请看下面的memcpy源代码,如果没有能力研究,那么目前你只是记住“不同的编译器有不同的方法,甚至同一个编译器的不同编译选项都有不同的结果”就行了,等你有了这个能力以后,自己去看源代码
       page    ,132
      title   memcpy - Copy source memory bytes to destination
;***
;memcpy.asm - contains memcpy and memmove routines
;
;       Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
;       memcpy() copies a source memory buffer to a destination buffer.
;       Overlapping buffers are not treated specially, so propogation may occur.
;       memmove() copies a source memory buffer to a destination buffer.
;       Overlapping buffers are treated specially, to avoid propogation.
;
;*******************************************************************************

      .xlist
      include vcruntime.inc
      .list
      .xmm

M_EXITmacro
      ret                     ; _cdecl return
      endm    ; M_EXIT

PALIGN_memcpy macro d
MovPalign&d&:
      movdqaxmm1,xmmword ptr
      lea   esi, byte ptr

      align   @WordSize

PalignLoop&d&:
      movdqaxmm3,xmmword ptr
      sub   ecx,30h
      movdqaxmm0,xmmword ptr
      movdqaxmm5,xmmword ptr
      lea   esi, xmmword ptr
      cmp   ecx,30h
      movdqaxmm2,xmm3

      palignr xmm3,xmm1,d

      movdqaxmmword ptr ,xmm3
      movdqaxmm4,xmm0

      palignr xmm0,xmm2,d

      movdqaxmmword ptr ,xmm0
      movdqaxmm1,xmm5

      palignr xmm5,xmm4,d

      movdqaxmmword ptr ,xmm5
      lea   edi, xmmword ptr
      jae   PalignLoop&d&
      lea   esi, xmmword ptr

      endm    ; PALIGN_memcpy

      CODESEG

    extrn   __isa_available:dword
    extrn   __isa_enabled:dword
    extrn   __favor:dword

page
;***
;memcpy - Copy source buffer to destination buffer
;
;Purpose:
;       memcpy() copies a source memory buffer to a destination memory buffer.
;       This routine does NOT recognize overlapping buffers, and thus can lead
;       to propogation.
;       For cases where propogation must be avoided, memmove() must be used.
;
;       Algorithm:
;
;         Same as memmove. See Below
;
;
;memmove - Copy source buffer to destination buffer
;
;Purpose:
;       memmove() copies a source memory buffer to a destination memory buffer.
;       This routine recognize overlapping buffers to avoid propogation.
;       For cases where propogation is not a problem, memcpy() can be used.
;
;   Algorithm:
;
;       void * memmove(void * dst, void * src, size_t count)
;       {
;               void * ret = dst;
;
;               if (dst <= src || dst >= (src + count)) {
;                     /*
;                        * Non-Overlapping Buffers
;                        * copy from lower addresses to higher addresses
;                        */
;                     while (count--)
;                               *dst++ = *src++;
;                     }
;               else {
;                     /*
;                        * Overlapping Buffers
;                        * copy from higher addresses to lower addresses
;                        */
;                     dst += count - 1;
;                     src += count - 1;
;
;                     while (count--)
;                               *dst-- = *src--;
;                     }
;
;               return(ret);
;       }
;
;
;Entry:
;       void *dst = pointer to destination buffer
;       const void *src = pointer to source buffer
;       size_t count = number of bytes to copy
;
;Exit:
;       Returns a pointer to the destination buffer in AX/DX:AX
;
;Uses:
;       CX, DX
;
;Exceptions:
;*******************************************************************************

ifdef MEM_MOVE
      _MEM_   equ <memmove>
else; MEM_MOVE
      _MEM_   equ <memcpy>
endif; MEM_MOVE

%       public_MEM_
_MEM_   proc \
      dst:ptr byte, \
      src:ptr byte, \
      count:IWORD

      ; destination pointer
      ; source pointer
      ; number of bytes to copy

      OPTION PROLOGUE:NONE, EPILOGUE:NONE

      push    edi                           ; save edi
      push    esi                           ; save esi

; size param/4   prolog byte#reg saved
      .FPO ( 0, 3         , $-_MEM_   , 2, 0, 0 )

      mov   esi,                ; esi = source
      mov   ecx,                ; ecx = number of bytes to move
      mov   edi,               ; edi = dest

;
; Check for overlapping buffers:
;       If (dst <= src) Or (dst >= src + Count) Then
;               Do normal (Upwards) Copy
;       Else
;               Do Downwards Copy to avoid propagation
;

      mov   eax,ecx                         ; eax = byte count

      mov   edx,ecx                         ; edx = byte count
      add   eax,esi                         ; eax = point past source end

      cmp   edi,esi                         ; dst <= src ?
      jbe   short CopyUp                  ; no overlap: copy toward higher addresses

      cmp   edi,eax                         ; dst < (src + count) ?
      jb      CopyDown                        ; overlap: copy toward lower addresses

;
; Buffers do not overlap, copy toward higher addresses.
;
CopyUp:
      cmp   ecx, 020h
      jb      CopyUpDwordMov                  ; size smaller than 32 bytes, use dwords
      cmp   ecx, 080h
      jae   CopyUpLargeMov                  ; if greater than or equal to 128 bytes, use Enhanced fast Strings
      bt      __isa_enabled, __ISA_AVAILABLE_SSE2
      jc      XmmCopySmallTest
      jmp   Dword_align

CopyUpLargeMov:
      bt      __favor, __FAVOR_ENFSTRG      ; check if Enhanced Fast Strings is supported
      jnc   CopyUpSSE2Check               ; if not, check for SSE2 support
      rep   movsb
      mov   eax,               ; return original destination pointer
      pop   esi
      pop   edi
      M_EXIT

;
;      Check if source and destination are equally aligned.
;
CopyUpSSE2Check:
      mov   eax,edi
      xor   eax,esi
      test    eax,15
      jne   AtomChk   ; Not aligned go check Atom
      bt      __isa_enabled, __ISA_AVAILABLE_SSE2
      jc      XmmCopy ; yes, go SSE2 copy (params already set)
AtomChk:
      ; Is Atom supported?
      bt      __favor, __FAVOR_ATOM
      jnc   Dword_align ; no,jump

      ; check if dst is 4 byte aligned
      test    edi, 3
      jne   Dword_align

      ; check if src is 4 byte aligned
      test    esi, 3
      jne   Dword_align_Ok

; A software pipelining vectorized memcpy loop using PALIGN instructions

; (1) copy the first bytes to align dst up to the nearest 16-byte boundary
; 4 byte align -> 12 byte copy, 8 byte align -> 8 byte copy, 12 byte align -> 4 byte copy
PalignHead4:
      bt      edi, 2
      jae   PalignHead8
      mov   eax, dword ptr
      sub   ecx, 4
      lea   esi, byte ptr
      mov   dword ptr , eax
      lea   edi, byte ptr

PalignHead8:
      bt      edi, 3
      jae   PalignLoop
      movq    xmm1, qword ptr
      sub   ecx, 8
      lea   esi, byte ptr
      movq    qword ptr , xmm1
      lea   edi, byte ptr

;(2) Use SSE palign loop
PalignLoop:
      test    esi, 7
      je      MovPalign8
      bt      esi, 3
      jae   MovPalign4

PALIGN_memcpy 12
      jmp   PalignTail

PALIGN_memcpy 8
      jmp   PalignTail

PALIGN_memcpy 4

;(3) Copy the tailing bytes.
PalignTail:
      cmp    ecx,10h
      jb   PalignTail4
      movdqu xmm1,xmmword ptr
      sub    ecx, 10h
      lea    esi, xmmword ptr
      movdqa xmmword ptr ,xmm1
      lea    edi, xmmword ptr
      jmp    PalignTail

PalignTail4:
      bt      ecx, 2
      jae   PalignTail8
      mov   eax, dword ptr
      sub   ecx,4
      lea   esi, byte ptr
      mov   dword ptr , eax
      lea   edi, byte ptr

PalignTail8:
      bt      ecx, 3
      jae   PalignTailLE3
      movq    xmm1, qword ptr
      sub   ecx,8
      lea   esi, byte ptr
      movq    qword ptr , xmm1
      lea   edi, byte ptr

PalignTailLE3:
      mov   eax, dword ptr TrailingUpVec
      jmp   eax

; The algorithm for forward moves is to align the destination to a dword
; boundary and so we can move dwords with an aligned destination.This
; occurs in 3 steps.
;
;   - move x = ((4 - Dest & 3) & 3) bytes
;   - move y = ((L-x) >> 2) dwords
;   - move (L - x - y*4) bytes
;

Dword_align:
      test   edi,11b                        ; check if destination is dword aligned
      jz   short Dword_align_Ok             ; if destination not dword aligned already, it should be aligned

Dword_up_align_loop:
      mov   al, byte ptr
      mov   byte ptr , al
      dec   ecx
      add   esi, 1
      add   edi, 1
      test    edi, 11b
      jnz   Dword_up_align_loop
Dword_align_Ok:
      mov   edx, ecx
      cmp   ecx, 32
      jb      CopyUpDwordMov
      shr   ecx,2
      rep   movsd                           ; move all of our dwords
      and   edx,11b                         ; trailing byte count
      jmp   dword ptr TrailingUpVec; process trailing bytes

;
; Code to do optimal memory copies for non-dword-aligned destinations.
;

; The following length check is done for two reasons:
;
;    1. to ensure that the actual move length is greater than any possiale
;       alignment move, and
;
;    2. to skip the multiple move logic for small moves where it would
;       be faster to move the bytes with one instruction.
;


      align   @WordSize
ByteCopyUp:
      jmp   dword ptr TrailingUpVec; process just bytes


;-----------------------------------------------------------------------------

      align   @WordSize
TrailingUpVec      dd      TrailingUp0, TrailingUp1, TrailingUp2, TrailingUp3

      align   @WordSize
TrailingUp0:
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
                                                ; spare
      M_EXIT

      align   @WordSize
TrailingUp1:
      mov   al,                        ; get byte from source
                                                ; spare
      mov   ,al                        ; put byte in destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

      align   @WordSize
TrailingUp2:
      mov   al,                        ; get first byte from source
                                                ; spare
      mov   ,al                        ; put first byte into destination
      mov   al,                      ; get second byte from source
      mov   ,al                      ; put second byte into destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

      align   @WordSize
TrailingUp3:
      mov   al,                        ; get first byte from source
                                                ; spare
      mov   ,al                        ; put first byte into destination
      mov   al,                      ; get second byte from source
      mov   ,al                      ; put second byte into destination
      mov   al,                      ; get third byte from source
      mov   ,al                      ; put third byte into destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------

; Copy down to avoid propogation in overlapping buffers.
      align   @WordSize
CopyDown:
; inserting check for size. For < 16 bytes, use dwords without checkign for alignment

      lea   esi, ; esi, edi pointing to the end of the buffer
      lea   edi,
      cmp   ecx, 32
      jb      CopyDownSmall
      bt      __isa_enabled, __ISA_AVAILABLE_SSE2
      jc      XmmMovLargeAlignTest
; See if the destination start is dword aligned

      test    edi,11b                         ; Test if dword aligned
      jz      CopyDownAligned               ; If not, jump

CopyDownNotAligned:
      mov   edx,edi                         ; get destination offset
      and   edx, 11b
      sub   ecx, edx
CopyDownAlignLoop:
      mov   al, byte ptr
      mov   byte ptr, al
      dec   esi
      dec   edi
      sub   edx, 1
      jnz   CopyDownAlignLoop

CopyDownAligned:
      cmp   ecx,32                        ; test if small enough for unwind copy
      jb      CopyDownSmall                   ; if so, then jump
      mov   edx, ecx
      shr   ecx,2                           ; shift down to dword count
      and   edx,11b                         ; trailing byte count
      sub   esi, 4
      sub   edi, 4                        ; settign up src, dest registers
      std                                     ; set direction flag
      rep   movsd                           ; move all of dwords at once
      cld                                     ; clear direction flag back

      jmp   dword ptr TrailingDownVec; process trailing bytes


;-----------------------------------------------------------------------------

      align   @WordSize
TrailingDownVec    dd      TrailingDown0, TrailingDown1, TrailingDown2, TrailingDown3

      align   @WordSize
TrailingDown0:
      mov   eax,               ; return original destination pointer
                                                ; spare
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

      align   @WordSize
TrailingDown1:
      mov   al,                      ; get byte from source
                                                ; spare
      mov   ,al                      ; put byte in destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

      align   @WordSize
TrailingDown2:
      mov   al,                      ; get first byte from source
                                                ; spare
      mov   ,al                      ; put first byte into destination
      mov   al,                      ; get second byte from source
      mov   ,al                      ; put second byte into destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

      align   @WordSize
TrailingDown3:
      mov   al,                      ; get first byte from source
                                                ; spare
      mov   ,al                      ; put first byte into destination
      mov   al,                      ; get second byte from source
      mov   ,al                      ; put second byte into destination
      mov   al,                      ; get third byte from source
      mov   ,al                      ; put third byte into destination
      mov   eax,               ; return original destination pointer
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT

; Copy overlapping buffers using XMM registers
XmmMovLargeAlignTest:
      test    edi, 0Fh                        ; check if it's 16-byte aligned
      jz   XmmMovLargeLoop
XmmMovAlignLoop:
      dec   ecx
      dec   esi
      dec   edi
      mov   al,
      mov   , al
      test    edi, 0Fh
      jnz   XmmMovAlignLoop

XmmMovLargeLoop:
      cmp   ecx, 128
      jb      XmmMovSmallTest
      sub         esi, 128
      sub         edi, 128
      movdqu   xmm0, xmmword ptr
      movdqu   xmm1, xmmword ptr
      movdqu   xmm2, xmmword ptr
      movdqu   xmm3, xmmword ptr
      movdqu   xmm4, xmmword ptr
      movdqu   xmm5, xmmword ptr
      movdqu   xmm6, xmmword ptr
      movdqu   xmm7, xmmword ptr
      movdqu   xmmword ptr, xmm0
      movdqu   xmmword ptr, xmm1
      movdqu   xmmword ptr, xmm2
      movdqu   xmmword ptr, xmm3
      movdqu   xmmword ptr, xmm4
      movdqu   xmmword ptr, xmm5
      movdqu   xmmword ptr, xmm6
      movdqu   xmmword ptr, xmm7
      sub   ecx, 128
      test    ecx, 0FFFFFF80h
      jnz   XmmMovLargeLoop


XmmMovSmallTest:
      cmp   ecx, 32                         ; if lesser than 32, use dwords
      jb   CopyDownSmall

XmmMovSmallLoop:
      sub      esi, 32
      sub      edi, 32
      movdqu   xmm0, xmmword ptr
      movdqu   xmm1, xmmword ptr
      movdqu   xmmword ptr, xmm0
      movdqu   xmmword ptr, xmm1
      sub      ecx, 32
      test       ecx, 0FFFFFFE0h
      jnz      XmmMovSmallLoop

CopyDownSmall:
      test    ecx, 0FFFFFFFCh               ; mask the bytes
      jz      CopyDownByteTest
CopyDownDwordLoop:
      sub   edi, 4
      sub   esi, 4
      mov   eax,
      mov   , eax
      sub   ecx, 4
      test    ecx, 0FFFFFFFCh
      jnz   CopyDownDwordLoop
CopyDownByteTest:
      test    ecx, ecx
      jz      CopyDownReturn
CopyDownByteLoop:
      sub   edi, 1
      sub   esi, 1
      mov   al,
      mov   , al
      sub   ecx, 1
      jnz   CopyDownByteLoop
CopyDownReturn:
      mov   eax,               ; return original destination pointer
                                                ; spare
      pop   esi                           ; restore esi
      pop   edi                           ; restore edi
      M_EXIT


; Using XMM registers for non-overlapping buffers

align       16
XmmCopy:
      mov         eax, esi
      and         eax, 0Fh
      ; eax = src and dst alignment (src mod 16)
      test      eax, eax
      jne         XmmCopyUnaligned

      ; in:
      ; edi = dst (16 byte aligned)
      ; esi = src (16 byte aligned)
      ; ecx = len is >= (128 - head alignment bytes)
      ; do block copy using SSE2 stores
XmmCopyAligned:
      mov         edx, ecx
      and         ecx, 7Fh
      shr         edx, 7
      je          XmmCopySmallTest
      ; ecx = loop count
      ; edx = remaining copy length

; Copy greater than or equal to 128 bytes using XMM registers
align       16
XmmCopyLargeLoop:
      movdqa      xmm0,xmmword ptr
      movdqa      xmm1,xmmword ptr
      movdqa      xmm2,xmmword ptr
      movdqa      xmm3,xmmword ptr
      movdqa      xmmword ptr ,xmm0
      movdqa      xmmword ptr ,xmm1
      movdqa      xmmword ptr ,xmm2
      movdqa      xmmword ptr ,xmm3
      movdqa      xmm4,xmmword ptr
      movdqa      xmm5,xmmword ptr
      movdqa      xmm6,xmmword ptr
      movdqa      xmm7,xmmword ptr
      movdqa      xmmword ptr ,xmm4
      movdqa      xmmword ptr ,xmm5
      movdqa      xmmword ptr ,xmm6
      movdqa      xmmword ptr ,xmm7
      lea         esi,
      lea         edi,
      dec         edx
      jne         XmmCopyLargeLoop

; Copy lesser than 128 bytes
XmmCopySmallTest:
      test      ecx, ecx
      je          CopyUpReturn

      ; ecx = length (< 128 bytes)
      mov         edx, ecx
      shr         edx, 5                      ; check if there are 32 bytes that can be set
      test      edx, edx
      je          CopyUpDwordMov
      ; if > 16 bytes do a loop (16 bytes at a time)
      ; edx - loop count
      ; edi = dst
      ; esi = src

align 16
XmmCopySmallLoop:
      movdqu      xmm0, xmmword ptr
      movdqu      xmm1, xmmword ptr
      movdqu      xmmword ptr , xmm0
      movdqu      xmmword ptr , xmm1
      lea         esi,
      lea         edi,
      dec         edx
      jne         XmmCopySmallLoop

CopyUpDwordMov:

      ; last 1-32 bytes: step back according to dst and src alignment and do a 16-byte copy
      ; esi = src
      ; eax = src alignment(set at the start of the procedure and preserved up to here)
      ; edi = dst
      ; ecx = remaining len
      and         ecx, 1Fh
      je          CopyUpReturn
CopyUpDwordTest:
      mov   eax, ecx; save remaining len and calc number of dwords
      shr   ecx, 2
      je      CopyUpByteTest ; if none try bytes
CopyUpDwordLoop:
      mov   edx, dword ptr
      mov   dword ptr , edx
      add   edi, 4
      add   esi, 4
      sub   ecx, 1
      jne   CopyUpDwordLoop
CopyUpByteTest:
      mov   ecx, eax
      and   ecx, 03h
      je      CopyUpReturn ; if none return
CopyUpByteLoop:
      mov   al, byte ptr
      mov   byte ptr , al
      inc   esi
      inc   edi
      dec   ecx
      jne   CopyUpByteLoop
align 16
CopyUpReturn:
      ; return dst
      mov   eax,               ; return original destination pointer
      pop   esi
      pop   edi
      M_EXIT


; dst addr is not 16 byte aligned
align 16
XmmCopyUnaligned:

; copy the first the first 1-15 bytes to align both src and dst up to the nearest 16-byte boundary:

; in
; esi = src
; edi = dst
; eax = src and dst alignment
; ecx = length

      mov   edx, 010h
      sub   edx, eax                        ; calculate number of bytes to get it aligned
      sub   ecx, edx                        ; calc new length and save it
      push    ecx
      mov   eax, edx                        ; save alignment byte count for dwords
      mov   ecx, eax                        ; set ecx to rep count
      and   ecx, 03h
      je      XmmAlignDwordTest               ; if no bytes go do dwords
XmmAlignByte:
      mov   dl, byte ptr             ; move the bytes
      mov   byte ptr , dl
      inc   esi                           ; incrementthe addresses
      inc   edi
      dec   ecx                           ; decrement the counter
      jne   XmmAlignByte
XmmAlignDwordTest:
      shr   eax, 2                        ; get dword count
      je      XmmAlignAdjustCnt               ; if none go to main loop
XmmAlignDwordLoop:
      mov   edx, dword ptr             ; move the dwords
      mov   dword ptr , edx
      lea   esi,                   ; increment the addresses
      lea   edi,
      dec   eax                           ; decrement the counter
      jne   XmmAlignDwordLoop
XmmAlignAdjustCnt:
      pop   ecx                           ; retrieve the adjusted length
      jmp   XmmCopyAligned


_MEM_   endp
      end


82457097 发表于 2019-4-3 19:09:42

本帖最后由 82457097 于 2019-4-3 19:11 编辑

memcpy函数第一个参数是指向用来存储复制来的数据的地址的指针;第二个参数是指向赋值源的指针;第三个是复制数据的字节数;

如题:取原数组的第三个元素的地址为储存容器起始地址,取原数组第一个元素地址为复制数据源起始地址,尺寸为5个字节
综上,将3 4 5 6 7改为 1 2 3 4 5 所以得到1 2 1 2 3 4 5 8 9 0;

方大侠 发表于 2019-4-3 19:35:19

82457097 发表于 2019-4-3 19:09
memcpy函数第一个参数是指向用来存储复制来的数据的地址的指针;第二个参数是指向赋值源的指针;第三个是复 ...

是把后面5个字节的全部存储下来拷贝过去的吗??不是一个一个拷贝过去的吗??
data = data;
data = data; //12 12 3456
data = data;//这里data不是已经变成1了吗

人造人 发表于 2019-4-3 23:13:52

因为src和dst所指的内存区域重叠了

标准好像并没有规定在这个情况下memcpy的行为
不同的编译器有不同的方法,甚至同一个编译器的不同编译选项都有不同的结果
sh-5.0$ cat main.c
#include <stdio.h>
#include <string.h>

#define MAX_DATA_SIZE (10)

int main()
{
      unsigned int i = 0;
      unsigned char data = { '1','2','3','4','5','6','7','8','9','0' };
      memcpy(&data, data, MAX_DATA_SIZE / 2);

        for(i = 0; i < 10; ++i)
        {
                printf("%c ", data);
        }
        printf("\n");

      return 0;
}
sh-5.0$ gcc -o main main.c
sh-5.0$ ./main
1 2 1 2 3 4 5 8 9 0
sh-5.0$ gcc -O3 -o main main.c
sh-5.0$ ./main
1 2 1 2 3 4 3 8 9 0
sh-5.0$

页: [1]
查看完整版本: 一个选择题