---
title: Компиляция кода C/C++ в WebAssembly
slug: WebAssembly/C_to_wasm
translation_of: WebAssembly/C_to_wasm
---
<div>{{WebAssemblySidebar}}</div>

<p class="summary">После того как вы написали код на C / C ++, вы можете скомпилировать его в WebAssembly, например, с помощью инструмента <a href="/en-US/docs/Mozilla/Projects/Emscripten">Emscripten</a>. Давайте посмотрим, как это работает.</p>

<h2 id="Подготовка_рабочей_среды_для_Emscripten">Подготовка рабочей среды для Emscripten</h2>

<p>Первым делом установим компоненты для дальнейшей работы.</p>

<h3 id="Необходимые_компоненты">Необходимые компоненты</h3>

<p>Установите Emscripten SDK и настройте рабочее окружение используя инструкции: <a href="https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html">https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html </a></p>

<h2 id="Компиляция_примера">Компиляция примера</h2>

<p>Когда рабочее окружение подготовлено, попробуем собрать пример кода на языке Си при помощи Emscripten. Вам доступно большое количество опций для настройки компиляции, но мы рассмотрим только два основных сценария компиляции с использованием Emscripten:</p>

<ul>
 <li>Компиляция в wasm и создание HTML-страницы для запуска вашего кода, а также JavaScript-кода, необходимого для работы wasm модуля в веб-среде.</li>
 <li>Просто компиляция в wasm и создание JavaScript-кода.</li>
</ul>

<p>Мы рассмотрим оба способа ниже.</p>

<h3 id="Создание_HTML_и_JavaScript">Создание HTML и JavaScript</h3>

<p>Это самый простой способ, который мы рассмотрим. С его помощью вы сможете использовать Emscripten для создания всего что нужно, чтобы ваш код работал в браузере как модуль WebAssembly.</p>

<ol>
 <li>Нам понадобится простой пример для компиляции. Скопируйте следующий код программы на Си и сохраните его в файле  <code>hello.c</code> в новой папке на вашем локальном диске:

  <pre class="brush: cpp">#include &lt;stdio.h&gt;

int main(int argc, char ** argv) {
  printf("Hello World\n");
}</pre>
 </li>
 <li>Теперь, используя терминал, перейдите в каталог, в котором находится ваш файл hello.c, и выполните следующую команду:
  <pre class="brush: bash">emcc hello.c -s WASM=1 -o hello.html</pre>
 </li>
</ol>

<p>Рассмотрим параметры, которые мы передали компилятору:</p>

<ul>
 <li><code>-s WASM=1</code> — Указывает, что мы хотим получить wasm модуль. Если не использовать этот параметр, по умолчанию Emscripten просто создаёт  <a href="http://asmjs.org/">asm.js</a>;</li>
 <li><code>-o hello.html</code> — Указывает, что мы хотим, чтобы Emscripten сгенерировал HTML-страницу <code>hello.html</code> запускающую наш код, а также сам модуль wasm и код JavaScript который позволит использовать модуль в веб-среде.</li>
</ul>

<p>На этом этапе в вашем каталоге должны находится:</p>

<ul>
 <li>Бинарный код модуля wasm (<code>hello.wasm</code>)</li>
 <li>Файл JavaScript, содержащий код связывающий нативные функции Си и JavaScript/wasm (<code>hello.js</code>)</li>
 <li>HTML-страница для загрузки, компиляции и инициализации wasm модуля, и отображающий его вывод в браузере (<code>hello.html</code>)</li>
</ul>

<h3 id="Запуск_вашего_примера">Запуск вашего примера</h3>

<p>Теперь, всё что нужно чтобы запустить полученный  <code>hello.html</code> в браузере, это поддержка WebAssembly. Он включён по умолчанию в Firefox 52+, Chrome 57+ и последних версиях Opera. Также вы можете использовать модули WebAssembly в Firefox 47+, включив флаг <code>javascript.options.wasm</code> в <em>about:config</em>, или в Chrome 51+ и Opera 38+ перейдя в <em>chrome://flags</em> и включив флаг <em>Experimental WebAssembly.</em></p>

<p>Если все работает как планировалось, вы должны увидеть надпись "Hello world" на открывшейся веб-странице и в JavaScript консоли вашего браузера. Поздравляем, вы только что скомпилировали программу на Си в WebAssembly и запустили её в своём браузере!</p>

<div class="note">
<p><strong>Примечание</strong>: На самом деле, если просто открыть полученный <code>hello.html</code>, то ничего работать не будет. Подразумевается что все файлы находятся на веб-сервере и вы запускаете страницу через <code>localhost/hello.html</code>. Для этих целей можно использовать отладочный веб-сервер Emscripten. Чтобы его запустить, откройте терминал, перейдите в каталог, в котором находятся ваши файлы и выполните команду <code>emrun hello.html</code></p>
</div>

<h3 id="Использование_собственного_HTML_шаблона">Использование собственного HTML шаблона</h3>

<p>Вы можете использовать собственный шаблон HTML. Давайте посмотрим, как это сделать:</p>

<ol>
 <li>
  <p>Прежде всего, сохраните следующий код в файле hello2.c в новом каталоге:</p>

  <pre class="brush: cpp">#include &lt;stdio.h&gt;

int main(int argc, char ** argv) {
    printf("Hello World\n");

}</pre>
 </li>
 <li>
  <p>Найдите файл  <code>shell_minimal.html</code> в вашем репозитории emsdk. Скопируйте его в подкаталог <code>html_template</code> внутри вашего нового каталога.</p>
 </li>
 <li>
  <p>Теперь, используя терминал, перейдите в ваш новый каталог и выполните следующую команду:</p>

  <pre class="brush: bash">emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html</pre>

  <p>В этот раз мы использовали немного другие параметры компиляции:</p>

  <ul>
   <li>Мы указали <code>-o hello2.html</code>, чтобы компилятор по прежнему генерировал необходимый JavaScript-код и <code>.html</code> файл.</li>
   <li>Также, мы указали  <code>--shell-file html_template/shell_minimal.html </code>чтобы компилятор использовал ваш шаблон для создания HTML страницы запускающей этот пример.</li>
  </ul>
 </li>
 <li>
  <p>Теперь давайте запустим этот пример. Команда, указанная выше, сгенерирует файл <code>hello2.html</code>, который будет иметь тоже содержание что и шаблон, но с некоторым кодом, добавленным в процесс загрузки сгенерированного wasm, запускающим его и т.д. Откройте его в своём браузере, и вы увидите тот же результат, что и прошлом примере.</p>
 </li>
</ol>

<div class="note">
<p><strong>Примечание</strong>: вы можете указать компилятору создавать только JavaScript-кода, без HTML, используя внутри флага <code>-o</code>, <code>.js</code> вместо <code>.html</code> для формата выходного файла, например <code>emcc -o hello2.js hello2.c -O3 -s WASM=1</code>. После этого вы должны создать свой собственный HTML файл с нуля. Однако так делать не рекомендуется — Emscripten требуется большое количество связывающего кода для обработки операций выделения памяти, утечек памяти и других проблем, которые уже включены в предоставляемый шаблон. Намного легче использовать уже готовое решение, чем создавать свои собственные версии самому.</p>
</div>

<h3 id="Вызов_пользовательской_функции_определённой_в_Си">Вызов пользовательской функции, определённой в Си</h3>

<p>Если у вас есть функция определённая в коде на Си, которую вы хотите по необходимости вызывать из JavaScript, то вы можете использовать для этого функцию  <code>ccall()</code> из Emscripten, и объявление <code>EMSCRIPTEN_KEEPALIVE</code> которое добавит вашу функцию в список экспортируемых функций (см. <a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Почему функции в моем коде исчезают после компиляции и/или я получаю сообщение «Нет функций для обработки»</a>). Давайте посмотрим, как это работает.</p>

<ol>
 <li>
  <p>Для начала сохраните следующий код в файле  <code>hello3.c</code> в новом каталоге:</p>

  <pre class="brush: cpp">#include &lt;stdio.h&gt;
#include &lt;emscripten/emscripten.h&gt;

int main(int argc, char ** argv) {
    printf("Hello World\n");
}

#ifdef __cplusplus
extern "C" {
#endif

void EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) {
  printf("MyFunction Called\n");
}

#ifdef __cplusplus
}
#endif</pre>

  <p>По умолчанию, код созданный Emscripten, всегда просто вызывает функцию  <code>main()</code> , а остальные неиспользуемые функции удаляются. Добавьте определение <code>EMSCRIPTEN_KEEPALIVE</code> перед именем функции чтобы этого не происходило. Также вы должны подключить библиотеку <code>emscripten.h</code> для использования <code>EMSCRIPTEN_KEEPALIVE</code>.</p>

  <div class="note">
  <p><strong>Примечание</strong>: Мы используем блоки <code>#ifdef</code> чтобы, пример оставался рабочим если вы попытаетесь использовать C++ код. Из за различия в правилах преобразования имён между Си и Си++, этот код может сломаться, но мы написали его так, что функция будет рассматриваться как функция Си даже если вы будете использовать Си++.</p>
  </div>
 </li>
 <li>
  <p>Теперь добавьте <code>html_template/shell_minimal.html</code> в ваш новый каталог, просто для удобства. В настоящем проекте стоит размещать его в специально определённый каталог.</p>
 </li>
 <li>
  <p>Теперь снова займёмся этапом компиляции. Внутри вашего последнего каталога, используя терминал, скомпилируйте ваш Си код следующей командой. (Обратите внимание что при компиляции обязательно нужно использовать опцию NO_EXIT_RUNTIME, иначе после выполнения функции <code>main()</code>, рабочий цикл будет завершён. Это приведёт, например, к вызову функции atexits и дальше будет невозможно использовать наш скомпилированный код. Другими словами это необходимо для правильной эмуляции Си.)</p>

  <pre class="brush: bash">emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1  -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]'</pre>
 </li>
 <li>
  <p>Если вы снова запустите пример в своём браузере, вы увидите тоже самое что и до этого!</p>
 </li>
 <li>
  <p>Теперь нам нужно вызвать нашу новую функцию <code>myFunction()</code> из JavaScript. Прежде всего, добавьте {{htmlelement("button")}} как показано ниже, чуть выше первого открывающего тега <code>&lt;script type='text/javascript'&gt;</code>.</p>

  <pre class="brush: html">&lt;button class="mybutton"&gt;Run myFunction&lt;/button&gt;</pre>
 </li>
 <li>
  <p>Теперь добавьте следующий код в конце первого элемента {{htmlelement("script")}} (чуть выше закрывающего тега <code>&lt;/script&gt;</code>):</p>

  <pre class="brush: js">document.querySelector('.mybutton').addEventListener('click', function(){
  alert('check console');
  var result = Module.ccall('myFunction', // name of C function
                             null, // return type
                             null, // argument types
                             null); // arguments
});</pre>
 </li>
</ol>

<p>Это показывает как использовать <code>ccall()</code> для вызова экспортируемой функции.</p>

<h2 id="Смотрите_также">Смотрите также</h2>

<ul>
 <li><a href="http://emscripten.org/">emscripten.org</a> — узнайте больше об Emscripten и разнообразии его настроек.</li>
 <li><a href="https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-compiled-c-functions-from-javascript-using-ccall-cwrap">Calling compiled C functions from JavaScript using ccall/cwrap</a></li>
 <li><a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?</a></li>
 <li><a href="https://research.mozilla.org/webassembly/">WebAssembly on Mozilla Research</a></li>
 <li>
  <p><a href="/en-US/docs/WebAssembly/existing_C_to_wasm">Compiling an Existing C Module to WebAssembly</a></p>
 </li>
</ul>