交换两段连续(不连续)内存<转>


在《编程珠玑,第二版》和《编程之美》中都有关于交换两段连续的内存块的题目,而且非常经典,交换两段连续内存块的问题又可以延伸到交换两段不连续内存块的问题,无论是哪种情况都需要一个基本操作:反转内存块

反转内存时,比较简单,可以两个指针同时走,也可以一个指针,随意,实现如下:

01 // reverse the memory block
02 // goal: |12345| -> |54321|
03 void * reverseMemory(void *pMemory, const size_t memSize)
04 {
05     if(NULL == pMemory) return pMemory;
06     if(memSize < 2)        return pMemory;
07
08     char * beg = static_cast<char *>(pMemory);
09     char * end = beg + memSize -1;
10     for(; beg < end; ++beg, --end)
11     {
12         char memTmp = *beg;
13         *beg = *end;
14         *end = memTmp;
15     }
16     return pMemory;
17 }

其中,函数的两个形参分别为要反转内存的起始地址和内存块的大小。返回值类型为void*,指向反转内存块的起始地址。

实现了反转内存块这一基本操作后,我们就可以用它来交换两段连续的内存,从而解决类似“数组循环移位”的问题。具体算法在编程珠玑和编程之美中都有详细介绍,这里简单说一下:

假设有连续的内存块M和N形如:|----M---|--------N---------|

  1. 首先反转内存块M:M’=reverse(M)
  2. 反转内存块N:N’=reverse(N)
  3. 对整个内存块M’ N’进行反转:(M’N’)’ = NM


举例:M=”abc”, N=”1234”

  1. M’=reverse(M)=”cba”
  2. N’=reverse(N)=”4321”
  3. (M’N’)’=(cba4321)’=”1234abc”

实现如下:

01 // swap two adjacent memory block
02 // goal: |*****|######|  -> |######|*****|
03 void * swapAdjacentMemory(void *pMemory, const size_t headSize, const size_ttotalSize)
04 {
05     if(NULL == pMemory)    return pMemory;
06     if(totalSize < 2)    return pMemory;
07     if(headSize >= totalSize)    return pMemory;
08
09     char* pStart = static_cast<char*>(pMemory);
10     reverseMemory(pStart,headSize);
11     reverseMemory(pStart+headSize,totalSize-headSize);
12     reverseMemory(pStart,totalSize);
13     return pMemory;
14 }

函数的形参为内存起始地址pMemory、头长度headSize和内存总长度totalSize,其中headSize就相当于“数组循环移位问题”中移位大小,如右移4位等同于headSize为4。

现在我们明白了如何交换两段连续的内存,那么如何交换两段不连续的内存呢?

形如:有内存MAN三块:|----M---|----A----|-----N------|,我们要交换M,N,保持A不动。

类似于交换两段连续内存,仔细地想一下并不难,算法如下:

  1. 反转内存块M:M’=reverse(M)
  2. 反转A:A’=reverse(A)
  3. 反转N:N’=reverse(N)
  4. 反转M’A’N’:(M’A’N’)’=NAM

实现如下:

01 // swap two nonadjacent memory block
02 // goal: |*****|$$$$|######|  -> |######|$$$$|*****|
03 void* swapNonAdjacentMemory(void *pMemory, const size_t headSize, const size_tendSize, const size_t totalSize)
04 {
05     if(NULL == pMemory)    return pMemory;
06     if(totalSize < 3)    return pMemory;
07     if(headSize >= totalSize || endSize >= totalSize)    return pMemory;
08     if(totalSize < (headSize+endSize))    return pMemory;
09
10     char* pStart = static_cast<char *>(pMemory);
11     reverseMemory(pStart,headSize);
12     reverseMemory(pStart+headSize,totalSize-headSize-endSize);
13     reverseMemory(pStart+totalSize-endSize,endSize);
14     reverseMemory(pStart,totalSize);
15     return pMemory;
16 }

实现之后,我们写一个测试用例来验证一下:


01 #include <iostream>
02 using namespace std;
03
04 #define MAXBUFFERSIZE 100
05
06 void main()
07 {
08     //这里我们将所有的测试用例放在一个字符串数组中,循环便可以测试所有的字符串
09     //如果要修改字符串或增加新的测试用例,只需修改这个表即可
10
11     static const char* stringArr[]=
12     {
13         "",
14         "ab",
15         "abc",
16         "abc1234"
17     };
18
19     void * resultArr = malloc(MAXBUFFERSIZE); // store the test result
20     if(NULL == resultArr)    return ;
21
22     size_t arrLen = sizeof(stringArr)/sizeof(*stringArr);
23
24     // test swapAdjacentMemory |
25     for(size_t i = 0; i < arrLen; ++i)
26     {
27         memset(resultArr,0,MAXBUFFERSIZE);
28         memcpy(resultArr,stringArr[i],strlen(stringArr[i]));
29         swapAdjacentMemory(resultArr,2,strlen(*(stringArr+i)));
30         printf("%s\n",resultArr);
31     }
32
33     // test swapNonAdjacentMemory
34     for(size_t i = 0; i < arrLen; ++i)
35     {
36         memset(resultArr,0,MAXBUFFERSIZE);
37         memcpy(resultArr,stringArr[i],strlen(stringArr[i]));
38         swapNonAdjacentMemory(resultArr,2,2,strlen(stringArr[i]));
39         printf("%s\n",resultArr);
40     }
41
42     free(resultArr);
43 }

反转内存 & 交换内存 这一操作很重要,后面的文章我们会看到其灵活运用的效果。

注:该文参考http://blog.csdn.net/houdy/article/details/1437256整理而来,感谢Houdy!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值