Paepoi » C and GLib Tips » gcc で C/C++
gcc で C/C++
# 最終更新日 2021.02.27
Linux 環境で gcc を使って C 及び C++ 言語のビルド方法と注意点。
macOS の gcc とは別物なので macOS の場合は Clang のほうをご覧ください。
言語そのものは解説しません、本でも買ってください。
あえてコマンドや gedit でコンパイルする方法を解説します。
「ビルダー」等のアプリはコマンドでビルドが普通にできる人が使うものです。
リテラルに日本語を使う場合は LANG 変数と同じ文字コードで保存されている必要がある。
LANG は下記コマンドで確認、2021 年現在は全部 UTF-8 ですけど一応。
LANG と一致しているなら文字化けせずに表示できます。
コンパイルするには端末を起動してそのディレクトリに cd コマンドで移動します。
out という拡張子に意味は無い、単なるデフォルト名。
既に a.out というファイルが存在する場合は注意、容赦なく上書きされます。
コンパイルが成功すると何も表示されず入力待ちに戻ります。
Success とか ok なんて親切な出力はありません、UNIX ではそれが普通です。
実行パーミッション +x も付いているのでそのまま a.out を実行できる。
ただし当然パスは通っていないので「./」を付加、ドットは現在のパスに変換される。
コードに間違いがあるとコンパイルエラーになり行番号付きで stderr が出力されます。
以下は printf の最後にセミコロンを入れ忘れた場合、以前より親切になっています。
拡張子は不要というか無意味です、実行パーミッションがあれば動きます。
たとえば suzuki.gsr という名前にしたければ
Man page of GCC
同一ディレクトリにまとめているなら gcc *.c のようにワイルドカードを使うのが簡単。
とにかくオブジェクト同士が合体して func.c で定義した関数が利用できるようになる。
ただしこの方法では「暗黙的な宣言」という警告が出ます。
ヘッダを作ってプロトタイプ宣言をしましょう。
Clang のページ のようにするのが正しい方法です。
class, STL 等は C++ 標準なので当然 Linux からも利用できます。
iostream.h と書いている古い本等がありますが C++ ヘッダに拡張子はありません。
ところで、C++ ソースの拡張子は GNU 界隈では cc という拡張子が一般的です。
他に大文字の C や cxx でもいい、gcc の man ページ最初のほうに書いています。
Windows で使われる cpp でも一応 C++ と見なしてくれるようです。
ただ g++ は内容が C++ であれば c の拡張子でも問題なくビルドできる。
gcc で wchar_t は UTF-32(UCS-4) で 4byte である、_t のサフィックスがある変数は環境依存。
UTF-32 と UCS-4 は定義した団体が違うだけで同じものと思ってください。
正確には広義が UTF-32 ですが GLib の関数が UCS-4 の名前を使っているので Linux 必須の知識になる。
リテラルの場合は文字数分になりますが最低が 4 バイトのようです。
wchar_t のサイズが Windows とは違う他に 64bit では long も変わる。
バグの元なのでヤメて注意しましょう。
以前は -std=c99 オプションを付けることによりフル対応可能でした。
c89|c99 というラッパースクリプトもある。
日本語キーボードは恵まれていますがコレが無いと困る言語圏もあったのです。
トライグラフ - Wikipedia
Digraph は C99 で規定されたチョッピリ洗練版。
gcc はオプション無しで使える。
現在はキーボードにブレース等が無い言語圏でもすべて右 Alt キー経由で入力できるようです。
ということで歴史の長い C 以外の言語ではンなことできませんのであしからず。
C 言語をやるならそのうち必要になるので入れておきましょう。
以下の内容で拡張子を付けない Makefile という名前のファイルを作成。
注意としてインデントは \t で行うのを忘れないように。
そして端末から make コマンド、hayabusa という実行ファイルが作成されます。
また Gedit の外部ツールを有効にして「ビルド」を実行(Ctrl+F8)なんてこともできます。
Makefile の書き方については以下のサイトが解りやすかった。
Makefileの解説
GLib によるマクロ g_printf や gchar で記述されている。
様々な理由がありますが GLib に少しでも係るアプリを作るならこちらを使いましょう。
GLib ヘッダを include するには pkg-config 指定が必要です。
GLib Reference Manual
コンパイル毎に打ち込みなんてアホクサイので Makefile を利用しましょう。
そして肝心なコード、ここでは GtkWindow を表示するだけの最小限サンプル。
それと GTK+ 初期化関数の呼び出しは必須なので忘れないように。
これ以上細かいことはリファレンスマニュアルを参照ください。
GTK+ 3 Reference Manual
Linux 環境で gcc を使って C 及び C++ 言語のビルド方法と注意点。
macOS の gcc とは別物なので macOS の場合は Clang のほうをご覧ください。
言語そのものは解説しません、本でも買ってください。
あえてコマンドや gedit でコンパイルする方法を解説します。
「ビルダー」等のアプリはコマンドでビルドが普通にできる人が使うものです。
C 言語のビルド
/* testc.c */ #include <stdio.h> int main (int argc, char *argv[]) { printf("はろーわーるど\n"); return 0; }日本語を出力する何の変哲もない C 言語コードを書き test.c という名で保存。
リテラルに日本語を使う場合は LANG 変数と同じ文字コードで保存されている必要がある。
LANG は下記コマンドで確認、2021 年現在は全部 UTF-8 ですけど一応。
echo $LANG #=>ja_JP.UTF-8 # # ja_JP.utf8 の場合もある、内部表記のことなので気にしない #エディタの保存文字コードが気になるなら cat や less コマンドで確認。
LANG と一致しているなら文字化けせずに表示できます。
コンパイルするには端末を起動してそのディレクトリに cd コマンドで移動します。
gcc ファイル名と打ち込むと a.out というファイルが作られます。
out という拡張子に意味は無い、単なるデフォルト名。
既に a.out というファイルが存在する場合は注意、容赦なく上書きされます。
コンパイルが成功すると何も表示されず入力待ちに戻ります。
Success とか ok なんて親切な出力はありません、UNIX ではそれが普通です。
実行パーミッション +x も付いているのでそのまま a.out を実行できる。
ただし当然パスは通っていないので「./」を付加、ドットは現在のパスに変換される。
コードに間違いがあるとコンパイルエラーになり行番号付きで stderr が出力されます。
以下は printf の最後にセミコロンを入れ忘れた場合、以前より親切になっています。
コマンドラインオプション
この方法だけでは全部 a.out という名前になる、-o オプションで別の名前を付けられます。拡張子は不要というか無意味です、実行パーミッションがあれば動きます。
たとえば suzuki.gsr という名前にしたければ
gcc -o suzuki.gsr test_c.c他のオプションについては以下を参照。
Man page of GCC
分割コンパイル
テストとして関数を定義したファイル。/* func.c */ int plus (int x) { return x + x; }と main 関数を定義したファイル。
/* main.c */ #include <stdio.h> int main (int argc, char *argv[]) { printf ( "%d\n", plus(3) ); return 0; }後は gcc func.c main.c のようにソースコードを全部指定する。
同一ディレクトリにまとめているなら gcc *.c のようにワイルドカードを使うのが簡単。
とにかくオブジェクト同士が合体して func.c で定義した関数が利用できるようになる。
ただしこの方法では「暗黙的な宣言」という警告が出ます。
ヘッダを作ってプロトタイプ宣言をしましょう。
Clang のページ のようにするのが正しい方法です。
C++ 言語のビルド
C++ 言語のコンパイルは g++ コマンドを使います。class, STL 等は C++ 標準なので当然 Linux からも利用できます。
iostream.h と書いている古い本等がありますが C++ ヘッダに拡張子はありません。
// stl.cc #include <iostream> #include <vector> #include <algorithm> /** * みんな大好き C++ のテンプレート * C++11 より unary_function は非推奨 * なんだけど、継承する必要が無くなっただけだったり */ template <class T> struct outputfunc { // : public std::unary_function<T, void> { void operator()(T obj) { std::cout << obj << " ♪♪♪ "; } }; class App { public: App() { std::vector<std::string> vec = {"SUZUKIの", "バイクは", "カッコイイ"}; // 関数オブジェクトにしたけど今は範囲ベース for のほうがいい std::for_each(vec.begin(), vec.end(), outputfunc<std::string>()); } ~App() { std::cout << std::endl << "今日も元気でね!" << std::endl; } static auto init() { // auto で this を戻せる return new App(); } }; int main(int argc, char* argv[]) { // イヂワルな初期化 auto app = App::init(); delete app; return 0; }Fedora 33 では上記で何のオプションも不要でビルド可能です。
ところで、C++ ソースの拡張子は GNU 界隈では cc という拡張子が一般的です。
他に大文字の C や cxx でもいい、gcc の man ページ最初のほうに書いています。
Windows で使われる cpp でも一応 C++ と見なしてくれるようです。
ただ g++ は内容が C++ であれば c の拡張子でも問題なくビルドできる。
UNICODE
Windows しか知らない人は UNICODE を扱う wchar_t は UTF-16LE で 2byte だと思っている。gcc で wchar_t は UTF-32(UCS-4) で 4byte である、_t のサフィックスがある変数は環境依存。
UTF-32 と UCS-4 は定義した団体が違うだけで同じものと思ってください。
正確には広義が UTF-32 ですが GLib の関数が UCS-4 の名前を使っているので Linux 必須の知識になる。
リテラルの場合は文字数分になりますが最低が 4 バイトのようです。
#include <stdio.h> #include <wchar.h> // wchar_t #include <uchar.h> // char32_t // C11 対応の gcc にて int main (int argc, char const* argv[]) { /** * char16_t char32_t の確認 */ printf("リテラルのサイズは %lu バイト\n", sizeof('a')); printf("char のサイズは %lu バイト\n", sizeof(char)); printf("wchar_t のサイズは %lu バイト\n", sizeof(wchar_t)); printf("UTF-16(UCS-2) のサイズは %lu バイト\n", sizeof(char16_t)); printf("UTF-32(UCS-4) のサイズは %lu バイト\n", sizeof(char32_t)); return 0; } /* output リテラルのサイズは 4 バイト char のサイズは 1 バイト wchar_t のサイズは 4 バイト UTF-16(UCS-2) のサイズは 2 バイト UTF-32(UCS-4) のサイズは 4 バイト */
x86_64
64bit アプリケーションではポインタサイズが 64bit(8byte) になる。wchar_t のサイズが Windows とは違う他に 64bit では long も変わる。
#include<stdio.h> #include<stdlib.h> int main (int argc, char *argv[]) { int * p; printf ( "int = %lu\n", sizeof(int) ); printf ( "long = %lu\n", sizeof(long) ); printf ( "pointer = %lu\n", sizeof(p) ); printf ( "char = %lu\n", sizeof(char) ); printf ( "wchar_t = %lu\n", sizeof(wchar_t) ); printf ( "size_t = %lu\n", sizeof(size_t) ); return 0; /* output Linux x86_64 | Windows x64 | int = 4 | int = 4 long = 8 | long = 4 pointer = 8 | pointer = 8 char = 1 | char = 1 wchar_t = 4 | wchar_t = 2 size_t = 8 | size_t = 8 */ }なので Linux なら long 変数にポインタを突っ込...
バグの元なのでヤメて注意しましょう。
C99|C11
現行 Fedora の gcc v10 は標準で C99 に対応しています(// コメント等)以前は -std=c99 オプションを付けることによりフル対応可能でした。
c89|c99 というラッパースクリプトもある。
#include<stdio.h> // 以下のコマンドにすると C89 準拠になりビルドできない // gcc -std=c89 hoge.c // c89 hoge.c int main (int argc, char *argv[]) { // 変数宣言の簡易化 //int i; //for (i=0; i<3; i++) { for (int i=0; i<3; i++) { printf("%d\n", i); } return 0; }C11 は一部対応、-std=c11 というオプションは無い。
#include <stdio.h> // C11 test int main (int argc, char const* argv[]) { /** * C11: fopen のモードに x 追加 * 存在するファイルを開けないように指定 * ちなみに fopen_s は Visual Studio だけ */ FILE *fp = fopen("スズキ.txt", "w"); if (fp == NULL) { printf("書き込みできません\n"); return -1; } else { fputs("カッコイイ!", fp); fclose(fp); } fp = fopen("スズキ.txt", "wx"); if (fp == NULL) { printf("存在するので書き換えできません\n"); // gets の実験はこの return をコメントアウトしてね return -1; } else { fputs("ダサイ!", fp); fclose(fp); } /** * gets は削除されていない、警告は出るがビルドは可能 * gcc や Clang では fgets 推奨 * ちなみに gets_s は Visual Studio だけ */ char buf[1024]; printf( "何か入力してください > " ); gets(buf); printf("入力されたのは「%s」です\n", buf ); return 0; }
C++11|C++14|C++17
現行 Fedora の gcc v10 は標準で C++14 まで標準対応しています。#include <iostream> #include <vector> #include <algorithm> int main (int argc, char const* argv[]) { /** * C++11 auto による型憶測や範囲ベース for ループ */ const char suzuki[3][16] = {"SUZUKIの", "バイクは", "カッコイイ"}; for (auto x : suzuki) { std::cout << x << std::endl; } /** * C++14 ラムダ式の拡張 */ std::vector<std::string> v = {"SUZUKIは", "クルマも", "カッコイイ!"}; std::for_each( v.begin(), v.end(), [] (auto s) -> void { std::cout << s << std::endl; }); return 0; }C++17 は一部対応、オプションが必要。
#include <iostream> // C++ 17 構造化束縛、std::pair や std::tuple を Python のように受け取れる // g++ -std=c++17 test_cc17.cc std::pair<std::string, int> suzuki() { return {"GSR", 400}; } int main (int argc, char const* argv[]) { auto [bike, cc] = suzuki(); std::cout << bike << cc << std::endl; return 0; }
Trigraph, Digraph
Trigraph は昔コンピューターで表記できる文字が少なかった時代に規定された三文字表記。日本語キーボードは恵まれていますがコレが無いと困る言語圏もあったのです。
トライグラフ - Wikipedia
??=include <stdio.h> /** * -trigraphs オプションにて利用可能 * gcc -trigraphs hoge.c */ main() ??< printf("%s??/n", "はろー Trigraphs"); ??>
Digraph は C99 で規定されたチョッピリ洗練版。
gcc はオプション無しで使える。
%:include <stdio.h> main() <% printf("%s\n", "はろー Digraph"); %>
現在はキーボードにブレース等が無い言語圏でもすべて右 Alt キー経由で入力できるようです。
ということで歴史の長い C 以外の言語ではンなことできませんのであしからず。
Makefile
現行 Fedora 33 では make コマンドが入っていません。C 言語をやるならそのうち必要になるので入れておきましょう。
sudo dnf install makemake コマンドは Makefile というファイルを読み取り各種コマンドを実行します。
以下の内容で拡張子を付けない Makefile という名前のファイルを作成。
注意としてインデントは \t で行うのを忘れないように。
# Makefile hayabusa: suzuki.c gcc -o hayabusa suzuki.cそれを suzuki.c というソースがあるディレクトリに置きましょう。
そして端末から make コマンド、hayabusa という実行ファイルが作成されます。
また Gedit の外部ツールを有効にして「ビルド」を実行(Ctrl+F8)なんてこともできます。
Makefile の書き方については以下のサイトが解りやすかった。
Makefileの解説
GLib
GNOME 関連アプリのソースコードを見ると printf や char なんて使われていません。GLib によるマクロ g_printf や gchar で記述されている。
様々な理由がありますが GLib に少しでも係るアプリを作るならこちらを使いましょう。
GLib ヘッダを include するには pkg-config 指定が必要です。
`pkg-config --cflags --libs glib-2.0`GLib には日本語を扱うのに便利な関数も用意されている。
#include <glib.h> #include <glib/gprintf.h> /* gcc test.c `pkg-config --cflags --libs glib-2.0` */ int main (int argc, char *argv[]) { gchar *c; gchar *n; GError *error; gunichar *ucs; glong len; gint i = 0; c = g_strdup("バーグマン 400"); /* 漢字も一文字として長さを得る */ g_printf ("%s は %ld 文字です\n", c, g_utf8_strlen(c, 1024)); /* 漢字も一文字として切り取り */ n = g_utf8_substring(c, 0, 5); g_printf ("%s 200\n", n); g_free(n); /* アルファベットのみ大文字化 */ n = g_utf8_strup("Suzuki Hayabusa カッコイイ", -1); g_printf("%s\n", n); g_free(n); /* UTF-32(UCS-4) に変換 */ ucs = g_utf8_to_ucs4 (c, -1, NULL, &len, &error); g_free(c); /* エラーチェック */ if (!ucs) { g_printf (error->message); gint retval = error->code; g_error_free (error); return retval; } /* 一文字づつ書き出し */ for (i; i<len; i++) { n = g_malloc0(4); g_unichar_to_utf8 (ucs[i], n); g_printf ("%s\n", n); g_free(n); } g_free(ucs); return 0; }これ以上細かいことはリファレンスマニュアルを参照ください(英語)。
GLib Reference Manual
GTK+
GTK+ ヘッダを include するにはやはり pkg-config 指定が必要です。コンパイル毎に打ち込みなんてアホクサイので Makefile を利用しましょう。
# Makefile gtkexe: test_gtk.c gcc -o gtkexe test_gtk.c `pkg-config --cflags --libs gtk+-3.0`※gtk+ により glib, gio はインクルードされるので別途指定は不要
そして肝心なコード、ここでは GtkWindow を表示するだけの最小限サンプル。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; /* 初期化必須 */ gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL); gtk_widget_show_all (window); gtk_main (); return 0; }C 言語なので PyGtk のようなメソッドではなく関数をひたすら利用していきます。
それと GTK+ 初期化関数の呼び出しは必須なので忘れないように。
これ以上細かいことはリファレンスマニュアルを参照ください。
GTK+ 3 Reference Manual
Copyright(C) sasakima-nao All rights reserved 2002 --- 2024.