一种基于C++的结构体赋值内存的方法探究

本文探讨一种基于C++的结构体赋值内存的方法,通过该方法,在进行内存赋值的同时,对代码阅读较友好,可适用于一些实际工作环境,且能提高开发效率,降低维护成本。

问题提出

某工程某次迭代,需要添加一个功能,该功能无法做成一个单独的模块,要在不同模块函数中判断并处理。初次实现版本,在对应的函数最后添加一个参数,且设置默认值,这样既能达到目的又不影响原始逻辑。但涉及模块较多,代码审查时,被上峰否了。当初如此实现,一是工程代码是首次接触,工程代码量不少,且需在较少时间内了解熟悉并掌握如何改,从函数参数入手改是最快最简单的方法。

经思考后,发现原始函数有一个较大结构体参数,内有预留字段,缓冲区够大,但已经对某些地址进行赋值和获取地,地址偏移量已是固定了,比如第68字节开始的4个字节等。

方案描述

可以在缓冲区指定偏移量赋值并读取,比如固定第0字段为一标志字段,表示是否排序。但这样不安全,万一没有赋值,但被其它数据污染恰好为1,则影响代码逻辑。

故而考虑使用sort=1;foo=xxx;bar=yyy;这样的形式,出现了sort=,其后的1个字节才是所需的。这样通过多个字节加强数据准确性的判断。好处是不限长度,直观方便,但要解析对比字符串才能得到所需内容。

然而上述形式适用于字符串,假定要传递地址指针(在C++中,传递指针是常有的事),需将地址值转成字符串,读取时,再将字符串转成数值形式,较麻烦。

最终使用自定义结构体方式,使用字段对应偏移量,对于不使用的偏移地址,使用预留字段字段。但是也有缺点,即需手动设计结构体,一旦确定不能轻易改,且内容是定长的(可适当多预留一些空间)。具体见下。

工程代码

设计思路

这里结合代码描述思路,自定义的结构体StructInfo_tt,按需求列出字段和对应值的,设置tagXXX和valXXX。前者形如foo=,则其长度为4;后者假定是指针,考虑兼容32位和64位,设置长度为8。

通过StructInfo_t *myptr = (StructInfo_t *)buffer;将内存直接映射到结构体(C++就如此便捷),通过memcpy(myptr->tagFoo, "foo=", sizeof(myptr->tagFoo)); memcpy(myptr->valFoo, &mybuf, sizeof(myptr->valFoo));拷贝对应的字段。

读取亦类似。

实现代码

完整代码:

#include <stdio.h>
#include <stdlib.h>

#include <string.h>

#define DEFWIDTH 16          /* Default # chars to show per line */
#define MAXWIDTH 32          /* Maximum # of bytes per line     */
/**
输出示例:
地址:字符十六进制 可打印字符
new buffer print: 70
0x0012FAE4: 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52  .PNG........IHDR
0x0012FAF4: 00 00 00 f0 00 00 01 40 08 02 00 00 00 0d 8a 66  [email protected]
0x0012FB04: 04 00 00 20 00 49 44 41 54 78 da ed 7d 3d 68 24  ... .IDATx..}=h$
0x0012FB14: 01 02 03 04 05 06 07 08 09 0a 0b                 ...........
*/
void dump(const char *buffer, int len)
{
    int i, j, n;
    int line = DEFWIDTH;
    char c;
    unsigned char* buf = (unsigned char *)buffer;    // 必须是unsigned char类型

    n = len / line;
    if (len % line)
        n++;

    for (i=0; i<n; i++)
    {
        //printf("0x%08x: ", (unsigned int)(buf+i*line)); // linux ok
        printf("0x%8p: ", buf+i*line); // windows ok
        
        for (j=0; j<line; j++)
        {
            if ((i*line+j) < len)
                printf("%02x ", buf[i*line+j]);
            else
                printf("   ");
        }

        printf(" ");
        for (j=0; j<line && (i*line+j)<len; j++)
        {
            if ((i*line+j) < len)
            {
                c = buf[i*line+j];
                printf("%c", c >= ' ' && c < '~' ? c : '.');
            }
            else
                printf("   ");
        }

        printf("\n");
    }
}

// 自定义结构体
typedef struct StructInfo_tt
{
	char tagFoo[4]; // foo=
	char valFoo[8]; // ptr
	char tagBar[4]; // bar=
	char valBar[4]; // size ptr
	char tagName[8]; // LaTeLee=
	char valName[6]; // 250
	
	char reserve[216]; // left
} StructInfo_t;

char buffer[250] = {0};

void set_struct(char* mybuf, int bufsize)
{
    // char* bufptr = mybuf;
    // int bufsize = 250;
    StructInfo_t *myptr = (StructInfo_t *)buffer;
	memcpy(myptr->tagFoo, "foo=", sizeof(myptr->tagFoo));
	memcpy(myptr->valFoo, &mybuf, sizeof(myptr->valFoo));

    memcpy(myptr->tagBar, "bar=", sizeof(myptr->tagBar));
	memcpy(myptr->valBar, &bufsize, sizeof(myptr->valBar));
    
    char tmpName[8] = "520250";
	memcpy(myptr->tagName, "LaTeLee=", sizeof(myptr->tagName));
	memcpy(myptr->valName, tmpName, sizeof(myptr->valName));
    
    printf("mybuf ptr: %p\n", mybuf);
    dump((const char *)myptr, 64);
}

void get_struct()
{
	char *bufptr = NULL;
	int bufsize = 0;
    
    StructInfo_t *myptr = (StructInfo_t*)buffer;
	if(strncmp(myptr->tagFoo, "foo=", sizeof(myptr->tagFoo)) == 0)
	{
		// memcpy(&bufptr, myptr->valFoo, sizeof(myptr->valFoo));
		memcpy(&bufptr, myptr->valFoo, sizeof(char*));
	}
	if(strncmp(myptr->tagBar, "bar=", sizeof(myptr->tagBar)) == 0)
	{
		memcpy(&bufsize, myptr->valBar, sizeof(myptr->valBar));
	}
    
    memcpy(bufptr, "this is hello from hell", bufsize);
    printf("got ptr: %p size: %d\n", bufptr, bufsize);
    dump((const char *)myptr, 64);
}

测试环境

32位:

# uname -a
Linux debian 3.2.0-4-686-pae #1 SMP Debian 3.2.65-1+deb7u1 i686 GNU/Linux
# g++ --version
g++ (Debian 4.7.2-5) 4.7.2

64位:

$ uname -a
Linux master 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

测试代码

缓冲够大:

int main(void)
{
    printf("struct value test, sizeof(char*): %d  sizeof(int): %d\n", sizeof(char*), sizeof(int));
    char mybuf[64] = {0};
    int bufsize = 64;
    set_struct(mybuf, bufsize);
    get_struct();
    
    printf("out mybuf [%s]\n", mybuf);
    dump((const char *)mybuf, 64);
    
    return 0;
}

测试结果:

struct value test, sizeof(char*): 8  sizeof(int): 4
mybuf ptr: 0x7fffd9638240
0x0x601080: 66 6f 6f 3d 40 82 63 d9 ff 7f 00 00 62 61 72 3d  [email protected]=
0x0x601090: 40 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  @...LaTeLee=5202
0x0x6010a0: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x6010b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
got ptr: 0x7fffd9638240 size: 64
0x0x601080: 66 6f 6f 3d 40 82 63 d9 ff 7f 00 00 62 61 72 3d  [email protected]=
0x0x601090: 40 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  @...LaTeLee=5202
0x0x6010a0: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x6010b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
out mybuf [this is hello from hell]
0x0x7fffd9638240: 74 68 69 73 20 69 73 20 68 65 6c 6c 6f 20 66 72  this is hello fr
0x0x7fffd9638250: 6f 6d 20 68 65 6c 6c 00 67 6f 74 20 70 74 72 3a  om hell.got ptr:
0x0x7fffd9638260: 20 25 70 20 73 69 7a 65 3a 20 25 64 0a 00 6f 75   %p size: %d..ou
0x0x7fffd9638270: 74 20 6d 79 62 75 66 20 5b 25 73 5d 0a 00 00 00  t mybuf [%s]....

观察:运行系统的指针长度为8字节,int类型为4字节。指针及大小被正确传递。

缓冲区较小:

int main(void)
{
    printf("struct value test, sizeof(char*): %d  sizeof(int): %d\n", sizeof(char*), sizeof(int));
    char mybuf[16] = {0};
    int bufsize = 16;
    set_struct(mybuf, bufsize);
    get_struct();
    
    printf("out mybuf [%s]\n", mybuf);
    dump((const char *)mybuf, 64);
    
    return 0;
}

测试结果:

struct value test, sizeof(char*): 8  sizeof(int): 4
mybuf ptr: 0x7ffd3992b880
0x0x601080: 66 6f 6f 3d 80 b8 92 39 fd 7f 00 00 62 61 72 3d  foo=...9....bar=
0x0x601090: 10 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  ....LaTeLee=5202
0x0x6010a0: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x6010b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
got ptr: 0x7ffd3992b880 size: 16
0x0x601080: 66 6f 6f 3d 80 b8 92 39 fd 7f 00 00 62 61 72 3d  foo=...9....bar=
0x0x601090: 10 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  ....LaTeLee=5202
0x0x6010a0: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x6010b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
out mybuf [this is hello fr
0x0x7ffd3992b880: 74 68 69 73 20 69 73 20 68 65 6c 6c 6f 20 66 72  this is hello fr
0x0x7ffd3992b890: 80 b9 92 39 fd 7f 00 00 00 00 00 00 10 00 00 00  ...9............
0x0x7ffd3992b8a0: 00 00 00 00 00 00 00 00 55 15 39 d7 40 7f 00 00  ........U.9.@...
0x0x7ffd3992b8b0: 00 00 00 00 00 00 00 00 88 b9 92 39 fd 7f 00 00  ...........9....

说明:

因为mybuf较少,底层按实际大小拷贝,字符串this is hello fr就是16字节,只是其后没有\0结尾,因此无法正确换行(从二进制打印也可看出)。

32位测试

缓存区足够:

struct value test, sizeof(char*): 4  sizeof(int): 4
mybuf ptr: 0xbfcac45c
0x0x8049e20: 66 6f 6f 3d 5c c4 ca bf 40 00 00 00 62 61 72 3d  foo=\[email protected]=
0x0x8049e30: 40 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  @...LaTeLee=5202
0x0x8049e40: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x8049e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
got ptr: 0xbfcac45c size: 64
0x0x8049e20: 66 6f 6f 3d 5c c4 ca bf 40 00 00 00 62 61 72 3d  foo=\[email protected]=
0x0x8049e30: 40 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  @...LaTeLee=5202
0x0x8049e40: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x8049e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
out mybuf [this is hello from hell]
0x0xbfcac45c: 74 68 69 73 20 69 73 20 68 65 6c 6c 6f 20 66 72  this is hello fr
0x0xbfcac46c: 6f 6d 20 68 65 6c 6c 00 67 6f 74 20 70 74 72 3a  om hell.got ptr:
0x0xbfcac47c: 20 25 70 20 73 69 7a 65 3a 20 25 64 0a 00 00 00   %p size: %d....
0x0xbfcac48c: 73 74 72 75 63 74 20 76 61 6c 75 65 20 74 65 73  struct value tes

缓冲区较小:

# ./a.out 
struct value test, sizeof(char*): 4  sizeof(int): 4
mybuf ptr: 0xbfb3f25c
0x0x8049e20: 66 6f 6f 3d 5c f2 b3 bf 10 00 00 00 62 61 72 3d  foo=\.......bar=
0x0x8049e30: 10 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  ....LaTeLee=5202
0x0x8049e40: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x8049e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
got ptr: 0xbfb3f25c size: 16
0x0x8049e20: 66 6f 6f 3d 5c f2 b3 bf 10 00 00 00 62 61 72 3d  foo=\.......bar=
0x0x8049e30: 10 00 00 00 4c 61 54 65 4c 65 65 3d 35 32 30 32  ....LaTeLee=5202
0x0x8049e40: 35 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  50..............
0x0x8049e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
out mybuf [this is hello fr]
0x0xbfb3f25c: 74 68 69 73 20 69 73 20 68 65 6c 6c 6f 20 66 72  this is hello fr
0x0xbfb3f26c: 10 00 00 00 30 8a 04 08 00 00 00 00 f8 f2 b3 bf  ....0...........
0x0xbfb3f27c: 46 9e 53 b7 01 00 00 00 24 f3 b3 bf 2c f3 b3 bf  F.S.....$...,...
0x0xbfb3f28c: c8 42 6a b7 21 38 7e b7 ff ff ff ff f4 bf 7e b7  .Bj.!8..........

说明:

get_struct函数中的语句memcpy(&bufptr, myptr->valFoo, sizeof(myptr->valFoo));只能在64位系统正常运行,因为StructInfo_t定义字段大小时,是按最大原则的,即存储指针使用8字节。而在32位系统中,bufptr只有4字节,因此必须使用memcpy(&bufptr, myptr->valFoo, sizeof(char*));方可适用于不同位数系统。

小结

经测试,上文所述方案可解决所遇问题。但效果如何,待后续观察。

猜你喜欢

转载自blog.csdn.net/subfate/article/details/133377518