В С ++ основная функция является точкой входа в программу, как я могу изменить ее на другую функцию? - PullRequest
28 голосов
/ 20 октября 2010

Мне задали вопрос для интервью, чтобы изменить точку входа программы на C или C ++ с main() на любую другую функцию. Как это возможно?

Ответы [ 13 ]

43 голосов
/ 20 октября 2010

В стандарте C (и, я полагаю, C ++) вы не можете, по крайней мере, не для размещенной среды (но см. Ниже). Стандарт определяет, что отправной точкой для кода C является main. Стандарт (c99) не оставляет много места для аргументов:

5.1.2.2.1 Запуск программы: (1) Функция, вызываемая при запуске программы, называется основной.

Вот и все. Затем он немного размышляет о параметрах и возвращаемых значениях, но там действительно нет никакой возможности для изменения имени.

Это для размещенной среды. Стандарт также предусматривает автономную среду (т. Е. Не ОС, для таких вещей, как встроенные системы). Для автономной среды:

В автономной среде (в которой выполнение программы на С может происходить без какой-либо выгоды от операционной системы), имя и тип функции, вызываемой при запуске программы, определяются реализацией. Любые библиотечные средства, доступные для автономной программы, кроме минимального набора, требуемого пунктом 4, определяются реализацией.

Вы можете использовать "хитрость" в реализациях C , чтобы вы могли сделать так, чтобы main не было точкой входа. Это на самом деле то, что делали ранние компиляторы Windows, чтобы отметить WinMain в качестве начальной точки.


Первый способ: компоновщик может включить некоторый предварительный стартовый код в файл, подобный start.o, и именно этот фрагмент кода запускается для настройки среды C, а затем вызывает main. Ничто не помешает вам заменить это чем-то, что вызывает bob.


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


Третий способ: вы можете связать этот кусок кода:

int main (int c, char *v[]) { return bob (c, v); }

, а затем ваша точка входа для вашего кода, по-видимому, bob, а не main.


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

Я хотел бы спросить интервьюера: с чего бы вам хотеть сделать это?

10 голосов
/ 30 августа 2012

Точка входа на самом деле является функцией _start (реализовано в crt1.o ).

Функция _start подготавливает аргументы командной строки, а затем вызывает main(int argc,char* argv[], char* env[]), Вы можете изменить точку входа с _start на mystart, установив параметр компоновщика:

g++ file.o -Wl,-emystart -o runme

Конечно, это замена для точки входа _start, поэтому вы не получите аргументы командной строки:

void mystart(){

}

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

9 голосов
/ 20 октября 2010

Из стандартных документов C ++ 3.6.1 Основная функция ,

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

Итак, зависит от вашего компилятора /компоновщик ...

7 голосов
/ 20 октября 2010

Если вы используете VS2010, это может дать вам некоторое представление

Как легко понять, это не предписано стандартом C ++ и относится к области реализацииспецифическое поведение ».

4 голосов
/ 29 июня 2016

Это очень умозрительно, но у вас может быть статический инициализатор вместо основного:

#include <iostream>

int mymain()
{
    std::cout << "mymain";
    exit(0);
}

static int sRetVal = mymain();

int main()
{
    std::cout << "never get here";
}

Вы могли бы даже сделать это 'Java-подобным', поместив материал в конструктор:

#include <iostream>

class MyApplication
{
public:
    MyApplication()
    {
        std::cout << "mymain";
        exit(0);
    }
};

static MyApplication sMyApplication;

int main()
{
    std::cout << "never get here";
}

Теперь. Интервьюер мог бы подумать об этом, но я лично никогда не использовал бы их. Причины:

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

Тем не менее, я видел, что он используется в производстве вместо init() для инициализаторов библиотеки. Предостережение заключается в том, что в Windows (из опыта) ваша статика в DLL может или не может быть инициализирована в зависимости от использования.

3 голосов
/ 05 октября 2015

С помощью gcc объявите функцию с атрибутом ((конструктор)), и gcc выполнит эту функцию перед любым другим кодом, включая main.

3 голосов
/ 20 октября 2010

Измените объект crt, который фактически вызывает функцию main(), или предоставьте свою собственную (не забудьте отключить связывание с обычной).

2 голосов
/ 01 декабря 2016

Я думаю, что легко удалить нежелательный символ main () из объекта перед связыванием.

К сожалению, опция точки входа для g ++ не работает для меня (двоичный файл падает перед входом в точку входа). Поэтому я удаляю нежелательную точку входа из объектного файла.

Предположим, у нас есть два источника, которые содержат функцию точки входа.

  1. target.c содержит main (), который нам не нужен.
  2. our_code.c содержит testmain (), которым мы хотим быть точкой входа.

После компиляции (опция g ++ -c) мы можем получить следующие объектные файлы.

  1. target.o, который содержит main (), который нам не нужен.
  2. our_code.o, который содержит testmain (), мы хотим быть точкой входа.

Таким образом, мы можем использовать objcopy для удаления нежелательной функции main ().

objcopy --strip-symbol = main target.o

Мы можем переопределить testmain () и main (), используя objcopy.

objcopy --redefine-sym testmain = main our_code.o

И тогда мы можем связать их обоих в двоичный файл.

g ++ target.o our_code.o -o our_binary.bin

Это работает для меня. Теперь, когда мы запускаем our_binary.bin, точка входа - это символ our_code.o:main(), который относится к функции our_code.c::testmain().

2 голосов
/ 27 ноября 2013

Это очень просто:

Как вы должны знать, когда вы используете константы в c, компилятор выполняет своего рода «макрос», изменяя имя константы для соответствующего значения.

просто включите аргумент #define в начало вашего кода с именем функции запуска, за которым следует имя main:

Пример:

#define my_start-up_function (main)
2 голосов
/ 20 октября 2010

Для систем на базе Solaris я нашел это .Вы можете использовать раздел .init для каждой платформы, которую я предполагаю:

   pragma init (function [, function]...)

Источник:

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

...