我正在尝试使用超出结构大小分配的内存来模仿“有效负载” 并允许该有效负载包含指向另一个结构的指针。有人能告诉我这是否可能,或者我想做的事情是不可行的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define ptrsize sizeof(char*)
typedef struct s{
int i;
short j;
long k;
}S;
S *salloc(int sz,int i,short j,long k){
S *m=malloc(sizeof(S)+sz);
m->i=i;m->j=j;m->k=k;
return m;
}
char *goToData(S *m){
char* dataloc=(char*)m+sizeof(S);
return dataloc;
}
int main(int argc,char **argv){
char a[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
S *mys=salloc(26,2,3,100); // struct * to size 26+sizeof(S) & set struct vars
char *mydp=goToData(mys); // get the address of the payload
memcpy(mydp,a,sizeof(a)); // copy a into the payload
S *mysc=salloc(ptrsize,1,2,3); // allocate a container struct
char *datapw=goToData(mysc); // go to the first byte of the payload of mysc
(*(S**)datapw)=mys; // want to point at mys -- is this possible?
printf("%d\n",ptrsize);
printf("addr mys %x\n",(unsigned int)mys);
printf("addr mysc %x\n",(unsigned int)mysc);
printf("addr mysc | *datapw %x\n",(unsigned int)*datapw); // from here would like to be indirectly reference mys
return 0;
}
答案 0 :(得分:1)
goToData()
功能似乎是合理的。内存分配是合理的,但是您没有记录在结构之后分配的空间大小。
这里有一个缓冲区溢出:
char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
S *mys = salloc(26,2,3,100); // struct * to size 26+sizeof(S) & set struct vars
char *mydp = goToData(mys); // get the address of the payload
memcpy(mydp, a, sizeof(a)); // copy a into the payload
您分配了26个字节,但是您要复制27个字段(sizeof(a) == 27
,因为sizeof()
计算结尾处的NUL '\0'
。这是灾难的秘诀。不要在C中使用优雅的变化;使用一致性。在两个地方都使用26或sizeof(a)
,但不能混合使用。
如果出现问题,请使用以下行:
(*(S**)datapw)=mys; // want to point at mys -- is this possible?
我甚至不确定我明白你在这里要做什么,但它看起来并不好看。
虽然 datapw
已对齐以用作S *
,但您尚未为其分配足够的空间。在解除引用之前,我不清楚你应该将它转换为S**
。
如果你试图在mysc
指向的结构包含指向mys
所指向的结构的指针后创建空格,那么你最好有一个很好的理由不包括指针在结构中。 真的,非常很好的理由。
然而,尽管我有很强烈的疑虑,但该代码仍然准确无误。但它非常不透明。
我认为你应该得到你想要的结果:
*((S **)datapw) = mys;
我讨厌考虑严格别名的含义,尽管你可能没问题,因为对char *
有某种豁免。
所以,重新审视这个陈述并找出你想要做的事情 - 因为你所写的不是这样做的,无论它是什么。
printf("addr mysc | *datapw %x\n",(unsigned int)*datapw);
这有一些问题。由于datapw
是char *
,*datapw
将成为一个角色。可能不是你的想法。我认为(unsigned int)*(S **)
是您所需要的。
剩下的就是OK-ish,尽管在64位系统上,地址对%x
来说太大了。您将使用32位系统,但是您应该%p
使用(void *)
强制转换,或"%" PRIXPTR
来自<inttypes.h>
(uintptr_t)
铸造。
我最终得到了:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define ptrsize sizeof(char*)
typedef struct s
{
int i;
short j;
long k;
} S;
static S *salloc(int sz, int i, short j, long k)
{
S *m = malloc(sizeof(S) + sz);
m->i = i;
m->j = j;
m->k = k;
return m;
}
static char *goToData(S *m)
{
char *dataloc = (char*)m + sizeof(S);
return dataloc;
}
int main(void)
{
char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
S *mys = salloc(sizeof(a), 2, 3, 100); // struct * to size 26+sizeof(S) & set struct vars
char *mydp = goToData(mys); // get the address of the payload
memcpy(mydp, a, sizeof(a)); // copy a into the payload
S *mysc = salloc(ptrsize, 1, 2, 3); // allocate a container struct
char *datapw = goToData(mysc); // go to the first byte of the payload of mysc
*((S **)datapw) = mys; // want to point at mys -- is this possible?
printf("%zu\n", ptrsize);
printf("addr mys %" PRIXPTR "\n", (uintptr_t)mys);
printf("addr mysc %" PRIXPTR "\n", (uintptr_t)mysc);
printf("addr mysc | *datapw %" PRIXPTR "\n", (uintptr_t)*(S **)datapw);
return 0;
}
(我用-Wmissing-prototypes
编译;函数前面的static
阻止编译器警告我这些函数。由于你没有使用main()
的参数,我更换了出于同样的原因,argc
和argv
与void
- 以避免编译器警告。)
当我在Mac上的valgrind
下运行它(MacOS X 10.7.2,GCC 4.2.1,Valgrind 3.7.0)时,我得到了一个干净的运行数据输出:
8
addr mys 100005120
addr mysc 100005190
addr mysc | *datapw 100005120
答案 1 :(得分:1)
关于接受的答案,我会将结构更改为:
typedef struct s{
int i;
short j;
long k;
char data[1]; //<- start of optional data
}S;
static S *salloc(int sz, int i, short j, long k)
{
S *m = malloc((sizeof(S)-1) + sz );
m->i = i;
m->j = j;
m->k = k;
return m;
}
然后你不需要goToData函数,因为S已经有一个指向它的指针。
int main(void)
{
char a[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
S *mys = salloc(sizeof(a), 2, 3, 100); // struct * to size 26+sizeof(S) & set struct vars
memcpy(&mys->data, a, sizeof(a)); // copy a into the payload
S *mysc = salloc(ptrsize, 1, 2, 3); // allocate a container struct
char *datapw = &mysc->data; // go to the first byte of the payload of mysc
*((S **)datapw) = mys; // want to point at mys -- is this possible?
printf("%zu\n", ptrsize);
printf("addr mys %" PRIXPTR "\n", (uintptr_t)mys);
printf("addr mysc %" PRIXPTR "\n", (uintptr_t)mysc);
printf("addr mysc | *datapw %" PRIXPTR "\n", (uintptr_t)*(S **)datapw);
return 0;
}
我记得一个非常古老的Microsoft DOS编译器支持:
typedef struct s{
int i;
short j;
long k;
char data[0]; //<- start of optional data
}S;
这意味着sizeof(S)不包含可选数据,但是为您提供了指向数据开头的指针供您使用。