Как выполнить некоторый код перед вводом подпрограммы main () в VC? - PullRequest
7 голосов
/ 08 апреля 2009

Я читаю исходный код Microsoft CRT и могу придумать следующий код, где функция __initstdio1 будет выполняться перед процедурой main ().

Вопрос в том, как выполнить некоторый код перед вводом подпрограммы main () в VC (не код VC ++)?

#include <stdio.h>

#pragma section(".CRT$XIC",long,read)

int __cdecl __initstdio1(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 10;
    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}

Вывод будет:

Some code before main!
z = 10
End!

Однако я не могу понять код.

Я немного поработал с Google .CRT $ XIC, но удача не найдена. Может ли какой-нибудь эксперт объяснить мне приведенный выше фрагмент кода, особенно следующее:

  1. Что означает эта строка _CRTALLOC(".CRT$XIC") static pinit = __initstdio1;? Каково значение переменной пинит?
  2. Во время компиляции компилятор (cl.exe) выдает предупреждение, как показано ниже:

32-разрядный оптимизирующий компилятор C / C ++ Microsoft (R) версии 15.00.30729.01 для 80x86 Авторское право (C) Microsoft Corporation. Все права защищены.

stdmacro.c
stdmacro.c(9) : warning C4047: 'initializing' : 'int' differs in levels of indirection from 'int (__
cdecl *)(void)'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:stdmacro.exe
stdmacro.obj

Какие корректирующие действия необходимо предпринять для удаления предупреждающего сообщения?

Заранее спасибо.


Добавлено:

Я изменил код и ввел тип для обозначения _PIFV. Теперь предупреждающее сообщение исчезло.

Новый код выглядит следующим образом:

#include <stdio.h>

#pragma section(".CRT$XIC1",long,read)

int __cdecl __initstdio1(void);

typedef int  (__cdecl *_PIFV)(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC1") static _PIFV pinit1 = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 100;

    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}

Ответы [ 6 ]

5 голосов
/ 08 апреля 2009

Простой способ сделать это.

#include <iostream>

int before_main()
{
    std::cout << "before main" << std::endl;
    return 0;
}

static int n = before_main();

void main(int argc, char* argv[])
{
    std::cout << "in main" << std::endl;
}
4 голосов
/ 08 апреля 2009

Это то, что _CRTALLOC определяется как:

extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[];// C initializers
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];// C++ initializers

Это таблица вещей для предварительной инициализации, из которой помещен указатель на вашу функцию __initstdio1.

На этой странице описана инициализация CRT:

http://msdn.microsoft.com/en-us/library/bb918180.aspx

3 голосов
/ 08 апреля 2009

По крайней мере, в C ++ вам не нужны все эти специфические для реализации вещи:

#include <iostream>

struct A {
   A() { std::cout << "before main" << std::endl; }
};

A a;

int main() {
   std::cout << "in main" << std::endl;
}
2 голосов
/ 11 марта 2010

Я недавно написал отмеченную наградами статью об этом в CodeGuru.

1 голос
/ 09 апреля 2009

Даже в C необходимо, чтобы какой-то код выполнялся до ввода main(), чтобы преобразовать командную строку в соглашение о вызовах C. На практике стандартная библиотека требует некоторой инициализации, и точные потребности могут варьироваться от компиляции к компиляции.

Истинная точка входа в программу устанавливается во время соединения и обычно находится в модуле с именем, подобным crt0 по историческим причинам. Как вы обнаружили, исходный код этого модуля доступен в источниках crt.

Для поддержки инициализаций, обнаруженных во время соединения, используется специальный сегмент. Его структура представляет собой список указателей на функции с фиксированной сигнатурой, которые будут повторяться в начале crt0 и вызывать каждую функцию. Этот же массив (или очень похожий) указателей на функцию используется в ссылке на C ++ для хранения указателей на конструкторы глобальных объектов.

Массив заполняется компоновщиком, позволяя каждому связанному модулю включать в него данные, которые объединяются вместе для формирования сегмента в готовом исполняемом файле. Единственное значение для переменной pinit заключается в том, что она объявлена ​​(макросом _CRTALLOC()) для размещения в этом сегменте и инициализирована по адресу функции, вызываемой во время запуска Си.

Очевидно, детали этого чрезвычайно специфичны для платформы. Для общего программирования вам, вероятно, лучше обернуть инициализацию и текущую основную часть в новый main():

int main(int argc, char **argv) {
    early_init();
    init_that_modifies_argv(&argc, &argv);
    // other pre-main initializations...
    return real_main(argc,argv);
}

Для специальных целей наилучшим ответом может быть изменение самого модуля crt0 или выполнение специфических для компилятора приемов для вызова дополнительных функций ранней инициализации. Например, при создании встроенных систем, которые запускаются из ПЗУ без загрузчика операционной системы, обычно необходимо настроить поведение модуля crt0, чтобы вообще иметь стек для передачи параметров в main() , В этом случае, возможно, нет лучшего решения, чем модифицировать crt0 для инициализации аппаратного обеспечения памяти в соответствии с вашими потребностями.

1 голос
/ 08 апреля 2009

Здесь есть некоторая информация (поиск по ЭЛТ). Значение переменной pinit не имеет значения, это просто фрагмент данных, помещенный в исполняемый файл, где среда выполнения может его найти. Тем не менее, я бы посоветовал вам дать ему тип, например:

_CRTALLOC(".CRT$XIC") static void (*pinit)()=...

Предупреждение компоновщика, вероятно, просто предупреждает вас, что у вас есть функция, которая имеет int тип возвращаемого значения, но ничего не возвращает (вероятно, вам лучше изменить тип возвращаемого значения на void).

...