在运行时调整 char[x] 的大小为 char[y]

3

好的,我希望我能正确地解释这个问题。 我有一个结构体:

typedef struct _MyData
{
   char Data[256];
   int  Index;
} MyData;

现在,我遇到了一个问题。大多数情况下,MyData.Data可以容纳256个字符,但在某些情况下,我需要扩展它所能容纳的字符数量以达到不同的大小。我不能使用指针。有没有办法在运行时调整Data的大小?怎么做?非常感谢提供代码。
请注意:
  1. 我不能使用指针,请不要试图弄清为什么,我只是不能。
  2. 结构体被注入到另一个程序的内存中,因此不能使用指针。
抱歉有点严厉,但我之所以在这里提问,是因为我已经尝试了所有可能行得通的方法。再次说明,我正在寻找代码。目前我对“可能行得通...”或“你考虑过这个...”等内容不感兴趣。感谢您的理解和支持。

13
为什么不能使用指针?动态分配数组是唯一的方法来实现这个。 - anon
1
要么静态地为数据输入保留足够的空间,要么就必须使用指针来动态分配空间。实际上没有其他选择。 - Falaina
在哪里?如果您认为您有答案,请发布一个可工作的代码示例。谢谢。 - wonderer
3
数组的好处在于它们不指向数据。因此,如果你将一个数组放入结构体中,数据将从数组开始位置开始,使得可以将结构体和其中包含的数组作为一个单独的单位来发送来回处理。如果你在那里放一个指针,这将会破坏它的连续性。你应该不再把数组看成指针。你可以写 char *ptr = array;,但这并不代表任何事情:你也可以写 char *ptr = 0;,但整数和指针是两个完全不同的东西。 - Johannes Schaub - litb
1
并不建议使用指针。char Data[];不是指针。你在哪里看到我在我的主要建议中建议使用指针了呢?嗯,无意冒犯,但我对这个问题也已经结束了,除非你问一些更有意义的问题。我不会编写一个多线程+分布式应用程序,只是为了向你展示它的工作原理。 - Johannes Schaub - litb
显示剩余16条评论
11个回答

19

你可以使用可变长数组成员

typedef struct _MyData
{
   int  Index;
   char Data[];
} MyData;

这样你就可以分配正确的空间。

MyData *d = malloc(sizeof *d + sizeof(char[100]));
d->Data[0..99] = ...;

稍后,您可以释放并分配另一个内存块,并将指向MyData的指针指向它,这时灵活数组成员中将有更多/少的元素(realloc)。请注意,您还必须在某个地方保存长度。

在C99之前,在不使用灵活数组成员的情况下,char Data[]被视为具有不完整类型的数组,编译器会报错。这里我建议您有两种可能的方法:

  • 使用指针:char *Data并使其指向分配的内存。这不像使用嵌入式数组那样方便,因为您可能需要进行两次分配:一次为结构体,一次为指针所指向的内存。如果您的程序允许,在栈上也可以分配结构体。
  • 使用char Data[1],但是将其视为更大的数组,以便它覆盖整个分配的对象。这在形式上是未定义的行为,但是这是常用技术,所以在您的编译器中可能是安全的。

你为什么把这个设为答案?它不起作用,会崩溃。 - wonderer
@wonderer,这个答案有什么问题吗?它建议使用一个数组(“char Data [];”)。你通过不接受其他任何东西而接受了这个答案 :) 如果对我的答案有任何不清楚的地方,请问一下,我会看看我们能做些什么。 - Johannes Schaub - litb
正如我在问题和后续评论中所写,我不能使用任何类型的指针。原因(再次强调)是因为我将该结构体传递到另一个进程内存中,无法访问指针指向的数据。你还想让我解释什么? - wonderer
我在之前的评论中已经向您解释了。我正在将该结构体注入到另一个进程中。除非我传递所有数据而不使用指针,否则我将无法访问它。所以,除非我的英语很糟糕(可能是这样),否则我认为我已经非常清楚地解释了为什么会失败。但无论如何,我已经完成了。我将把这个答案视为不可能完成的任务。所以你想做什么就做吧。 - wonderer
问题就像我在答案中总结的那样;所讨论的结构体被用于两个进程之间的通信,而且只有其中一个进程可以修改它... 因此对该结构体的任何更改都是不可接受的。 - Chris Arguin
显示剩余2条评论

13

这里的问题在于你的陈述“我无法使用指针”。你必须使用指针,这会使一切变得更加容易。嘿,realloc甚至会复制你现有的数据,你还想要什么?

那么你为什么认为自己不能使用指针呢?最好试着解决这个问题。


+1,没有指针是无法完成的。只有使用malloc、realloc等函数才能实现。真正的问题是为什么没有使用指针。 - Clement Herreman
1
因为我正在将这些数据传递给另一个进程,而所有内容都必须位于该结构内。一旦我传递了这些数据,另一个进程就必须能够访问它,但是使用指针无法实现,因为这两个进程不共享内存。在该结构中使用指针时,程序会崩溃。 - wonderer
7
@wonderer,每种IPC机制都有一些传递可变量数据的方式。你应该研究如何在你使用的机制中实现这个功能。 - Nick Meyer
问题是我们正在处理一个不再受支持的程序,所以我找到的唯一执行某些代码的方法是通过DLL注入。注入基本上在程序的主调用执行后执行,并在“遗留”软件内部执行特定函数时执行注册表清理。我需要传递结构中包含的所有数据,否则注入的程序(遗留软件)将不知道它们的位置。 - wonderer
@wonderer:按照litb的建议去做,你注入的代码只需要知道结构体从哪里开始即可。 - bdonlan
如果你正在处理一个期望看到512个字符缓冲区的API,那么你就被限制在512个字符上了,仅此而已。对此你无能为力。 - Pavel Minaev

5
你需要重新调整结构,就像这样。
typedef struct _MyData
{
   int  Index;
   char Data[256];
} MyData;

使用malloc/realloc分配内存实例,如下:

my_data = (MyData*) malloc ( sizeof(MyData) + extra_space_needed );

这是一种不太好的方法,我不建议使用(我会使用指针),但它回答了你如何在没有指针的情况下完成它的问题。

一个限制是它只允许每个结构体有一个可变大小的成员,并且必须位于末尾。


是的,那样做可以运行,但可能会遇到很多问题。例如,sizeof(MyData)不再正确,因此默认的按值复制操作将会截断数据至256字节。 - Kip
4
如果这是C语言,请勿对malloc的返回值进行强制类型转换。可以参考此链接:http://c-faq.com/malloc/mallocnocast.html。 - Sinan Ünür

5
让我总结一下这个帖子中我看到的两个重要观点:
  1. 该结构用于通过某些IPC机制在两个程序之间进行交互
  2. 目标程序无法更改
因此,您不能以任何方式更改该结构,因为目标程序被卡在尝试按当前定义读取它。很遗憾,您被卡住了。
您可以尝试找到获取等效行为的方法,或者找到一些邪恶的黑客来强制目标程序读取新结构(例如修改可执行文件中的二进制偏移量)。这都是相当特定于应用程序的,所以我不能给出比这更好的指导。
您可以考虑编写第三个程序来充当两个程序之间的接口。它可以接收“长”消息并对其进行处理,然后将“短”消息传递给旧程序。您可以相当轻松地将其注入到IPC机制之间。

1
@wonderer:实际上一直错的是你。这是你问题的唯一答案 - 你所要求的是不可能完成的。你应该把Chris的答案标记为正确答案。 - Kyselejsyreček

2
您可以这样做,而无需为数组分配指针:
typedef struct _MyData
{
    int Index;
    char Data[1];
} MyData;

稍后,您可以像这样分配:

int bcount = 256;
MyData *foo;

foo = (MyData *)malloc(sizeof(*foo) + bcount);

realloc:

int newbcount = 512;
MyData *resized_foo;

resized_foo = realloc((void *)foo, sizeof(*foo) + newbcount);

在C语言中,执行char Data[0];是不合法的。 - Johannes Schaub - litb

2

从你所说的来看,你肯定需要将MyData作为静态数据块保留。在这种情况下,我认为唯一的选择是以某种方式(可选地)将这些数据结构链接在一起,以便其他进程可以重新组装。

你需要在MyData中添加一个额外的成员,例如:

typedef struct _MyData
{
   int  Sequence;
   char Data[256];
   int  Index;
} MyData;

其中,Sequence 表示重新组装数据的降序序列(序列号为零表示最后一个数据缓冲区)。


我很难理解你的概念。你能否发布一些代码并解释一下大致的想法? - wonderer
我是说你可以将这些数据缓冲区链接在一起,以容纳所需的额外数据。例如,要传递一个大小为600字节的数据,您需要调用您的函数(在单独的进程中)3次:序列2:256字节 序列1:256字节 序列0:88字节 - Alan

2
问题在于你提出问题的方式。不要考虑C语言的语法,而是像黑客一样思考。请详细解释一下你目前如何在正确的时间将数据传输到另一个进程中,并且还要说明其他程序如何知道数据的起始和结束位置。另一个程序是否期望接收以null结尾的字符串?如果你使用char[300]声明你的结构体,那么另一个程序会崩溃吗?
你看,当你说“传递数据”给另一个程序时,你可能会[a]欺骗其他进程复制你放在它前面的内容,[b]欺骗其他程序让你覆盖其通常“私有”的内存,或者[c]采用其他方法。无论哪种情况,如果其他程序可以接收你的大量数据,那么一定有办法将其传输给它们。

好的,考虑代码注入。简单来说,将数据放入一个结构体中。分配内存,写入内存,在被注入的应用程序中创建远程线程并运行线程。 - wonderer
如果你仍然在关注这个线程...... 我们在这里是一致的,我想你正在使用CreateRemoteThread()。但我想问的是:你如何让目标程序查看你的数据?你是否在将数据复制到它们的数据区域中?还是覆盖了它们指向数据的一个指针? - egrunin

0

我觉得 KIV 的技巧非常有用。但是,我建议先调查指针问题。

如果你看一下 malloc 的实现(可以参考这篇 IBM 文章,第 5 部分:主要分配器的伪代码),当你分配内存时,内存管理器会分配控制头,并根据你请求的大小分配后续可用空间。这很像在说:

typedef struct _MyData
{
   int  size;
   char Data[1]; // we are going to break the array-bound up-to size length
} MyData;

现在,您的问题是
如何将这样一个大小不匹配的结构传递给另一个进程?

这就带来了一个问题
另一个进程如何确定此数据的大小?
我期望通信中有一个长度字段。

如果您已经解决了以上问题,那么将指向该结构的指针传递给另一个进程有什么问题吗?
另一个进程能否区分指向结构和指向分配内存的指针之间的区别?


感谢提供代码。问题在于我们正在处理一个不再受支持的程序,所以我找到的唯一执行某些代码的方法是通过简单的DLL注入。注入基本上是在主调用程序执行后执行,并且在“遗留”软件内部执行某个函数时还执行注册表清理。因此,除非您能够为注入的函数传递指针的正确方法,否则我无法使用它们,因为它们会使程序崩溃。 - wonderer
我觉得我开始明白你想做什么了。问题是,你似乎在尝试一种“利用”方式来实现你的目标。这将非常依赖于这个遗留程序的编写方式。我无法想到任何具体的问题来问你,以帮助我弄清楚这里的任何方案 :-( - nik
使用类似FARPROC的东西,将指针地址传递到已注入代码中并进行获取怎么样? - wonderer

0

你不能手动重新定位。

当我在处理简单的数据存储系统(非常简单的文件系统)时,我使用了一些技巧,你也可以尝试一下。

typedef struct
{
    int index ;     
    char x[250];
} data_ztorage_250_char;

typedef struct
{
    int index;      
    char x[1000];
} data_ztorage_1000_char;

int main(void)
{
      char just_raw_data[sizeof(data_ztorage_1000_char)];
      data_ztorage_1000_char* big_struct;
      data_ztorage_250_char* small_struct;
      big_struct = (data_ztorage_1000_char*)big_struct; //now you have bigg struct
      // notice that upper line is same as writing 
      // big_struct = (data_ztorage_1000_char*)(&just_raw_data[0]);

      small_struct = (data_ztorage_250_char*)just_raw_data;//now you have small struct

      //both structs starts at same locations and they share same memory
     //addresing data is 
      small_struct -> index = 250;
}

正如我所说,我不能使用指针。我正在通过注入将该结构体(其中可能声明了不止一个int和char)传递给另一个程序(该程序不再受支持,因此我们无法更改它,我正在使用注入在特定位置执行代码)。因此,我发送的所有内容都必须使用正确的右侧声明,而不能使用指针。 - wonderer
在这种情况下,你是做不到的。结构体本身并没有写明Data的大小。结构体定义只是在数据开头和整数之间定义了位置,并且在Data[256]、Data[257]、Data[258]、Data[259]上进行写入将覆盖索引。在这种情况下,仅使用链接器,您无法告诉程序现在您的整数基于Data的不同偏移量。 - Luka Rahne

0

有没有一个非常简单的解决方案?你能这样做吗:

typedef struct _MyData
{
   char Data[1024];
   int  Index;
} MyData;

我有一种感觉,你的回答会是“不行,因为我无法控制另一个程序,它期望256个字节”... 如果这确实是你对我的回答,那么我的回答就是:这是不可能的。


不,我的回答将是:如果明天我需要传递2048怎么办?第二天需要传递100000000又该怎么办? - wonderer
使其成为最大可想象的尺寸; 这将成为您代码的限制。 - Josh
当然,添加错误检查,以便如果您需要传递的数据是一天100000000,则不会覆盖您不应该覆盖的内存。当那一天到来时,请增加Data的大小。 - Josh
基本上,我会分配大量内存,并将其注入到另一个进程中,即使我只需要发送几百千字节。 - wonderer
不幸的是,其他人的建议都被拒绝了,这是我能想到的最好的! - Josh
告诉我没有答案也是一个好答案。如果不能做到,那么我也无能为力。 - wonderer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接