使用C struct而不包含头文件

时间:2010-06-02 16:38:10

标签: c struct external header-files

我的基本问题是我想通过在我的代码中不包含该头文件来使用头文件中定义的一些结构和函数。

头文件由工具生成。由于我无权访问头文件,因此无法将其包含在我的程序中。

以下是我的方案的一个简单示例:

first.h

#ifndef FIRST_H_GUARD
#define FIRST_H_GUARD
typedef struct ComplexS {
   float real;
   float imag;
} Complex;

Complex add(Complex a, Complex b);

// Other structs and functions
#endif

first.c

#include "first.h"

Complex add(Complex a, Complex b) {
   Complex res;
   res.real = a.real + b.real;
   res.imag = a.imag + b.imag;
   return res;
}

my_program.c

// I cannot/do not want to include the first.h header file here
// but I want to use the structs and functions from the first.h
#include <stdio.h>

int main() {
   Complex a; a.real = 3; a.imag = 4;
   Complex b; b.real = 6; b.imag = 2;

   Complex c = add(a, b);
   printf("Result (%4.2f, %4.2f)\n", c.real, c.imag);

   return 0;
}

我的目的是为my_program构建一个目标文件,然后使用链接器将目标文件链接到一个可执行文件中。我想在C中实现什么?

7 个答案:

答案 0 :(得分:7)

为了在my_program.c中使用结构,结构必须在my_program.c定义。没有办法绕过它。

要定义它,您必须包含first.h或以其他方式在Complex中提供my_program.c的定义(例如复制粘贴{{1的定义)进入Complex)。

如果你的my_program.c看起来像你发布的那样,那么做任何复制粘贴都没有意义,因为它无论如何都是一样的。只需添加first.h

如果由于该标题中的其他内容(您未在此处显示)而不想包含first.h,则可以将first.h的定义移到单独的小标题中,并将其包含在两个地方。

答案 1 :(得分:1)

如果您知道如何放置数据(并且在您向我们展示之后就知道了),您可以在文件中复制它们!如果您不想#include,请复制粘贴!如果它是对数据或其他东西进行逆向工程的结果,那么无论如何都需要struct使用猜测来让编译器正确访问数据。

另一方面,如果你根本不知道数据如何存储在结构中,编译器就无法知道它。

答案 2 :(得分:1)

您可以使用first.c中的函数包含对struct成员的所有访问权限,在first.c和my_program.c(或公共头文件)中转发声明struct(struct ComplexS),并仅访问struct通过my_program.c中的指针(first.c中的所有函数都可以在struct指针上运行)。

然后你的程序只需知道前向声明,而不是结构成员。

完成后,my_program.c可能会显示:

struct ComplexS;
typedef struct ComplexS Complex;

int main() {
   Complex *a = new_complex(3,4);
   Complex *b = new_complex(6,2);
   Complex *c = add_complex(a, b);
   printf("Result (%4.2f, %4.2f)\n", get_real(c), get_imag(c));

   return 0;
}

答案 3 :(得分:1)

我修改了文件以使用指针和转发引用并使其工作。

我现在要检查生成的头文件,看看是否需要使用任何不接受指针作为参数的函数。

这是我最终尝试的代码:

first.h

#ifndef FIRST_H_GUARD
#define FIRST_H_GUARD
typedef struct ComplexS {
   float real;
   float imag;
} Complex;

Complex* new_complex(float a, float b);
Complex* add(Complex* a, Complex* b);
void print_complex(Complex* a);
#endif

first.c

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

Complex* new_complex(float a, float b) {
   Complex* temp = (Complex*)malloc(sizeof(Complex));
   temp->real = a;
   temp->imag = b;
   return temp;
}

Complex* add(Complex* a, Complex* b) {
   Complex *res = new_complex(a->real + b->real, a->imag + b->imag);
   return res;
}

void print_complex(Complex* a) {
   printf("Complex(%4.2f, %4.2f)\n", a->real, a->imag);
}

second.c

#include <stdio.h>

struct ComplexS; // forward declaration
typedef struct ComplexS Complex; 

Complex* new_complex(float a, float b); 
Complex* add(Complex* a, Complex* b); 
void print_complex(Complex* a);

int main() {
   Complex* a = new_complex(3, 4);
   Complex* b = new_complex(6, 2);

   Complex* c = add(a, b);
   print_complex(c);

   return 0;
}

输出:

Complex(9.00, 6.00)

答案 4 :(得分:0)

如果要使用该结构,则必须在某处定义它。否则,编译器没有足够的信息来构建程序(它甚至不知道Complex对象需要多少内存)。仅通过使用结构,它无法弄清楚数据类型是什么样的。

但是,如果你知道结构的大小,你仍然可以使用它,尽管它是有限且有潜在危险的。例如,您可以在C文件中包含以下定义:

typedef char[2*sizeof(float)] Complex;

这将允许您以基本方式使用Complex数据类型(实质上,将其视为一大块原始数据)。您将能够正常访问结构成员,但您可以在函数之间传递Complex个对象(或Complex*指针),将它们读/写到文件中,以及将它们相互比较。如果您很勇敢,可以使用char指针并引用单个字节来访问结构内的数据。小心这样做,因为它需要了解特定编译器如何在内存中布局结构,包括任何对齐/填充字节。这是一个通过字节偏移访问内部成员的示例,假设结构是“打包”(无填充):

typedef char[2*sizeof(float)] Complex;

Complex add(Complex a, Complex b) {
  Complex res;
  float real, imag;
  real = ((float*)&a)[0] + ((float*)&b)[0];
  imag = ((float*)&a)[1] + ((float*)&b)[1];
  ((float*)&res)[0] = real;
  ((float*)&res)[1] = imag;
  return res;
}

只要Complex的定义与示例代码不同,此代码应该为您提供与您发布的代码相同的结果。这样做风险极大,如果你决定这样做,请不要提我的名字。

只有在编译时知道“真实”Complex结构的确切大小,并且只允许对结构进行粗略访问时,此技术才有效。尝试使用点符号(如在first.c中的函数中)使用它的任何代码都将引发编译器错误。要使用点符号访问struct的内部结构,您需要完整的结构定义。

如果要将first.c编译到您的代码将链接的库中,那么您可以进行以下修改以允许您使用add函数而无需使用first.h:< / p>

/* In start.c */
Complex add(Complex* a, Complex* b) {
  Complex res;
  res.real = a->real + b->real;
  res.imag = a->imag + b->imag;
  return res;
}

/* In your code */
typedef char[2*sizeof(float)] Complex;
Complex add(Complex* a, Complex* b);

现在,您应该能够创建Complex类型的对象并将它们来回传递给add函数,而不必了解结构的内部结构(除了大小)

答案 5 :(得分:0)

免责声明:我永远不会以这种方式编写代码。

好的,这是我正在讨论的黑客攻击。只是不要在任何远程重要的代码中使用它,因为它不能保证始终有效,但是你会看到在查看内存后如何组织内存的想法。我鼓励将它们编译为使用gcc -S进行汇编并同时查看它。

root@brian:~# cat struct-hack.c
#include <stdio.h>

struct hack {
        int a, b;
};
int main()
{
        struct hack myhack = { 0xDEAD, 0xBEEF };
        FILE *out = fopen("./out", "w");
        fwrite(&myhack, sizeof(struct hack), 1, out);
        fclose(out);
        return 0;
}
root@brian:~# cat struct-read.c
#include <stdio.h>
#include <stdlib.h>

// in bytes.
#define STRUCT_ADDRESS_MODE     int
#define STRUCT_SIZE     8


int main()
{
        /** THIS IS BAD CODE. DO NOT USE IN ANY REMOTELY SERIOUS PROGRAM. **/

        // Open file
        FILE *in = fopen("./out", "r");
        if(!in) exit(1);

        // We need a way of addressing the structure, an Int is convenient because we
        // know the structure contains a couple ints.
        STRUCT_ADDRESS_MODE *hacked_struct = malloc(STRUCT_SIZE);
        if(!hacked_struct) exit(1);

        fread(hacked_struct, STRUCT_SIZE, 1, in);

        printf ("%x, %x\n", hacked_struct[0], hacked_struct[1]);
        free(hacked_struct);
        fclose(in);
        return 0;
}

root@brian:~# ./struct-hack
root@brian:~# hexdump  -C out
00000000  ad de 00 00 ef be 00 00                           |........|
00000008
root@brian:~# ./struct-read
dead, beef

答案 6 :(得分:0)

在first.h / first.c中声明并定义基于Complex *的add。还要在my_program.c中声明基于Complex *的初始化和打印例程 - 如果你不想明确地包含头或类型声明,那么这里的想法就是不要直接在代码中使用Complex作为类型。

所以这是你的新主页:

#include <stdio.h>

struct Complex; // forward declaration
Complex* initialize(int, int); 
Complex* add(Complex*, Complex*); 
void print_complex(Complex*);

int main() {
   Complex* a = initialize(3, 4); // create new complex no 3 + 4i
   Complex* b = initialize(6, 2);

   Complex* c = add(a, b); // redefined add
   print_complex(c);

   return 0;
}