Использование структуры C без включения заголовочного файла - PullRequest
2 голосов
/ 02 июня 2010

Моя основная проблема заключается в том, что я хочу использовать некоторые структуры и функции, определенные в заголовочном файле, не включая этот заголовочный файл в мой код.

Файл заголовка генерируется инструментом. Поскольку у меня нет доступа к заголовочному файлу, я не могу включить его в свою программу.

Вот простой пример моего сценария:

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 ]

7 голосов
/ 02 июня 2010

Чтобы использовать структуру в my_program.c, структура должна быть определена в my_program.c. Там нет никакого способа обойти это.

Чтобы определить его, вы должны либо включить first.h, либо предоставить определение Complex в my_program.c другим способом (например, скопировать и вставить определение Complex в my_program.c).

Если ваш first.h выглядит так, как вы опубликовали, тогда, конечно, нет никакого смысла в копировании, поскольку в любом случае это будет одно и то же. Просто включите ваш first.h.

Если вы не хотите включать first.h из-за чего-то еще в этом заголовке (что здесь не показано), вы можете переместить определение Complex в отдельный маленький заголовок и включить его вместо в обоих местах.

1 голос
/ 02 июня 2010

Я изменил файлы для использования указателей и прямых ссылок и заставил их работать.

Теперь я собираюсь проверить созданный заголовочный файл, чтобы выяснить, нужно ли мне использовать какую-либо функцию, которая не принимает указатели в качестве аргументов.

Вот код, который я попробовал:

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)
1 голос
/ 02 июня 2010

Вы можете обернуть весь доступ к членам структуры с помощью функций из first.c, затем объявить структуру struct (struct ComplexS) в first.c и my_program.c (или общий заголовок) и получить доступ к структуре только через указатель в my_program.c (все функции в first.c будут работать с указателями структуры).

Тогда ваша программа должна знать только предварительное объявление, а не члены структуры.

Когда вы закончите, 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;
}
1 голос
/ 02 июня 2010

Если вы знаете, как размещаются данные (и знаете, так как вы их показали нам), вы можете скопировать их в свой файл!Если вы не хотите #include, скопируйте и вставьте!Если это результат обратного инжиниринга данных или чего-то еще, вам все равно нужно сделать struct с вашими догадками, чтобы дать компилятору доступ к данным должным образом.

С другой стороны, если вы не знаетеНесмотря на то, что данные хранятся в структуре, компилятор не может узнать это за вас.

0 голосов
/ 02 июня 2010

В first.h / first.c объявите и определите add на основе Complex *. Также объявите инициализацию и распечатайте подпрограммы на основе Complex * в my_program.c - идея здесь заключается в том, чтобы не использовать 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;
}
0 голосов
/ 02 июня 2010

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я бы никогда так не написал код IRL.

Хорошо, вот хак, о котором я говорил. Просто не используйте его в каком-либо коде, который будет важен удаленно, поскольку он не всегда будет работать, но вы увидите идею организации памяти после просмотра. Я рекомендую скомпилировать их в сборку с помощью 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
0 голосов
/ 02 июня 2010

Если вы хотите использовать структуру, вы должны определить ее где-нибудь. В противном случае компилятору не хватит информации для сборки программы (он даже не знает, сколько памяти требуется объекту 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), выдаст ошибку компилятора. Чтобы получить доступ к внутренним элементам структуры с помощью точечной нотации, вам необходимо полное определение структуры.

Если first.c будет скомпилирован в библиотеку, с которой будет ссылаться ваш код, то вы можете внести следующие изменения, чтобы позволить вам использовать функцию add без использования first.h:

/* 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, не зная ничего о внутренних элементах структуры (кроме размера).

...