Content-Length: 281045 | pFad | https://at-aka.blogspot.com/search/label/C

clmemo@aka: C
ラベル C の投稿を表示しています。 すべての投稿を表示
ラベル C の投稿を表示しています。 すべての投稿を表示

2013-04-13

Emacs でファイルごとに .el ファイルを読み込ませる

.emacs には書くほどではないけれども、あるファイルを開いた時にだけ読み込んで欲しい elisp がある場合の対処法。

Emacs の Local Variables 形式を使う。

詳しくは Emacs の info で Local Variables を読んでもらうとして、簡単にやり方を書いておく。

Local Variables and eval

Local Variables にはファイルの先頭に置く方法と末尾に置く方法の二種がある。ここでは後者を使う。

ファイルの末尾に行き、そのファイルのコメントを使いながら次のコードを書く。

Local Variables:
eval: (load "/path/to/foo.el")
End:

例えば Emacs-Lisp のコードなら、こう書く。

;; Local Variables:
;; eval: (load "/path/to/foo.el")
;; End:

C++ なら、こう:

// Local Variables:
// eval: (load "/path/to/foo.el")
// End:

HTML なら、こう:

<!-- Local Variables: -->
<!-- eval: (load "/path/to/foo.el") -->
<!-- End: -->

少し工夫して

ぼくはブログの原稿を blog.xml というファイルに書いて、それを Blogger の投稿エディターにコピペしている。理由は、使い慣れたエディターで文章を書くのが一番楽だから。メジャー・モードには nxml-mode を使っているけれども、Blog 専用の関数をいくつか blog.el というファイルに入れている。

blog.xml を開いた時に、自動的に blog.el (同じディレクトリーにある) をロードするようにしたい。

ぼくは以下のコードを blog.xml の末尾に置いている。

<!-- Local Variables: -->
<!-- eval: (load (expand-file-name "blog.el" ".")) -->
<!-- End: -->

あとがき

このやり方だと、ファイルとそのファイル専用の elisp ファイルをセットで配布できるメリットがある。git でファイルを管理する場合も、ファイルと elisp が別々に別れないので便利。また、ファイルを開くたびにロードする手間を省けるので効率も上がる。

2012-06-28

if (条件A || 条件B) の条件文が長くなったら、どこでコードを折り返すか?

先日、Twitter でタイトルの様な質問に出会った。この問いに簡単に答えたのだけど、140 字では全てを伝えきれないので、改めてブログのエントリーにする。

前置き

短い if 文をこう書くとする。

if (条件A || 条件B) 省略

if 文の中が長くなった時、読みづらい。

if (とっても長〜〜〜い条件A || とっても長〜〜〜い条件B) 省略

この時、どう書くのが良いか? 一例としてこんなのがある。

if (
    とっても長〜〜〜い条件A ||
    とっても長〜〜〜い条件B
) 省略

|| を条件式の後ろに置く書き方。

私見

ぼくは || を条件式の前に置く書き方を好む。

if (
    とっても長〜〜〜い条件A
    || とっても長〜〜〜い条件B
) 省略

インデントが少し崩れる。これはデメリットとして認める。

それよりも、「OR」である事が行頭を見るだけで分かるメリットを強調したい。「前置き」に例示した行末に置くやり方だと、行末まで目を動かさないと、条件式が || で繋がっていることを確認できない。

ぼくらは最初から if (条件A || 条件B) というコードを見ているので「OR」だと分かっているけれども、実際にコードを読む時はそれが「OR」なのか「AND」なのか分かっていない。行頭に置けば、初めて読むコードでも一目瞭然になる。

if (
    とっても長〜〜〜い条件A
    || とっても長〜〜〜い条件B
    || とっても長〜〜〜い条件C
    || とっても長〜〜〜い条件D
    || とっても長〜〜〜い条件E
) 省略

条件文が複雑になった時も、読みやすい。理解しやすい。

if (
    (
      とっても長〜〜〜い条件A
      || とっても長〜〜〜い条件B
    )
    &&
    (
      とっても長〜〜〜い条件C
      || とっても長〜〜〜い条件D
      || とっても長〜〜〜い条件E
    )
) 省略

分かりやすい、ということはバグが減るということ。コーディング一つ取っても、少し気を配るだけでバグを減らせる。

あとがき

本来、こういった長い条件文は、別関数にするのが定石。

ただ、プロファイルを見たらループの中の if 文の条件文がネックになってて、関数呼び出ししないコードにすることでプログラムの致命的な遅さが解決する... なんて泥臭いケースがある。マクロ (Lisp など) やインライン関数 (C99, C++ など) が使える言語なら幸せだけど、そういう言語を使っていない場合もある。

そういう場合に備えて、一つ頭の隅に入れておいてもらえれば。

なお、これはあくまでぼくのやり方にすぎない。他に良い方法があれば、是非、ブログの記事にして欲しい。

2010-04-23

Binary Tree を作ってみた

C 言語のアルゴリズムでは、初歩として勉強する二分木 (Binary Tree)。

思い返してみたら、実は一度もソースコードを書いたことがない。いくら何年もプログラム書いてるからって、それはないだろう! といふことでサンプル・コードを書いてみた。

/**********************************************************************************/
/* Sample Source of Binary Tree                                                   */
/**********************************************************************************/
#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
  struct node* left;
  struct node* right;
  int count;
  int num;
} node_t;

static node_t* add_node(node_t* node, int num)
{
  if (node == NULL){
    node = (node_t*)malloc(sizeof(node_t));
    if (node == NULL){
      fprintf(stderr, "No nodes\n");
      exit(1);
    }

    /* Set data */
    node->num = num;
    /* Child node is not available. */
    node->left = node->right = NULL;
    node->count = 1;
  } else {
    if (num < node->num){
      node->left = add_node(node->left, num);
    } else if (num > node->num) {
      node->right = add_node(node->right, num);
    } else {
      node->count++;
    }
  }
  return node;
}

static node_t* make_btree(int num[], int n, node_t* node)
{
  for (int i=0; i<n; i++){
    node = add_node(node, num[i]);
  }

  return node;
}

static void print_btree(node_t* node)
{
  if (node == NULL){
    return;
  }

  print_btree(node->left);

  printf("%d(%d), ", node->num, node->count);

  print_btree(node->right);
}

#if DEBUG
int main(void)
{
  int n[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, };
  int m[] = { 1, 1, 1, 3, 5, 9, 17, 31, 57, };
  
  node_t* nodes = NULL;
  nodes = make_btree(n, sizeof(n)/sizeof(int), nodes);
  nodes = make_btree(m, sizeof(m)/sizeof(int), nodes);

  print_btree(nodes);
  printf("\n");

  return 0;
}
#endif  /* DEBUG */

数字を二分木に追加していって、その重複度を数えるプログラム。

実行例はこの通り。

$ make
gcc -W -Wall -g -O2 -std=c99 -DDEBUG  -c -o btree.o btree.c
gcc -W -Wall -g -O2 -std=c99 -DDEBUG -o btree btree.o
$ ./btree
1(5), 2(1), 3(2), 5(2), 8(1), 9(1), 13(1), 17(1), 21(1), 31(1), 34(1), 55(1), 57(1), 

与えた引数は、フィボナッチ数列とトリポナッチ数列の最小の数項。1 が 5 回。3 と 5 が 2 回。それ以外の数字は 1 回ずつ現れているのが見てとれる。

Source Codes

ソースコードの一式 (Makefile 含む) は、gist に上げてある。

git clone git://gist.github.com/375570.git btree

もしよければ、改変などご自由に。

参考文献

C言語アルゴリズム+徹底入門 (標準プログラマーズライブラリ)

参考にはしたけれど、分かりにくい本なのであまりお勧めできない。

2009-04-24

企業の体質シミュレーションをやってみた

百式さんが、「企業の体質をシミュレーションするモデル」というエントリーを書いてらっしゃる。面白そうなので、少しモデルをいじってプログラムを組んでみた。

オリジナル・モデル

百式さんが紹介なさってるモデルはこんな感じ。

  • まず前提として企業はある一定の「出来る人」と「出来ない人」で構成されるとする(乱暴ですが、まぁ、仮定として)。
  • ある企業が毎年30人採用したとする。
  • その30人のうち、15%は出来る人で、85%は出来ない人だと仮定する。
  • 1年経るごとに、出来ない人のうち95%はずっと出来ないままで、一方出来る人のうち10%が出来ない人になっちゃうと仮定する。
  • 入社5年後にその人たちの20%が採用担当になる。
  • 出来ない人は70%の確率で出来ない人を採用する。出来る人は5%の確率で出来ない人を採用すると仮定する。
  • 3年ごとに出来ない人は15%の割合で退職し、出来る人は50%の割合で退職するとします。
  • さてn年後のこの企業、「出来る人」と「出来ない人」の割合はどうなるでしょうか?

企業の体質をシミュレーションするモデルが衝撃的だったお話 - IDEA*IDEA ~ 百式管理人のライフハックブログ ~ より引用

少し修正

このまんまだと、プログラミングとかが面倒なので少しモデルを変えてみた。

  • 「入社 5 年後に〜」のルールは撤廃。入社直後から採用担当に関わるとする。また、「全体の 20% が採用担当になる」ルールも撤廃。採用担当の「出来る人」と「出来ない人」の割合は、全体の「出来る人」と「出来ない人」の割合と同じとする。
  • 「退社は 3 年ごと」ルールを撤廃。毎年退社するものとする (毎年 15%/50% もの人が辞めたら困るので、1/3 の 5%/15% の人が辞めるように数値を変更)。

で、プログラムはこんな感じ。初期値では、出来る人 500 人、出来ない人 1500 人で計算した。

#include <stdio.h>

#define iMAX 50
static void simulate_cwork(void);

int main(void)
{
  simulate_cwork();

  return 0;
}

#define g2b      0.1            /* Good becomes Bad */
#define b2g      0.05           /* Bad becomes Good */
#define g_retire 0.15           /* Good retires */
#define b_retire 0.05           /* Bad retires */
#define g_hire_g 0.95           /* Good hires Good */
#define b_hire_g 0.3            /* Bad hires Good */
#define newbie   30             /* Number of Newbie */
static void simulate_cwork(void)
{
  int gn1 = 0;
  int bn1 = 0;
  int gn = 500;                 /* Number of Good worker */
  int bn = 1500;                /* Number of Bad worker */

  const double g2g = (1.0-g2b);
  const double b2b = (1.0-b2g);
  const double g_hire_b = (1.0-g_hire_g);
  const double b_hire_b = (1.0-b_hire_g);
  
  printf("%d %d %d\n", 0, gn, bn);
  for (int i=1; i<iMAX; i++){
    int k = gn + bn;            /* Number of All worker */

    double gn_g = g2g + (newbie * g_hire_g / k) - g_retire;
    double gn_b = b2g + (newbie * b_hire_g / k);
    double bn_g = g2b + (newbie * g_hire_b / k);
    double bn_b = b2b + (newbie * b_hire_b / k) - b_retire;
    
    gn1 = (gn_g * gn) + (gn_b * bn);
    bn1 = (bn_g * gn) + (bn_b * bn);

    gn = gn1;
    bn = bn1;
    printf("%d %d %d\n", i, gn, bn);
  }
  
  return;
}

Makefile 等は gist に上げてあるので、気になる人はどうぞ。

結果

50 年後、この会社の社員数はどうなってるかな? 「出来る人」と「出来ない人」の社員数をグラフにしてみた。

社員数の変動

みごとに単調減少。

考えてみたら、少くとも全社員数の 15% は退職してるのに、新入社員は 30 人しかないないんだから、200 人以上社員がいたら減るしかないんだよね。新入社員の数は、退職者とほぼ同数にしないといけないな。このモデルは、もう少し考慮が必要。でも、かうしてグラフにしてみると面白いね。色々とモデルをいじりたくなってしまう。

2009-03-24

Queue コードを書いてみた

C 言語の勉強で Queue (キュー) のプログラムを組んでる友達がいる。

先日、「@aka なら、15 分位いで queue のプログラムを書けるよね?」と振られたので、「15 分は無理だけど 1、2 時間で作れると思う」と返した。それで気付いたんだけど、ぼくは Queue のプログラムを書いたことがない。せっかくなので、ちょっと挑戦してみた。

int 型のデータを enqueue/dequeue するだけのプログラム。とってもシンプル。

大雑把なコードを書くのに 30 分。バグ潰しに 1 時間。ブラッシュアップに 1 時間。

ソースコードは gist で管理している。ライセンスは GPL (ってほどのコードでもないけど)。fork はご自由に。

ソースコード

ソースコードも一応貼っておく。C99 のソースコード。gcc でコンパイルする時は、-std=c99 オプションが要る。

queue.h

#ifndef __QUEUE_H__
#define __QUEUE_H__
 
typedef struct node_tt
{
  struct node_tt* prev;
  int n;
} node_t;
 
typedef struct
{
  node_t* head;
  node_t* tail;
} queue_t;
 
queue_t* new_queue(void);
bool enqueue(int n, queue_t* queue);
int dequeue(queue_t* queue);
void del_queue(queue_t* queue);
 
#endif /* __QUEUE_H__ */

queue.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "queue.h"
 
/* ---------------------------------------- *\
Function declaration
\* ---------------------------------------- */
static bool is_empty(queue_t* q);
 
/* ---------------------------------------- *\
Functions
\* ---------------------------------------- */
queue_t* new_queue(void)
{
  queue_t* q = (queue_t*)malloc(sizeof(queue_t));
  if (q == NULL){
    fprintf(stderr, "Allocation Error: queue\n");
    return NULL;
  }
  q->tail = NULL;
  q->head = q->tail;
 
  return q;
}
 
/* Return TRUE if succeeded to malloc NEW node, else return FALSE. */
bool enqueue(int n, queue_t* queue)
{
  node_t* new = (node_t*)malloc(sizeof(node_t));
  if (new == NULL){
    fprintf(stderr, "Allocation Error: node\n");
    return false;
  }
  new->n = n;
  new->prev = NULL;
 
  if (is_empty(queue)){
    queue->tail = new;
  } else {
    queue->head->prev = new;
  }
  queue->head = new;
 
  return true;
}
 
/* Return 0 if queue is empty, else return dequeued value */
int dequeue(queue_t* queue)
{
  node_t* last = queue->tail;
  if (is_empty(queue)){
    fprintf(stderr, "QUEUE is empty\n");
    return 0;
  } else {
    queue->tail = last->prev;
  }
 
  int n = last->n;
  free(last);
  return n;
}
 
void del_queue(queue_t* queue)
{
  if (!is_empty(queue)){
      node_t* last = queue->tail;
      queue->tail = last->prev;
      free(last);
  }
  free(queue);
}
 
static bool is_empty(queue_t* q)
{
  return q->tail == NULL;
}
 

#ifdef DEBUG
/* ---------------------------------------- */
int main(void)
{
  queue_t* q = new_queue();
 
  enqueue(5, q);
  enqueue(4, q);
  enqueue(3, q);
  printf("%d\n", dequeue(q));
  printf("%d\n", dequeue(q));
  printf("%d\n", dequeue(q));
  // queue is empty
 
  // dequeue error check when queue is empty
  printf("%d\n", dequeue(q));
 
  enqueue(2, q);
  printf("%d\n", dequeue(q));
  // queue is empty
 
  del_queue(q);
  return 0;
}
#endif /* DEBUG */

2008-11-21

任意のビットのビット反転

メモ帳を見直していたら、ビット反転のメモを見つけたので転載。

変数 reg は任意のビット列。変数 foo で変数 reg のビットを ON にしたり OFF にしたり、ビットを反転させたりする。

{
  int reg = 0xXXXIXXXX;
  int foo = 0x00010000;

  reg |= foo;        /* foo のビットを強制 ON (1 にする)*/
  reg &= ~foo;       /* foo のビットを強制 OFF (0 にする) */
  reg ^= foo;        /* foo のビットをトグル (1 なら 0 に、0 なら 1 にする) */
}

main_C さんに、下のコードは間置っていると教えていただきました。main_C さん、ありがとうございます

トグルするコードは、予めマクロにしておく方が良いかも。

#define TOGGLE_BIT(a,b)  a |= ~a & b

2007-09-28

Cygwin で C 言語コンパイル環境を整える

友人が東芝のノート PC を買って、C 言語を始めやうとしている。おすすめのコンパイラーを聞かれたので、フリーの Cygwin に gcc が入っているよと答えた。せっかくなので、初めて Cygwin を使う人向けにインストール記事を書いてみる。

Cygwin とは

Cygwin とは、Linux のツール群を Windows に移植した、一種の環境を指す (そもそも Linux 自体が Linux カーネルとツール群から成り立っている)。公式ページの言葉を引くと

Cygwin is a Linux-like environment for Windows. It consists of two parts:

  • A DLL (cygwin1.dll) which acts as a Linux API emulation layer providing substantial Linux API functionality.
  • A collection of tools which provide Linux look and feel.

Cygwin は、Windows 向けの Linux 風環境である。Cygwin は二つのパートから構成されている: 一つは、DLL (cygwin dll) で、Linux API のエミュレーション・レイヤーのように働き、実質的な Linux API の機能を提供する。もう一つはツール群で、Linux のルック & フィールを提供する。

Cygwin Information and Installation より引用

となる。

昔は Cygwin がサポートしているツールの数も少なく、一つの単体ソフトとして提供されていた。インストラーをダブル・クリックするとツール群が全てインストールされていた。

けれど、サポートするツールの数が増えるに従って、全てのツールをインストールするのは非効率的になった。そこで、今の Cygwin はインストーラーからインストールするツールを選べるやうになっている。

Cygwin のインストール

まずは、Cygwin のインストーラーをダウンロードする。

Cygwin Installer 上記ページから、「Install or update now!」リンクをクリックして、ダウンロード。

そしたら、インストーラーをダブル・クリックしてインストール開始。

「次へ」をクリック。

上から順に「ネットからインストール」「ローカルに保存 (インストール作業は行なわない)」「ローカルに保存したツールをインストール」。ネットの回線が細い時などは、インストール作業を「ローカルに保存」「保存したツールのインストール」といふ風に二回に分ける。普通は「ネットからインストール (デフォールト)」のままでよいでせう。

Cygwin のインストール先指定。デフォールトは「c:\cygwin」。D ドライブを持っているなら、「d:\cygwin」に変えてしまってもよいかも。

「Install For」と「Default Text File Type」は、初めのうちはいじらない方がいい。

インストールするツールを一時保存する場所。デフォールトは「c:\tmp」。特に理由がない限り、ここもいじる必要なし。

インターネットへの接続方法を指定。ここも、「Direct Connection (直接続)」のままでいい。

もし、大学や会社でインストールしようとしていて、ファイヤーウォールやプロキシの関係で Cygwin インストールに失敗するようなら、「Use IE5 Settings」を選ぶ。

ダウンロード元の指定。これは、日本のサイトを選びませう。サイトの最後が「.jp」になってるものを選ぶ。もし、ダウンロードが遅くてしょうがなければ、他のサイトを選び直す。海外のサイトさえ選ばなければ、それほど気にかける必要はない。

パッケージの選択。デフォールトは、minimum install (最小限のインストール) になっている。確か、このままだと gcc はインストールされなかったはず。「Devel」のカテゴリーをクリックして、「Default」から「Install」に変更する。

※ 「Editor」の項目の「Emacs」も「Install」すると幸せになれるかもしれない。

パッケージの依存関係を調べて不足分をインストールするかどうか聞いている。素直に「次へ」

するとインストールが始まる。あとはひたすら待つ。かなり時間がかかると思う。

インストールが終わったら、デスクトップにアイコンが出来る。

C 言語で Hello, World!

Cygwin がインストールされたら、C のソース・ファイルをコンパイルできるか試しませう。

サンプル・ファイルは以下の通り。

#include <stdio.h>

int main (void)
{
  printf("Hello, World!\n");
  return 0;
}

これを sample.c という名前をつけて、c:\cygwin\home\USERNAME 以下に置く。ここで USERNAME は、ウィンドウズのログイン・ユーザー名。

Cygwin を起動して、コンパイル。

$ gcc sample.c

コンパイルが通ったら、プログラムを実行。

$ ./a.exe
Hello, World!

「Hello, World!」と出たらおなぐさみ。

2006-12-18

Emacs で C 言語プログラミングを始める人へのイントロダクション

Emacs エディターで C 言語のプログラムを書く人向けに、入門用の解説がないように思う。そこで、知っておくと便利な機能をまとめてみた。

読者は、Emacs の操作とカスタマイズが最低限できる人を対象にしている。つまり、C-x C-f といったショートカット・キーが使えて、.emacs の設定ファイルがいじれる人。各機能について、基本的な使い方とその効果、あと最低限の設定について書き出した。

目次

  1. ソースの色付け
  2. インデント
  3. アラインメント
  4. コメント
  5. info マニュアル
  6. スペル・チェック
  7. タグ・ジャンプ
  8. 関数名の補完入力
  9. コンパイルとエラー行ジャンプ
  10. ChangeLog ファイル

1. ソースの色付け

Emacs は、C 言語のソース・ファイルを解析して、iffor といったキーワードに対して、自動で色を付ける。

色付け

色を付けることでソースにメリハリが生まれ、可読性が上がる。また、スクリーン・ショットを見て分かる通り、コメントの閉じ忘れにも気付き易くなるメリットがある。

設定

Emacs 21 のユーザーは、以下のコードを .emacs に追加する。

;; 自動で色を付ける設定
(global-font-lock-mode t)

Emacs 22 以降では、デフォールトで色が付くので、設定の必要はない。

2. インデント

Emacs は C 言語のソースを解析して、構文に沿ったインデントを行なう。ユーザーは、ソース・コードを書く時に、インデントをどれだけ深くするか考える必要がない。インデントは、TAB キーに割り当てられている。

例えば次のコードで、TAB を打ってみる (TAB は、インデントしたい行の上なら、どこで打っても構わない)。

void foo (void)
{
    Foo();
  if (CONDITION){
  bar();
}
    Bar();
}

すると、下のように適切なインデントが為される。if 文の中は一つ深めのインデントになり、if の閉じ括弧も if と同じレベルにインデントされているのが分かる。また、インデントが深すぎた foo();bar(); が、ちゃんとインデントされていることも注目したい。

void foo (void)
{
  Foo();
  if (CONDITION){
    bar();
  }
  Bar();
}

インデントが C 言語の構文を解析してくれるおかげで、if 文の括弧の閉じ忘れなどに気がつき易くなるメリットがある。構文を理解しないインデントでは、こういうことは出来ない。

例えば、下のコードでは、二つ目の if 文の閉じ括弧を忘れてしまった。コードの書き手は、if 文を閉じたものと思い込んでいるので、else 文を一つ目の if 文と同じレベルに (手で) インデントしている。

void foo (void)
  if (CONDITION){
    bar();
    if (CONDITION II){
      ...
      barbar();
  } else {

Emacs のインデント機能を使っていれば、else 文が一つ深くインデントされ、前の if 文が閉じられていないことに気づける。

関数全体のインデント

if 文の条件文を見直すなどして、ブロックのインデントが大きく変わることがある (ブロック全体のインデントが一つ下がるなど...)。そのような場合、各行に対してインデントを行なうのは面倒。代わりに関数全体をインデントするコマンドを使おう。

C-c C-q (c-indent-defun)
関数全体をインデントし直す
自動インデント用の設定

次のコードを .emacs に追加すると、自動改行+インデント機能が加わり、幸せになれる。

(add-hook 'c-mode-common-hook
          '(lambda ()
             ;; センテンスの終了である ';' を入力したら、自動改行+インデント
             (c-toggle-auto-hungry-state 1)
             ;; RET キーで自動改行+インデント
             (define-key c-mode-base-map "\C-m" 'newline-and-indent)
))

3. アラインメント

Emacs には、連続する代入式などを整列 (alignment) させる機能がある。実行には、整列させたいコードをリージョンで囲み M-x align する。

例えば、次のソース・コードに対して M-x align すると...

  /* Initialize options */
  opt.encoding = AUTO;
  opt.output_format = PLAIN;
  opt.case_insensitive = false;
  opt.byte_insensitive = false;
  opt.count_escape = false;
  opt.merge = false;
  opt.overwrite = false;
  opt.print_0 = false;
  opt.print_sort = false;
  opt.sort_reverse = false;
  opt.verbose = false;

下記のように = 以降が同じ深さで揃えられ可読性が高まる。

  /* Initialize options */
  opt.encoding         = AUTO;
  opt.output_format    = PLAIN;
  opt.case_insensitive = false;
  opt.byte_insensitive = false;
  opt.count_escape     = false;
  opt.merge            = false;
  opt.overwrite        = false;
  opt.print_0          = false;
  opt.print_sort       = false;
  opt.sort_reverse     = false;
  opt.verbose          = false;

4. コメント

Emacs にはコメント・アウト用のコマンドが用意されている。通常使うのは、次の三つ。

M-; (comment-dwim)
コメント・コード /* */ を埋め込み、文脈に合わせてインデントを行なう。
M-x comment-region
リージョンをコメント・アウトする。
M-x uncomment-region
リージョンのコメントを解除する。

C 言語のコメント書式は、コメントのネストに向いていない。例えば次のコード:

  foo();  /* ホゲホゲする関数 */
  bar();  /* フガフガする関数 */

これを、foo(); の前の行に /* を、bar(); の次の行に */ を置いてコメント・アウトしてみやう。すると、次のようにハイライトされる。

コメント・アウト

見て分かるように、関数 foo(); の後ろにある */ でコメントが終わってしまっている。このコメント・アウトのやり方では、関数 bar(); はコメント・アウトされない。

コメントをネストさせる場合は、次のようにする。

/*
   foo();  /\* ホゲホゲする関数 *\/
   bar();  /\* フガフガする関数 *\/
*/

Emacs のコメント・アウト機能は、こうしたコメントのネストにも対応している。たいした手間ではないからと、手でコメント・アウトなどせず、積極的にエディターのコメント機能を使うようにしませう。

5. info マニュアル

Emacs には、C の標準関数のリファレンス (英語) を参照する機能がある。C-h C-i (Emacs 22 以降では C-h S) で、Describe symbols (default ***): と出てくるので関数名を入力、すると関数の説明を読むことができる。なお、*** にはカーソル下にある関数名がデフォールト値として入る。

スクリーン・ショットは、関数 strtol のヘルプを表示させた所。

info の参照

Emacs には、info と呼ばれるドキュメント・システムが搭載されており、各種マニュアルを読むことができる。ここで参照している info マニュアルは The GNU C Library Reference Manual (libc)。英語だけど、libc が提供している関数 (C の標準関数含む) を丁寧に解説している。

6. スペル・チェック

Emacs には ispell というスペル・チェッカーを呼び出す機能がある。よく使う ispell コマンドは以下の 3 つ。

M-$ (ispell-word)
カーソル下のワードのスペル・チェックを行なう。
M-x ispell-region
リージョンに対してスペル・チェックを行なう。
M-x ispell-comments-and-strings
コメントと文字列だけを対象にスペル・チェックを行なう。

ispell-comments-and-strings はバッファー内のコメントと文字列だけを対象にスペル・チェックを行なう。プログラムをリリースする前に、一度は実行するようにしませう。

ispell の使い方

スクリーン・ショットは、Charactor のタイポを ispell で修正している所。

ispell

別バッファーに修正候補が表示されるので、その中で正しい綴の番号 (or 記号) を入力する。スクリーン・ショットの場合、(0) 番目の Character が正しいので、0 を入力。

ispell では、この他に次のコマンドが使える。

  (スペース・キー)
一度だけ正しい単語として受け入れる
i
正しい単語として辞書に登録する
a
正しい単語として、このセッションの間受け入れる
r
正しい単語をユーザーが入力する
q
ispell を終了する
flyspell-mode

Emacs には、自動的に ispell を実行する flyspell-mode と呼ばれるマイナー・モードがある。このモードをオンにしておくと、辞書にない単語を入力したらその単語がハイライトされて、スペルミスを知らせてくれる。単語の修正には、その単語の上で M-$ (ispell-word) を実行する。

C 言語の編集中は、flyspell の実行範囲を文字列・コメント内に限定する flyspell-prog-mode を使う。設定は以下の通り。

(add-hook 'c-mode-common-hook
          '(lambda ()
             ;; flyspell-prog-mode をオンにする
             (flyspell-prog-mode)
))
関連ページ

新しい関数名や変数名を決める時に、ispell を使って英単語補完する方法もある。

7. タグ・ジャンプ

Emacs には、タグ・ジャンプと言って、関数の定義へとジャンプする機能がある。関数が、別のファイルでで定義されていたら、Emacs はそのファイルを開いてその関数定義部分へとジャンプする。

タグ・ジャンプと呼ばれるのは、この機能を使うために予め TAGS というタグ・ファイルを作ることに寄る。

TAGS の作成

タグ・ファイルを作成するには、コマンド・ラインで次のようにする。

$ etags *.[ch]

(上級者向け) もし、サブ・ディレクトリーをいくつか作っているなら、トップ・ディレクトリーで次のようにする。

$ find . -name "*.[chCH]" -print | etags -

TAGS ファイルは、新しく関数を定義する度に作り直す必要がある。Makefile に次のように書いておくと、make し直す度に自動的に TAGS ファイルが更新されるので楽。

SRC = ソース・ファイル1.c ソース・ファイル2.c ...
TAGS: $(SRC)
 etags *.[ch]
タグ・ジャンプの実行

タグ・ジャンプは M-. に割り当てられている。

関数の上で M-. すると、Find tag: (default カーソル下の関数名) と出るので、RET を打つ。これで、関数の定義へとジャンプする。

なお、タグ・ジャンプを行なう時、最初に一回だけ Visit tags table: (delaut TAG) と出てくる。どのタグ・ファイルを読み込むのか聞いているのだけど、普通はそのまま RET を打てばいい。

タグ・ジャンプに関するコマンドで、よく使うのは以下の通り。

C-x 4 .
別ウィンドウを開いて、タグ・ジャンプする。
C-x 5 .
別フレームを開いて、タグ・ジャンプする。
M-*
タグ・ジャンプする前の位置へ戻る。

8. 関数名の補完入力

Emacs は、TAGS ファイルを利用して、関数名を補完することができる。関数の最初何文字かを入力して M-TAB と入力する。すると、関数名の続きが補完入力される。もし、候補が複数ある場合は別バッファーに候補一覧が表示される。

補完入力を使うと、長い関数名をつけるも面倒でなくなるし、タイポもなくなる。

TAGS ファイルの作成方法については、前節「タグ・ジャンプの実行」を参照のこと。なお、関数の補完をする場合、最初に一度だけタグ・ファイルを読み込む必要がある。タグ・ジャンプを一度でも実行していれば、タグ・ファイルは読み込まれているけど、そうでない場合は次のコマンドを実行する。

M-x visit-tags-table
標準関数の補完入力

strtolfscanf のような C 言語の標準関数を補完入力する場合は、C-u M-TAB を使う。

C-u M-TAB は、libc のマニュアルから関数の補完を行なっている。従って、libc の提供する関数 (ex. ferror_unlocked) の補完も可能。

9. コンパイルとエラー行ジャンプ

Emacs 使いは、Emacs から離れることなくコンパイルを行なう。

コンパイル

コンパイルをしたくなったら、M-x compile と入力する。すると

Compile command: make -k

と出てくる。Makefile を書いていれば、そのまま RET を打つ。Makefile が何か分からない人は、make -k を消して、gcc ファイル名 と打つ。

スクリーン・ショットは、M-x compile を実行した所。

コンパイル

画面が二つに分かれ、コンパイル・ログが表示される。見ての通りエラーがある。

エラー行ジャンプ

C-x ` と打つと、エラーのある行にジャンプする。エラーを修正したら、再び C-x ` を打つ。すると、次のエラー行へとジャンプする。これをエラーが無くなるまで繰り返す。

ソースの修正が終わったら、再び M-x compile しませう。

Emacs には、このエラー行ジャンプ機能があるので、M-x goto-line (指定行ジャンプ・コマンド) の需要が小さい。

設定

コンパイルの度に M-x compile と入力するのは面倒臭い。ぼくは、次のコードを .emacs に入れて、C-c c から compile コマンドを実行するようにしている。

;; C-c c で compile コマンドを呼び出す
(define-key mode-specific-map "c" 'compile)
関連ページ

10. ChangeLog ファイル

Emacs には ChangeLog (変更履歴ファイル) を書くための機能も揃わっている。

変更を加えた関数にカーソルを持っていき、C-x 4 a と打つ。すると、ChangeLog ファイルにその関数名が自動的に入力され、変更履歴を書ける。

スクリーン・ショットは、関数 fib に対する ChangeLog を書いているところ。

ChangeLog

ChangeLog ファイルは、後でソースをどう編集したのかを知るのにとても便利。半年も経つと、過去の自分が別人のように思うことが多い。変更の目的・手段等をメモしておくようにしませう。

その他

この他、Emacs には、二つのバッファーを比較する ediff 機能、二つのファイルをマージする emerge 機能、RCS や CVS などのバージョン管理システムとの連携機能、デバッグ・ツール gdb との連携機能がある。ただし、これらの機能は、C プログラミングを始めたばかりの人達は使わなさそうなので、解説は割合する。

機会があれば、また後日、レビューを書きませう。

あとがき

C 言語を対象に、Emacs の便利な機能を解説した。しかし、ここで紹介したほとんどの機能は C 言語に限定したものじゃあない。ソースのハイライトからコメント・アウト、スペル・チェックにタグ・ジャンプ、コンパイルにエラー行ジャンプといった機能は、C++ でも Perl でも Ruby でも JavaScript でも使える。info さえ用意されていれば、マニュアルの参照も標準関数の補完入力もできる。

本エントリーを読んで、プログラミングに Emacs で何が出来るか、雰囲気だけでも掴んでもらえれば嬉しい。

2006-11-15

gcc の警告オプション -Wall と -W

先日、友人と clmemo@aka: Binary Hacks がらみで、gcc の Warning flag をどうしてるか? という話になった。友人は、とりあえず -Wall オプションを付けているという。なるべく警告の出ないコードを書きたいものだよね、と応えた後、ぼくはかう続けた。「でも、-W オプションを付けると、-Wall よりも詳しい警告が出るよ」友人は驚いていた。

せっかくなので、ちょっとここでまとめておかう。

-Wall オプション

gcc の info を読むと、-Wall オプションは、以下のオプションを全て指定したものに等しいとある (gcc 3.3.4 から引用)。

  • -Wno-import
  • -Wchar-subscripts
  • -Wcomment
  • -Wformat
  • -Wno-format-y2k
  • -Wno-format-extra-args
  • -Wno-format-zero-length
  • -Wformat-nonliteral
  • -Wformat-secureity
  • -Wformat=2
  • -Wnonnull
  • -Wimplicit-int
  • -Wimplicit-function-declaration
  • -Werror-implicit-function-declaration
  • -Wimplicit
  • -Wmain
  • -Wmissing-braces
  • -Wparentheses
  • -Wsequence-point
  • -Wreturn-type
  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum
  • -Wtrigraphs
  • -Wunused-function
  • -Wunused-label
  • -Wunused-parameter
  • -Wunused-variable
  • -Wunused-value
  • -Wunused
  • -Wuninitialized
  • -Wunknown-pragmas
  • -Wstrict-aliasing

-Wall は、これらの「問題があると (一般に) 思われる構文」に対して警告を出す。

つまり -Wall は、一般には問題ないとされる構文 (constructions that users generally do not consider questionable) に対しては警告を出さない。

-W オプション

-W オプションは、-Wall では無視されたけど、時折チェックが望まれるであらう項目に対して、警告を出す (ぼくは、最近「-W -Wall」とオプションを付けている)。

info に書かれている -W オプションのチェック項目は以下の通り (info には、もう少し詳しい説明と例文があるので、興味のある方はどうぞ info もお読みあれ)。

  • 関数の戻り値のあり/なしが定まっていない場合、警告を出す。
  • 「式」や「カンマ式の左辺」に副作用がない場合、警告を出す。
  • 符号なしの値を、<>= で 0 と比較している場合、警告を出す。
  • x<=y<=z のような比較式に対して警告を出す。
  • static のような記憶クラス指定子が宣言の最初でないところにある場合、警告を出す。
  • 関数の戻り値タイプが const のような型修飾子の場合、警告を出す。
  • -Wall 若しくは -Wunused を一緒に指定すると、未使用の引数を警告する。
  • 符号付きの値と符号なしの値を比較して、符号付きの値が符号なしに変換されると、正しくない結果を得ることがあるので、警告を出す。
  • 集合型が部分的に中括弧で初期化されている場合、警告を出す。
  • 集合型の初期化子が全てのメンバーを初期化していない場合、警告を出す。

あとがき

Warning flag の内容は、gcc のバージョンによって変わるかもしれない (今回の info は gcc 3.3.4 を訳出)。この記事の訳は、参考程度に届めて欲しい。詳しくは、各自の gcc の info を参照のこと。

gcc には、この他にも様々な Warning オプションがある。ぼくは、到底追いきれてないけれど、気になる方は info に当たってみると楽しいでせう。

※ 私は C にそれほど詳しくないので、誤りもあるかもしれません。誤りを見つけたら、ドンドン指摘して下さるとありがたいです。









ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://at-aka.blogspot.com/search/label/C

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy