C言語基礎|atoi と atof の使い方

文字列の数字を“そのまま計算”に変える!atoi と atof を使って、入力処理を一気にラクにしよう。

C言語では、キーボード入力やファイルから読んだ値が、まず文字列として手に入ることがよくあります。
そこで活躍するのが atoi(整数へ)atof(実数へ)。どちらも「数字っぽい文字列」を数値型に変換してくれる便利な関数です。

ただし、便利なぶん注意点もあります。とくに 変換できないときの判定が難しいオーバーフロー時の動作が保証されない など、落とし穴も一緒に覚えておくのがコツです。

atoi / atof とは何をする関数?

まず全体像(表)

関数変換先の型役割ヘッダ
atoiint文字列 → 整数stdlib.h
atofdouble文字列 → 浮動小数点数stdlib.h
atollong文字列 → longstdlib.h
atolllong long文字列 → long longstdlib.h

この表の見方(説明)

  • 「どの関数がどの型に変換するのか」を一発で整理するための表です。
  • atoi と atof を中心に、同じ仲間(atol / atoll)も並べて、使い分けが分かるようにしています。

書式(関数の形)と意味

atoi

  • ヘッダ:#include <stdlib.h>
  • 形式:int atoi(const char *nptr);

何をする関数か

  • nptr が指す文字列を int に変換します。

重要な注意

  • 変換結果が int の範囲に収まらないときの動作は定義されません(要するに安全ではない)。

atof

  • ヘッダ:#include <stdlib.h>
  • 形式:double atof(const char *nptr);

何をする関数か

  • nptr が指す文字列を double に変換します。

重要な注意

  • 変換結果が double で表現できないときの動作は定義されません(こちらも安全ではない)。

変換のルール(どう読んで、どこで止まる?)

atoi / atof は、文字列の先頭から「数値として読める部分」を読み取って変換します。イメージはこんな感じです。

変換の読み取りイメージ(図)

入力: "  -12abc"
       ↑空白は飛ばす
        ↑符号OK
         ↑数字を読む
           ↑ここで止まる(aは数字じゃない)
結果: -12

この図の説明

  • 先頭の空白をスキップ → 符号(+/-)を読む → 数字が続く限り読む → 途中で非数字が出たらそこで終了、という流れを示しています。
  • “全部が数字である必要はない” という点が、初心者がつまずきやすいポイントです。

よくある入力と結果(表)

入力文字列atoi の結果atof の結果ひとこと
123123123.0ふつうにOK
-45-45-45.0符号OK
12abc1212.0途中で止まる
abc1200.0先頭から数値にできず 0
000.0これも 0(判別しづらい)
3.1433.14atoi は小数点で止まる

この表の説明

  • 「途中に文字が混ざっていても、先頭部分が数字なら変換される」こと
  • 「変換できないときも 0 になりがちで、0 と区別しにくい」こと
    この2点をまとめて確認するための表です。

atoi / atof は手軽ですが、エラー判定が弱いのが最大の弱点です。

弱点(表)

困りごと何が起きる?どう対策する?
変換失敗の判定失敗でも 0 を返しやすい(0と区別できない)strtol / strtod を使う(変換終了位置を確認できる)
オーバーフロー範囲外のとき動作が保証されないstrtol / strtod と errno を使って判定
入力の安全性scanf で長い入力が来ると危険fgets で行を読み、必要なら解析

この表の説明

  • ここでは「atoi / atof が簡単だけど危険になりやすい理由」を整理しています。
  • すぐに置き換えられる代替として strtol / strtod を名前だけでも覚えておくと、実務で助かります。

※とはいえ学習段階では、まず atoi / atof で「文字列→数値」の感覚を掴むのが大事です。

サンプルプログラム

人数(整数)1人あたり料金(小数) を文字列で受け取り、合計を計算するプログラム例です。

プロジェクト名:chap11-12-1 ソースファイル名:chap11-12-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char s_people[128];
    char s_price[128];

    puts("人数と1人あたり料金を入力して、合計金額を計算します。");

    printf("人数(例 3):");
    scanf("%127s", s_people);

    printf("1人あたり料金(例 1250.5):");
    scanf("%127s", s_price);

    int people = atoi(s_people);
    double price = atof(s_price);

    double total = people * price;

    printf("人数=%d、料金=%.2f なので、合計=%.2f です。\n", people, price, total);

    return 0;
}

このプログラムで出てくる命令(関数)も押さえよう

名前何をする?ここでの役割
puts文字列+改行を表示案内文を出す
printf書式付きで表示入力促し、結果表示
scanf標準入力から読み取る文字列として入力を受け取る
atoi文字列を int に変換人数にする
atof文字列を double に変換料金にする

この表の説明

  • 「変換だけでなく、周辺の入出力関数もセットで理解する」ための表です。
  • とくに scanf は入力の受け取りで必ず登場するので、役割をはっきりさせています。

使い方のコツ(安全に寄せる書き方)

➀ 文字列入力は幅指定をつける

上の例の scanf は %127s を使っています。これは 128バイト配列に最大127文字+終端\0 を入れるためです。
幅指定を付けないと、長い入力で配列をはみ出して危険です。

➁ atoi / atof の結果が 0 のときは注意

0 には2種類あります。

  • 入力が本当に 0
  • 変換できなくて 0

ここを厳密に判定したいなら、学習が進んだら strtol / strtod に移行すると安心です。

演習問題

条件:添字演算子を使わずに実現(ポインタ走査で)

演習11-7:文字列を表示する関数

文字列 s を表示する関数 put_string を作成せよ。添字演算子を使わずに実現すること。
void put_string(const char *s);

解答例

プロジェクト名:chap11-12-2 ソースファイル名:chap11-12-2.c

#include <stdio.h>

void put_string(const char *s)
{
    while (*s) {
        putchar(*s);
        s++;
    }
}

解説
*s で現在の文字を参照し、1文字出力したら s++ で次へ進みます。ナル文字で止まります。

演習11-8:文字の出現回数を数える

文字列 s の中に文字 c が含まれている個数を返す関数 str_chnum を作成せよ。
int str_chnum(const char *s, int c);

解答例

int str_chnum(const char *s, int c)
{
    int n = 0;
    while (*s) {
        if ((unsigned char)*s == (unsigned char)c)
            n++;
        s++;
    }
    return n;
}

解説
走査しながら一致したらカウントします。int c は putchar 系と相性が良い形です。

演習11-9:最初に見つかった文字へのポインタを返す

文字列 s の中に文字 c が含まれていれば、その最も先頭側の位置へのポインタを返せ。なければ NULL を返せ。
char *str_chr(const char *s, int c);

解答例

プロジェクト名:chap11-12-3 ソースファイル名:chap11-12-3.c

#include <stddef.h>

char *str_chr(const char *s, int c)
{
    while (*s) {
        if ((unsigned char)*s == (unsigned char)c)
            return (char *)s;
        s++;
    }
    return NULL;
}

解説
見つけた瞬間に返すのがポイントです。返り値は「その文字の位置」です。

演習11-10:大文字化・小文字化(2関数)

次の2関数を添字演算子なしで実装せよ。
void str_toupper(char *s);
void str_tolower(char *s);

解答例

プロジェクト名:chap11-12-4 ソースファイル名:chap11-12-4.c

#include <ctype.h>

void str_toupper(char *s)
{
    while (*s) {
        *s = (char)toupper((unsigned char)*s);
        s++;
    }
}

void str_tolower(char *s)
{
    while (*s) {
        *s = (char)tolower((unsigned char)*s);
        s++;
    }
}

解説
ctype.h の toupper / tolower は unsigned char にして渡すのが安全です。*s を直接書き換えます。

演習11-11:数字文字を削除する

文字列 str 内のすべての数字文字を除去する関数 del_digit を作成せよ。
例:AB1C9 → ABC
void del_digit(char *str);

解答例

プロジェクト名:chap11-12-5 ソースファイル名:chap11-12-5.c

#include <ctype.h>

void del_digit(char *str)
{
    char *src = str;
    char *dst = str;

    while (*src) {
        if (!isdigit((unsigned char)*src)) {
            *dst = *src;
            dst++;
        }
        src++;
    }
    *dst = '\0';
}

解説
src で読み、dst に「残す文字だけ」詰め直します。最後に dst にナル文字を置いて完成です。

演習11-12:atoi / atol / atoll / atof 相当を作る

次の2関数を作成せよ。添字演算子なし。

  • my_atoi:空白スキップ、符号、数字列まで対応
  • my_atof:空白スキップ、符号、整数部と小数部(指数は不要)まで対応

解答例

プロジェクト名:chap11-12-6 ソースファイル名:chap11-12-6.c

#include <ctype.h>

int my_atoi(const char *s)
{
    while (isspace((unsigned char)*s)) s++;

    int sign = 1;
    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') { s++; }

    int x = 0;
    while (*s >= '0' && *s <= '9') {
        x = x * 10 + (*s - '0');
        s++;
    }
    return sign * x;
}

double my_atof(const char *s)
{
    while (isspace((unsigned char)*s)) s++;

    int sign = 1;
    if (*s == '-') { sign = -1; s++; }
    else if (*s == '+') { s++; }

    double x = 0.0;
    while (*s >= '0' && *s <= '9') {
        x = x * 10.0 + (*s - '0');
        s++;
    }

    if (*s == '.') {
        s++;
        double base = 0.1;
        while (*s >= '0' && *s <= '9') {
            x += (*s - '0') * base;
            base *= 0.1;
            s++;
        }
    }
    return sign * x;
}

解説
atoi / atof の「読めるところまで読む」挙動を、ポインタ走査で再現しています。途中に文字が混ざったらそこで止まります。