C99に対応した標準Cライブラリの実装レポートを行っていきます。

プロフィール 

高木信尚

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

ブロとも申請フォーム 

この人とブロともになる

ホーム 全記事一覧 << 前ページ 次ページ >>

 

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
--/--/-- --:--|スポンサー広告

 

前回のatoi関数は独自の実装でしたが、今回取り上げるatol関数とatoll関数は、それぞれstrtol関数とstrtoll関数のラッパになります。なお、strtol関数とstrtoll関数の実装については次回に取り上げる予定です。

実装ですが、まずは<stdlib.h>ヘッダ内でインライン関数として定義し、同じ内容の外部定義も用意することになります。

static __inline__ long atol(const char *__s)
{
  return strtol(__s, NULL, 10);
}

static __inline__ long long atoll(const char *__s)
{
  return strtoll(__s, NULL, 10);
}

atoll関数は、返却値型がlong longであることからも分かるように、C99で追加された関数です。もちろん、内部で呼び出しているstrtoll関数も同様です。
2006/07/01 09:40|一般ユーティリティTB:0CM:0

 

atoi関数は、エラー処理ができないこともあり、なるべくstrtol関数に置き換えるべき非推奨の関数です。しかし、エラーがないことが分かっている場合には、エラー判定の処理がないことと、10進数固定であることにより、strtol関数より効率がよいのも確かです。特に、long型とint型が異なるサイズの場合、その違いが顕著に現れます。

atol関数との対象性もあるので、やや悩むところではありますが、今回の実装では、atoi関数に関してはstrtol関数を使用せずに独自の実装を行うことにしました。atol関数については、別の機会に取り上げますが、strtol関数を内部で使用することにします。

atoi関数の実装に入る前に、atoi関数のほかに、strtol関数やstrtod関数などで共通に使用する下請け関数を定義します。この関数は、空白類文字を読み飛ばし、符号文字を判定して、負であれば-1を、それ以外は0を返す仕様です。また、空白類文字と符号文字の直後にポインタを位置付けます。

#include <ctype.h>

int _space_sign(const char *s, const char **endptr)
{
  while (isspace((unsigned char)*s))
    ++s;
  int sign = 0;
  switch (*s)
  {
  case '-':
    sign = -1;
    // fall through
  case '+':
    ++s;
    break;
  }
  *endptr = s;
  return sign;
}

なお、リンク後のプログラムサイズを最小にするため、この_space_sign関数は、それだけで単独のソースファイルに記述するようにします。そうしないと、strtod関数を使いたいだけの場合でも、atoi関数がリンクされてしまい、非常に空間効率が悪くなります。

それでは、atoi関数本体の実装です。

#include <ctype.h>

int atoi(const char *s)
{
  int sign = _space_sign(s, &s);
  int result;
  for (result = 0; isdigit((unsigned char)*s); s++)
    result = result * 10 + *s - '0';
  if (sign != 0)
    result = -result;
  return result;
}

本来であれば、for文の中でオーバーフローの判定を行うべきですが、仮にエラーを検出しても適切にレポートする方法がないため、何もせずに放置しています。その引き換えとして、文字列から数値への変換を高速に行うことができるようになっています。

2006/06/29 22:32|一般ユーティリティTB:0CM:0

 

しばらく更新をサボっていました。しかも、ほとんどセットものであるmbtowc、wctomb、mbstowcsと書いたのに、なぜかwcstombsだけ残っていました。というわけで、今回はwcstombs関数です。といっても、"C"ロケールにしか対応しなければ、やることはmbstowcs関数とほとんど同じです。

#include <stddef.h>

size_t wcstombs(char * __restrict__ s,
                const wchar_t * __restrict__ pwcs,
                size_t n)
{
  register char *p = s;
  for (char *t = p + n;
      p != t && (*p = (unsigned char)*pwcs) != '\0';
      p++, pwcs++)
    ;
  return p - s;
}

後になって気付いたのですが、ワイド文字の値がUCHAR_MAXより大きい場合はエラーにした方がよいのかもしれません。いや、多分そうすべきなのでしょう。これについては、<wchar.h>ヘッダの実装時に改めて検討しなおすことにしたいと思います。
2006/06/28 11:43|一般ユーティリティTB:0CM:0

 

よく似た名前の関数が続きますが、今回は、多バイト文字列からワイド文字列に変換するmbstowcs関数です。例によって、現時点では"C"ロケールにしか対応しませんので、実装は簡単です。

#include <stddef.h>

size_t mbstowcs(wchar_t * __restrict__ pwcs,
                const char * __restrict__ s,
                size_t n)
{
  register wchar_t *p = pwcs;
  for (wchar_t *t = p + n;
      p != t && (*p = (unsigned char)*s) != L'\0';
      p++, s++)
    ;
  return p - pwcs;
}

この関数は、書き込んだワイド文字数を返しますが、n文字を超えて書き込むことはなく、返却値がnの場合には、終端にナルワイド文字が格納されません。

"C"ロケール以外に対応させるには、内部的にmbtowc関数を呼び出した方がよいのかもしれませんが、全てのライブラリ関数はmbtowc関数を呼び出さないかのように振舞わなければなりませんので、実際にはもう一工夫する必要がありそうです。
2006/06/19 06:31|一般ユーティリティTB:0CM:0

 

wctomb関数は、mbtowc関数の逆で、ワイド文字から多バイト文字へかんかんするための関数です。例によって、現時点では"C"ロケールにしか対応しませんので、実装はいたって簡単です。

#include <stddef.h>

int wctomb(char *s, wchar_t wc)
{
  if (s == NULL)
    return 0;
  *s = (char)wc;
  return 1;
}

sが空ポインタの場合、多バイト文字がシフトシーケンスに依存する場合は非0を、それ以外は0を返す必要があるので、今回は0を返しています。また、wcがナルワイド文字の場合は、シフトシーケンスを初期状態に戻したりといった仕様がありますが、ここでは関係ないので反映されていません。

ところで、sが指す配列はMB_CUR_MAXバイト以上であることが要求されています。C99より前のバージョンの場合、可変長配列が使えませんので、汎整数式とは限らないMB_CUR_MAXを配列の要素数とすることはできず、代わりにMB_LEN_MAXを使わざるを得ませんでしたが、C99ではMB_CUR_MAXを使うことができます。ただし、実際のところ、どちらが効率がよいかは微妙なところです。

2006/06/15 10:56|一般ユーティリティTB:0CM:0

ホーム 全記事一覧 << 前ページ 次ページ >>

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

このブログをリンクに追加する

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. template designed by 遥かなるわらしべ長者への挑戦.
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。