c - 在不动态分配内存的情况下在C中复制任意类型

标签 c arrays pointers language-lawyer memcpy

问题:

我想我已经想出了一种方法,据我所知,它几乎允许您编写完全与类型无关的代码,从而在“堆栈”上复制任意类型的变量(用引号引起来,因为C标准实际上不需要有一个堆栈,所以我真正的意思是将它与本地范围内的自动存储类一起复制)。这里是:

/* Save/duplicate thingToCopy */
char copyPtr[sizeof(thingToCopy)];
memcpy(copyPtr, &thingToCopy, sizeof(thingToCopy));

/* modify the thingToCopy variable to do some work; do NOT do operations directly on the data in copyPtr, that's just a "storage bin". */

/* Restore old value of thingToCopy */
memcpy(&thingToCopy, copyPtr, sizeof(thingToCopy));

通过有限的测试,它可以正常运行,并且据我所知,它应该可以在所有符合标准的C实现中运行,但是以防万一我错过了一些东西,我想知道:
  • 这是否完全符合C标准(我认为从C89到现代的东西,这应该一直很好),如果不能,是否可以修复以及如何修复?
  • 为了保持符合标准,此方法对自身施加了哪些使用限制?
  • 例如,据我了解,只要我从不直接使用char-array临时副本,就可以避免对齐问题-就像要存储到memcpy或从中加载的垃圾箱一样。但是我无法将这些地址传递给其他函数,而希望它们指向我正在使用的类型的指针,而不会冒对齐问题的风险(显然,从语法上讲,我可以通过从void *中获取char *来进行错误处理,甚至不指定确切的类型,正在合作,但重点是我认为这样做会触发未定义的行为)。
  • 是否有更干净和/或更高效的方法来实现同一目标?

  • *我的armel v7测试设备上的GCC 4.6.1(具有-O3优化功能)使用对临时变量的常规分配生成了与常规代码相同的代码,但可能是因为我的测试用例非常简单,因此能够弄清楚,如果更广泛地使用此技术,将会感到困惑。

    作为一个额外的利益,我很好奇这是否会在大多数与C兼容的语言中崩溃(我所知道的是C++,Objective-C,D甚至C#,尽管也欢迎提及其他语言)。

    理由:

    这就是为什么我认为上述方法可行的原因,以防万一您知道我来自哪里,以便解释我可能犯的任何错误:

    C标准的“字节”(按传统意义是“最小的可寻址存储单元”,而不是现代化的“8位”含义)是char类型-sizeof运算符以char单位生成数字。因此,通过在该变量上使用sizeof运算符,我们可以获取该变量类型所需的最小存储空间(可以在C语言中使用)。

    C标准保证几乎所有的指针类型都可以隐式转换为void *(但是如果它们的表示形式不同,则可以改变表示形式(但是,C标准保证void *char *具有相同的表示形式))。

    就语法而言,给定类型的数组的“名称”和指向该相同类型的指针基本上可以相同地对待。
    sizeof运算符是在编译时确定的,因此我们可以执行char foo[sizeof(bar)],而无需依赖有效的不可移植VLA。

    因此,我们应该能够声明一个“字符”数组,该数组是保存给定类型所需的最小大小。

    因此,我们应该能够将要复制的变量的地址和数组的名称传递给memcpy(据我所知,数组名称隐式用作char *到数组的第一个元素)。由于任何指针都可以隐式转换为void *(必须更改表示形式),因此可以使用。

    memcpy应该对要复制到数组中的变量进行逐位复制。不管类型是什么,涉及任何填充位,等等,sizeof保证我们将抓住构成类型的所有位,包括填充。

    由于我们无法显式使用/声明我们刚刚复制的变量的类型,并且由于某些架构可能会对某些类型的对齐要求(这种hack有时可能会违反),因此我们无法直接使用此副本- d必须对其进行memcpy编码回我们从中获得的变量或相同类型之一,以便使用它。但是,一旦我们将其复制回去,我们便得到了放在第一位的确切副本。本质上,我们释放变量本身以用作临时空间。

    动机(或“亲爱的上帝,为什么!?!”):

    我喜欢在有用时编写与类型无关的代码,但是我也喜欢用C进行编码,将两者结合起来主要是在类似于函数的宏中编写通用代码(然后可以通过包装来重新声明类型检查)调用类似函数的宏的函数定义)。可以将其视为C语言中的原始模板。

    完成此操作后,我遇到了需要暂存空间的其他变量的情况,但是由于缺少可移植的typeof()运算符,因此无法在此类“泛型”中声明任何匹配类型的临时变量。宏”代码段。这是我发现的真正便携式解决方案中最接近的东西。

    由于我们可以多次进行此操作(足够大的char数组足以容纳多个副本,或者几个char数组足以容纳一个副本),只要我们能够保持memcpy调用和直接复制指针名称,就在功能上就像拥有任意数量的复制类型的临时变量,同时能够使通用代码类型无关。

    附言为了略微避免可能不可避免的判断失误,我想说的是我确实认识到这是很复杂的问题,并且我只会在实践中将其保留给经过良好测试的库代码,因为它会显着增加有用性,而不会我会定期部署。

    最佳答案

    是的,它有效。是的,它是C89标准。是的,它令人费解。

    小改进

    字节表char[]可以在内存中的任何位置开始。
    根据thingToCopy的内容以及CPU,这可能会导致复制性能不佳。

    如果速度很重要(因为这种操作很少见就不会如此),您可能更喜欢使用intlong longsize_t单位来对齐表格。

    主要限制

    仅当您知道thingToCopy的大小时,您的建议才有效。
    这是一个主要问题:这意味着您的编译器需要知道编译类型为thingToCopy(因此,它不能是incomplete type)。

    因此,以下句子令人不安:

    由于我们无法显式使用/声明我们刚刚复制的变量的类型

    没门。为了编译char copyPtr[sizeof(thingToCopy)];,编译器必须知道什么是thingToCopy,因此它必须可以访问其类型!

    如果您知道,则可以执行以下操作:

    thingToCopy_t save;
    save = thingToCopy;
    /* do some stuff with thingToCopy */
    thingToCopy =  save;
    

    从对齐角度看,它更容易阅读,甚至更好。

    关于c - 在不动态分配内存的情况下在C中复制任意类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32537594/

    相关文章:

    c - 大字符串 C/C++ 的优化

    c - 当我 malloc 错误的内存量时,为什么 C 不会崩溃

    c# - C DLL 不会在 C# 中加载

    c - 获取本地时间(以毫秒为单位)

    javascript - array.toString () Javascript 的逆

    C++ 指向存储乱码的新内存的指针

    c - 两个指针变量之间的差异

    c++ - 区分 C++ 中的 QML 数组和映射/对象类型

    java - StringTokenizer 数组

    用指针改变C字符串值