プログラミング序論 page5(update:2017/09/07)
[ index | prev | next ]課題3,課題4 まとめ printfの使い方 scanfの使い方 Cの標準入出力

5.入出力

CUICharacter-based User Interface)での入出力について概要とCプログラムからの使い方を説明する。
   CLI(command line user interface)
ともいわれる
   ※GUI
Graphical User Interface)での入出力については扱わない。

(2.入出力と演算子2.1〜2.2)

5-1. 入出力装置

計算機とデータを交換するするために必要な装置。コンピュータから入出力装置へデータを渡す処理を出力(output)、逆を入力(input)と言う。

入出力装置:

入出力の歴史

計算機を利用するためには、データを与え結果を受け取ることが必要だ。しかし、これはそんなに簡単ではない。スイッチを倒してON/OFFを入力し、ランプの点滅で結果を受け取るような原始的な状況から始まって、現在はGUIが可能なところまで来た。今後は人間と自然言語で会話する方向へ向かうのかもしれない。

ビット単位での入出力

複数のON/OFFスイッチで直接アドレスとデータを指定してメモリーに書き込んだり、読み出してランプの点滅で表示する。
   
ハードウエアと直接データを交換するような入出力。ここでは触れない。
※最初期のパーソナルコンピュータAltair 8800の入出力はこのようなビット単位のものだった。
操作例の動画 YouTube http://www.youtube.com/watch?v=vAhp_LzvSWk
 

文字列単位での入出力

CUICharacter-based User Interface
文字に対応しない改行なども含むので、一般化してバイト列での入出力と捕らえるほうが正確。

1)テレタイプteletype
通信回線で文書を交換する装置(キーボード、タイプライタ、テープ穿孔機などがついた通信装置)
通信に使われる文字コードにはタイプされる文字以外に、改行したりベルを鳴らしたりする制御コードが含まれていた。

コンピュータの入出力装置として利用されたりもした。

2)端末装置(computer terminal)
キーボードと文字を表示するキャラクタディスプレイをもつ計算機専用端末装置。1980年代までは1台の計算機に複数の端末をつないで時分割で計算機を共用するシステム(TSS)が広く使われていた。

入力や出力は一列に並んだバイト列単位で受け渡される。バイト列の入出力のためにC言語は標準入出力関数を用意している。



3)コマンドプロンプトなどの端末エミュレータ
現在はグラフィックディスプレイを用いキーボード以外にマウス等も使うGUI
Graphical User Interface環境が 一般的になった。しかし、CUIは単純なこともありその用途も多い。そこで、端末装置からの利用形態を模擬するプログラムが用意されている。これを一般に端末エミュレータと呼ぶ。たとえば,Windowsにはコマンドプロンプトと呼ばれる 端末プログラムがある。


CUIは計算機の重要な入出力システムです

バイト列による入出力は01のパターンを処理する計算機の仕組みと相性が良く、仕組みも簡単な分だけ色々な使い方もできます。例えばCUIでデータ交換を行うプログラムであれば、途中に人間が入らなくてもプログラム同士でデータ交換が可能です。WebブラウザはWebサーバーへ要求内容を示すバイト列を送り、返事のバイト列を受け取つて,ウインドウに絵として表示します。

※皆さんはGUIでPCを使えると思いますが、GUIの仕組みは分からないのではありませんか?CUIは人間には煩雑で使いにくいかもしれませんが、仕組みは簡単で解り易いものです。

標準入出力

データの入力や出力装置は複数繋がっているが、この中で既定値として設定された入力を標準入力(standerd input)同じく既定値として設定された出力を標準出力(standerd output)と言う。

 

[メモ1]
 入出力のような計算機の状況で多様なものは、OSのプログラムを呼び出して、状況に応じた適切な処理を依頼する仕組みになっている。依頼されるOSのプログラムで具体的な入出力は行われる。 OS側で入出力の相手を切り替える こともできる。 標準入出力先の切り替えをリダイレクトと言う。リダイレクトにより標準入出力先を キーボードと端末画面からハードディスク上のファイルに変更するようなことも可能。

例えばキーボードから試験の点数を入力し画面に平均点を書き出すようなCのプログラムを利用して、点数を書きこんだテキストファイルから入力を行い結果を別のファイルに書き出すといった使い方もできる。 
 ※平均値を計算するプログラム(ソース:mean.c 実行ファイルmean.exe

[メモ2]
標準入出力では入出力用のデータを一時的にためるファイル バッファが使われる。このバッファを含む入出力用のデータ領域を指すポインターとして標準入力ではstdinが、標準出力ではstdoutが用意されている。一度にたくさんの入出力があっても処理しきれなくならないように、ここでバファリングをする。

バッファはデータを渡す側と受け取る側のタイミングや特性の差を埋めるために、途中でデータが受け取られるまで溜めておく仕組みです。

※郵便ポストに投函されるたびに郵便局員が取りに行っていては大変です。そこで,ポストに貯めておいて,時間を決めて集めに行きます。バッファはこのポストの役割をするものです。

HDDは高速回転する円盤の表面に磁気的な情報としてデータを記録します,円盤の回転により磁気ヘッドの下を通過した部分のデータを読み出したり,書き変えたりします。連続した読み出しや書き込みは高速に行えますが,円盤の記録する場所の先頭に磁気ヘッドの位置を合わせる頭出しには数msほど時間がかかります。この時間はプログラムを実行する時間に比べると100万倍ぐらい遅いので,データの書き出しのたびにHDDに書き込むと時間がかかりすぎます。このため,HDD上のファイルデータを扱うときはファイルバッファを使います。

※ ポインター:別の場所にあるモノを指し示すもの。C言語ではメモリーの番地を記憶する変数をポインターとして用いる。

ここで、一つ注意しなければならないことがある。それは、出力したデータが標準出力stdoutに溜まっていて相手先に送り出されていない場合があることだ。そこでバッファの中身を出力する命令fflushが用意されている。これを用いて
fflush(stdout);
のように命令文を書くことで、stdoutに溜まっているデータを送り出すことができる。

標準の入出力関数

関数は名前で呼び出すことのできるプログラム。入出力はほとんどのプログラムで必須 だが、作るのは大変。そこで標準の関数として入出力関数が用意されている。ここでは以下の4つの関数を紹介する。

(注)入出力関数の名前、引数の種類と順番、戻り値の型など、関数を使う為に必要な情報はヘッダーファイル「stdio.h」に記述されている。何度も出てきた#include<stdio.h>はこのヘッダーファイルを取り込む為のものである。

5-2. 標準出力への書き出し

標準出力へ送られた文字列は通常はWindowsのコマンドプロンプトの様な端末画面に 文字として表示される。文字列に改行などを指示する制御文字が含まれる場合は改行などの印字位置の変更が行われる。

5-2-1. putchar関数

標準出力に1バイト(引数の値の下位8ビット)のデータを送る関数。

#include<stdio.h>
int main(void)
{
    /*十進65はASCIIコード表では文字「A」のコード番号*/
    putchar(65);/*標準出力に表示されるのは十進数65に対応する文字「A」*/
    putchar('\n');/*改行を指示するコードを標準出力に送る*/
    return 0;
} 

関数のプロトタイプ宣言

putchar関数は次のようにプロトタイプ宣言されています。

int putchar(int c);

この宣言は,初めのint関数が戻す戻り値のデータ型。次のputchar関数名。最後の( )内のintは関数に渡す値を代入する仮引数(変数の一種)のデータ型。C言語が関数にデータは渡す方法は値渡しと呼ばれる。この手法では,関数を呼び出すたびに仮引数を作り,実引数の値を仮引数に代入して
渡す。詳しくは関数の説明のところを見てください。

※コンパイラに何らかの情報を与える記述を一般を宣言という。

1)関数プロトタイプ宣言:戻り値、関数名、仮引数のデータ型のみを示した宣言。 コンパイラが関数を呼び出す部分の機械語を作るのに必要な情報を与える宣言です。この宣言の情報は翻訳やリンクで使われます。


2)関数定義:戻り値、関数名、仮引数のデータ型さらに内部の処理手順(プログラム)まで記述したもの。宣言の一種ですが機械語に翻訳されてメモリーの上に実体を作る。このような実態を作る宣言を特に、定義といいます。

前記のプログラムではmain関数の定義が書かれていますが、putchar関数についてはインクルードするstdio.hにプロトタイプ宣言が書かれているだけです。ただし,最近はputchar関数の定義を書いているものもあります。この場合でも,putchar関数が動くために必要な全てが書かれているわけではありません。putchar関数の中から呼ばれる関数の部分までは書かれていないなど,リンク処理で不足部分を追加する必要があります。

●戻り値:int型
標準出力に送った1バイト(0〜255の範囲)の値。エラーが起きた場合はEOF(多くの場合−1)を戻す。

※戻り値がcharよりも広い範囲の値を持てるint型なのでputchar関数内でエラーが起きた場合も戻り値で示せる。

●引数:int型
 仮引数のデータ型は int型。 ここに関数を呼び出すときにカッコ( )の中に書かれた実引数の値を代入して,関数に渡す。実引数を’A'のようにchar型のリテラルにした場合でも、自動型変換によりint型のデータに変換してから仮引数に代入される。

putchar 関数の処理内容

仮引数で 渡されたint型データの下位1バイトを切り出して標準出力に送る
※標準出力のファイル・バッファに書き込むところまでがputchar関数の処理範囲です。

※putcharは標準出力に1バイトを送る関数である。 しかし、戻り値と引数は共にint型。

標準出力からの文字を受けとって端末画面に印字するのは端末側の仕事です。受け取った文字が表示可能な文字ならば現在のカーソル位置に対応する文字を表示し、1文字分カーソル(印字位置を示すマーク)を進める。改行などの制御コードならば、それに応じ てカーソルを移動する。

注意:1バイトの書き出しに使うのがputcharです。文字列や数値の文字列を標準出力に書き出す目的にはprintf を用いる。

[メモ]関数の呼び出し

関数は名前で呼び出すことのできるプログラムで、関数のプログラムを実行することを関数を呼び出すと言う。

関数の呼び出しは
   putchar(a+b);
のように関数名の後の( )に関数に渡す値を指定する。この値を与える記述を実引数という。
実引数は値を示すものであればいい。65のようなリテラルでも、変数でも、a+bのような計算式でもよい。

関数呼び出しの手順

関数を呼び出す側の機械語プログラムは新たな仮引数を作り、実引数の値を格納する。この後、呼び出す関数の機械語プログラムの先頭にプログラムの実行位置を変える。

呼び出された関数側では仮引数を使って戻り値を計算し、戻り値のデータ型に応じて決められた場所に値を記憶する。
※戻り値の記憶にはレジスタを使うことが多い

呼び出しから戻ってきたとき、呼び出した側は必要なら戻り値を変数に代入し記憶する。

 
メモリー
番地
機械語プログラム コンパイルやリンクに
必要な情報
  呼び出し側  
  .....  
  仮引数を作り 仮引数のデータ型
  実引数の値を計算して仮引数に代入  
  戻る場所RETを記録  
  呼び出す関数の先頭番地FUN
実行位置を跳ばす
跳び先番地はリンク時に関数名で検索し機械語に変換済み。
RET 戻り値を受け取る 関数の戻り値のデータ型
  .....  
  .....  
  呼ばれる関数  
FUN 仮引数をもとに計算を行う  
  戻り値をデータ型に応じた場所に記憶  
  RETに実行位置を戻す  

 関数を呼び出すプログラムを機械語に翻訳するとき、このような仕組みのため戻り値のデータ型、関数名、引数のデータ型の3つの情報が必要になる。この情報は関数プロトタイプ宣言と呼ばれる記述で示すことができる。#includeで取り込むヘッダーファイルには、この関数プロトタイプ宣言などがまとめて書かれている。

※再度
関数プロトタイプ宣言:戻り値、関数名、引数のデータ型のみを示した記述。
関数定義:戻り値、関数名、引数のデータ型さらに内部の処理手順(プログラム)まで記述したもの。

5-2-2. printf 関数

書式で指定された変換を行い、結果の文字列を標準出力に送る。

printf("Hello"); のような記述でprintf関数を使うと「Hello」等の文字列をそのまま書き出すことができます。

さらに、printf関数は数値などのデータを文字列に変換して書き出す機能を持っています。このような変換を伴う書き出しの場合にはprintf関数の最初の引数で変換方法を指定する書式を与え、2番目の引数に変換する値を与えます。ここで引数の区切りはカンマで示します。

printf(引数1, 引数2); 引数1は書式(フォーマット)を示す出力書式文字列です。引数2は文字列に変換する値です。

書式指定子で具体的な変換方法を指示します。例えば10進整数文字列へ変換は「%d」で、次のようなプログラムを書くことができます。

#include<stdio.h>
int main(void)
{
        int i=200;
        printf("変数iの値は");
        printf("%d", i);/*ここでiの値を文字列「200」の3文字に変換して書き出す*/
        return 0;
}

上の例はprintfを2回使いましたが、まとめて次のように書くこともできます。

#include<stdio.h>
int main(void)
{
        int i=200;
        printf("変数iの値は%d", i);/*ここで%dの部分を「200」に置き換えて書き出す*/
        return 0;
}

さらに、書式指定子を複数使って複数のデータを一度に書き出すことも可能です。この場合は3番目、4番目の引数で対応する部分の値を示します。

#include<stdio.h>
int main(void)
{
        int i=200;
        printf("複数の書式指定 %d %d %d", i, i+1, i+2);
        return 0;
}
/*実行結果
複数の書式指定 200 201 202
*/ 

 

[メモ]
    値123を文字列”123”に変換して標準出力に送る場合はprintf("%d",123);と書く。
  int型の値123はメモリ上のデータとしては{0x00,0x00,0x00,0x7B}の4バイトだが、{'1','2','3’}の3文字のコードは{0x31,0x32,0x33}となる。このような変換を行うプログラムを間違いなく書くのは大変。プログラムミスを避ける意味でもprintfを用いるのが正しい。

関数のプロトタイプ宣言

int printf(const char* format,...);

●戻り値:int型
 標準出力に渡したバイト数、エラーが発生した場合は負の値

●1個目の引数: const char* format (実引数には文字列リテラルを使うことが多い)
出力書式文字列。出力する文字列の書式を与えます。書式指定子以外はそのままの文字列で出力される。
(実は、
1個目の引数は文字型のポインターです。(データ型リテラル例題)。constは変更しない定数の意味です。)

出力書式の指定方法:
(1) printf("ABC123亜位宇"); 引数の文字列をそのまま標準出力に送る。
(2) printf("%cの文字コードは%d",65,65); %c,%dなどの書式指定子の指示に応じて、後ろの引数 の値を文字列に変換し %c%dなどを置き換えたバイト列を作り標準出力に送る。
(3) printf("%7.5f",3.142592);の様に桁数を指定することも可能で、この場合四捨五入を行って「3.14159」と出力される。

※書式指定子で使われる「%」を書き出したい場合は「%%」と書くと1文字「%」として書き出される:
”や\などの文字は\"や\\と書くと1文字の「"」や「\」として書き出される。

● 2個目以降の引数:  ...
  可変個引数で0個以上の引数の並び。呼び出しで引数の数が変化する様な場合の記号

※可変個引数では既定実引数拡張により。実引数の値を拡張した値を仮引数に代入して渡す。
int未満はintに、floatはdoubleに拡張されて関数に渡る。このためfloatとdoubleを書式指定子で区別していない。

printf 関数の処理内容

1個めの仮引数で 渡された文字列に書式指定が無ければそのまま文字列を標準出力に書き出す。書式指定が有れば,2個め以降の引数の値を文字列に変換して文字列を生成し標準出力へ書き出す。戻り値として書き出した総バイト数を戻す。
※標準出力のファイル・バッファに書き込むところまでがprintf関数の処理範囲です。

サンプルプログラム
#include<stdio.h>
int main(void)
{
    printf("%c\n",'A'); 
    printf("同じ-1でも 符号付%d 符号無%u 16進%x\n",-1,-1,-1);
    printf("%sは%f\n","円周率",3.14); 
    return 0;
}
/*
----実行結果----
A
同じ-1でも 符号付-1 符号無4294967295 16進ffffffff
円周率は3.140000
*/

※書式指定子%sは引数の文字列に置き換えて出力します。

printf 関数の書式指定子のまとめ

printf 関数では出力文字列の形式に対応した書式指定子が用意されています。

下の表の左端が関数の括弧内に並べられた実引数のデータ型です。関数に渡す仮引数のデータ型は既定実引数拡張によって2列目のようにintかdoubleに変換して渡されます。関数は仮引数で渡された値を3列目の書式指定子に基づいて、右端のような文字列に変換します。

実引数のデータ
仮引数のデータ型
(既定実引数拡張)
printfの書式指定子
出力の文字列
char int %c 1文字
Z
int %d  10進数
90
%x 16進数
5A
float double %f 実数
3.141593
double
char*char*%s文字列

※上記の書式指定子は覚えてほしい基本的なものだけをまとめています。教科書27-29pにより詳しく書かれているので参考にしてください。

5-3. 標準入力からの入力

標準入力からのバイト列を受け取る為の関数 。ここで紹介する関数は標準入力のファイル・バッファに受け取れる文字列があれば受け取って戻り値を返す。しかし,バッファが空の場合は受け取れるまで待つ。

標準入力には端末のキーボードで文字列に続いてEnterキーが押されたときに文字列が渡される。従って、以下の関数でキーボードのキー操作をリアルタイムに受け取るゲームプログラムの様なものは書けない。

5-3-1. getchar 関数

標準入力から1バイトのデータをそのまま受け取るには getchar 関数を使う。

#include<stdio.h>
int main(void)
{
        int c;
        c=getchar();
        printf("「%c」の文字コードは十進で%d 16進で%x です\n",c,c,c);
        return 0;
}
/*実行結果  A(Enter)の順にキーを押した
A    <<画面にエコーバックされる
「A」の文字コードは十進で65 16進で41 です <<cの値は65

*/ 

関数のプロトタイプ宣言

int getchar(void);

●戻り値:int型
 受け取った0〜255の範囲の値。エラーが起きた場合はEOF(多くの場合−1)を戻す。
※戻り値がcharよりも広い範囲の値を持てるint型なのでgetchar関数内でエラーが起きた場合も戻り値で示せる。
※戻り値をchar型の変数に代入しようとすると、コンパイラから警告される。この場合型変換を行う必要がある。

●引数:
 voidは引数が無いことを示している。しかし、関数を呼び出すときは、関数呼び出しである事を示すために空の( )は書く必要がある。

getchar 関数の処理内容

標準入力のファイル・バッファから1バイト取り出してint型に変換した値を戻すエラーが起きた場合はEOF(多くの場合−1)を戻す。 ファイル・バッファから1バイト受け取るが、残りはそのままバッファに残っている。バッファが空になるまでは,再度getcharを呼ぶと次の文字が受け取れる。

 

[メモ]
端末エミュレータはキーボードからの入力を一旦は溜めて、利用者に入力文字列の校正の機会を与える。標準入力へのデータ転送は利用者が入力を確定するEnterの入力が行われた時点で実行される。 getcharや次のscanfは入力データを受け取るまで待つているので、下のプログラムは利用者がEnterを入力した時点で結果を出力する。 さらに1文字ずつgetcharで処理しているとEnterによる改行も文字として取り込まれる。

#include<stdio.h>
int main(void)
{
        int c;
        c=getchar();
        printf("「%c」の文字コードは十進で%dです\n",c,c);
        
        c=getchar();
        printf("「%c」の文字コードは十進で%dです\n",c,c);
        
        return 0;
}
/*
実行結果1 AB(Enter)の順にキーを押した
AB                
「A」の文字コードは十進で65です
「B」の文字コードは十進で66です

実行結果2 A(Enter)の順にキーを押した
A                 
「A」の文字コードは十進で65です
「
」の文字コードは十進で10です<<ここはEnterキーに対応した改行文字を処理

*/

5-3-2. scanf 関数

書式付きの入力。

getchar関数は1バイトづつデータを読み取る。もし「100」の文字列が入力されたら、getchar関数は「100」の文字コードである3個の16進数 0x31,0x30,0x30を順番に読み取ることになる。この3個の数値から整数100を作るのは面倒だ。

入力からの文字列"100"を読んで整数データ型に変換して取り込むにはscanfを使う。
scanfは入力書式文字列とデータ を記憶するメモリ番地(メモリーアドレス)を与えて呼び出す。

scanf 関数のプロトタイプ宣言

int scanf(const char *format,...);

●戻り値:int型
読み込んだデータの数。値を1個読めたら1を戻す。エラーが起きた場合はEOF(多くの場合−1)を戻す。
※戻り値は読み込んだ値ではない。 読み込んだ値は2番目以降の引数で指示されたメモリ番地に書き込む。
※入力の文字列が書式に合わない場合は読み取りを行わず、戻り値は0となる。

●1個目の引数:const char *format
 入力を読み取るときの変換方法を書式指定子で指定した文字列。
例えば、%dは文字列を10進数表記とみなしてint型に変換する書式指定子、%lfは文字列をdouble型に変換する書式指定子です。scanfは入力された文字列を書式指定子に従ってビットパターンに変換します。

表のように書式指定子が入力文字列の種類と読みこむデータ型に応じて決められている。
※同じ実数を読む場合でもデータ型に応じてfloatなら%f、doubleなら%lfのように区別する必要がある。

入力の文字列 scanfの書式指定子 変換処理 データ
1文字
Z
%c  1文字として読み
char型で格納
char
10進数
90
%d 10進数として読み
int型で格納
int
16進数
5A
%x 16進数として読み
int型で格納
実数
3.1416
%f  float型で格納 float
%lf double型で格納 double

注意:値の区切りが空白や改行などなら区切り文字を示す必要はない。区切り文字がカンマなどの場合は以下のようにして示すことが可能

scanf("%d,%d,%d",&a,&b,&c);   /*「123, 465, 789」の様なカンマ区切りの文字列から3個の数値をa,b,cに読み取る場合*/

● 2個目以降の引数: データを格納するメモリー番地
 読み取った文字列を変換した値は、ここで指示された番地のメモリーに書き込まれる。
  ※ 値を書き込む番地が適切かどうかはチェックしない。

普通はデータを入れる変数を用意し、その変数のメモリー番地をアドレス演算子を用いて求め引数の値とする 。

&:アドレス演算子
変数名の前に&を付けると、その変数のメモリ番地の意味になる。コンパイラは変数に対応するメモリ番地を知っているので、翻訳時に番地に置き換えます。

データを読むためのプログラムは

  1. scanfを使うためにstdio.hをインクルードする
  2. データを記憶する変数を用意する
  3. scanf関数の第一引数に書式と第二引数以降に変数に割り当てられたメモリ番地を渡す
#include<stdio.h>
int main(void)
{
        int i;/*データを記憶する変数を用意*/
        double d;/*データを記憶する変数を用意*/

        scanf("%d", &i);/*入力を10進整数として読み、その値を変数iの番地のメモリーにint型で書きこむ*/
        scanf("%lf", &d);/*入力を実数として読み、その値を変数dの番地のメモリーにdouble型で書きこむ*/

        printf("i=%d d=%f \n", i, d);

        return 0;
}

コピー&ペーストして実行し結果を確かめてください。
このとき実行したら入力枠に「100 3.14」などを書き込んでエンターキーを押す必要があります。

scanf 関数の処理内容

書式指定に従って標準入力から文字列を読み取ります。改行や空白の文字は区切り文字として読みます。正常に値に変換できたら2番目以降の引数で指定されたメモリー番地に値を書き込みます。読み込まなかった文字はバッファに残りますから,続けてschanfを実行すれば次の値が読めます。

関数の戻り値で読み込めた数を返します。値が1個も読めない場合は0を戻します。この場合,2番目以降の引数で指定されたメモリー番地は書き換えません。

もし途中で変換に失敗した場合,読めなかった文字コードはバッファに残し,scanf関数はそこまでに読めた数を戻します。

標準入力が終端に達していて,これ以上の入力がない場合は,いくら待っても読み込めません。この場合はscanf 関数はEOF(多くの場合-1)を戻り値として戻ります。

 

#include<stdio.h>
int main(void)
{
        int a=0;
        int n;

        printf("aの値は%dです\n",a); 
        fflush(stdout);/*すぐに書き出してもらうため、この文を入れています*/
        
        n=scanf("%d",&a);/*データを格納するメモリ番地の値は&aで与える*/
        printf("%d個のデータを%d番地に読み込みました\n",n,&a);
        printf("aの値は%dです\n",a); 
        return 0;
}
/*実行結果
aの値は0です
123 <<ここで 123(Enter)の順にキーを押した。
1個のデータを1310432番地に読み込みました
aの値は123です
*/

[メモ]scanf 関数はトラブルを起しやすい。以下の様な点には十分注意して使うこと

(0)引数に正しくない番地を指定した場合。

最も良くある間違いは2番目以降のメモリアドレスの値のところに、値を入れたい変数名を&なしで書いてしまう間違いです。下の例ではscanf関数は読み込んだ値を10番地に書き込みます。このメモリーが他のことに使われていると実行時のプログラムエラーの原因となるでしょう。

int a=10;
scanf("%d",a);/*実際にこんな書き方をするとプログラムの暴走原因になります。*/
(1)書式指定子と異なる型の変数に読込んだ場合。

下記はint型の変数に%cで1バイト読込んだ場合。int型変数nの上位3バイトは書換えられないで最下位の1バイト部分だけが書き換わる。

#include<stdio.h>
int main(void)
{
        int n=0x12345678;/*変化を見る為に適当な初期値を設定*/
      
        /*変換指定と変数の型が一致しないと*/
        printf("nは 16進で%x です\n",n);
        fflush(stdout);
        scanf("%c",&n);
        printf("nは 16進で%x です\n",n);
       
        return 0;
}
/*
実行結果
nは 16進で12345678 です
1
nは 16進で12345631 です
 nの4バイト内の下位1バイトのみが、文字’1’に対応する文字コード0x31に変っている
*/
(2)書式指定子と異なる入力の場合。

この場合は入力文字列の変換に失敗しデータが読まれない。
scanf の戻す値が0になるなどで失敗を知ることが出来る。 このときデータを格納する変数は変化しない。

#include<stdio.h>
int main(void)
{
        int m,n;
        
        m=scanf("%d",&n);
        printf("読みこんだデータは%d個で値は%dです\n",m,n);
        fflush(stdout);

        m=scanf("%d",&n);
        printf("読みこんだデータは%d個で値は%dです\n",m,n);
        fflush(stdout);
      
        m=scanf("%d",&n);
        printf("読みこんだデータは%d個で値は%dです\n",m,n);
        
        return 0;
}
/*
実行結果1「123 456 789(Enter)」をキー入力
123 456 789
読みこんだデータは1個で値は123です
読みこんだデータは1個で値は456です<<何もしなくても空白文字は区切りに使える
読みこんだデータは1個で値は789です

実行結果2 「123,456,789(Enter)」をキー入力
123,456,789
読みこんだデータは1個で値は123です
読みこんだデータは0個で値は123です<<「,」が数値に変換できないので読めない
読みこんだデータは0個で値は123です
(注)m=scanf("%d,",&n);の様に読込みで数値の後に「」があることを示せば読込める
*/

※scanfで文字列を読み込むこともできますが、そのためには文字列を入れるメモリーの場所を作らなければなりません。この話は配列のところでしたいと思います。

演習課題3、4

課題3.文字コードの確認(課題3初期ファイル

SJISの文字コードを前提にした下記のプログラムを写経する。実行例の動作を確認すること。

if文など、まだ学んでいない構文が使われていますが、書き写すだけなのでめげずに頑張ってください。


※コメント部分は写さないように。
※プログラム中に日本語文字列が使われています。文字列以外の部分に倍角文字を入れないように注意。
※{ } と( ) は違います。間違えないように。
※字下げは重要なので必ず行うこと。(行の書き出し位置は手本のように下げること)
 IEで見ると手本の字下げが正しく表示されないことがあります。Firefoxで見てください

#include<stdio.h>
int main(void)
{
    /*字下げ{ }の内側は行の先頭に空白を4文字入れてください。*/
    
    /* まず使う変数を定義する */
    int c1,c2;

    /* 標準入力から1バイトづつ取り込んで変数c1,c2にint型の値として代入する */
    printf("コード値を調べる文字>");
    fflush(stdout);/*入力前に「コード値を調べる文字>」を必ず書き出すため*/   
    c1=getchar();
    c2=getchar();
    
    /* c1,c2を文字とコード値として標準出力に書く出す */
    if(c1<0x80) /*ASCIIコードの範囲内の文字の場合*/
    {
        /*字下げ{ }の内側は行の先頭に空白をさらに4文字入れてください。*/
        printf("%c:%02X\n",c1,c1);
    }
    else if(0xA0<c1 && c1<0xE0) /* 半角カナなどの範囲の文字の場合 */
    {
        printf("%c:%02X\n",c1,c1);
    }
    else /* 何れでもない場合は SJISの漢字など2バイト文字と仮定する */
    { 
        printf("%c%c:%02X%02X\n",c1,c2,c2,c1);
    }

    /* 標準入力から16進数文字列で入力された値を変数c1にint型の値として代入する */
    printf("文字コード値(16進表記)>");
    fflush(stdout);
    scanf("%x",&c1);/*scanfを使っているのでコンパイル時に警告されるかも*/
    
    /* c1の値に対応した文字を出力 */
    if(c1<0x100)/* 0x00~0xFFまでの1バイト文字コードの場合*/
    {
        printf("%02X:",c1);/*c1を16進数2桁の文字列に変換して出力*/
        putchar(c1);/*c1の下位1バイト(8ビット)分を出力*/
    }
    else /* 0x100以上の2バイト文字コードの場合*/
    {
        printf("%04X:",c1);/*c1を16進数4桁の文字列に変換して出力*/
        putchar(c1);/*c1の下位1バイト(8ビット)分を出力*/
        c2=c1>>8;/*c1のビットパターンを1バイト(8ビット)分下位へシフトした値をc2に代入*/
        putchar(c2);/*c2の下位1バイト分(c1の2バイト目)を出力*/
    }
    putchar('\n');

    return 0;
}
/* 実行例
コード値を調べる文字>
水:8590
文字コード値(16進表記)>8590
8590:水
*/

レポートツールで標準入力に文字列を与えるときの操作は下記を見てください。

レポートツールで標準入力を与える方法

次のようなプログラムの場合

※scanf関数を使うとワーニングがでますが、この処理系では、これはもっと安全な関数を用意しているので使いませんか?ということらしいです。しかし、ここではscanfを使います。

課題4 合計の計算(課題4初期ファイル

10進の整数 4個の文字列を標準入力からint型の変数に読み込んで合計を出すプログラムを作りなさい。

※上記以外の入出力は行わないこと

ヒント:

  1. 数値を格納するメモリーが必要なので4つの変数を用意しましょう。変数のデータ型に注意してください。
  2. 標準入力の文字列を受け取り数値に変換してメモリーに格納するscanf 関数を使います。
    この時に、関数には変換書式とメモリーの番地を教えることが必要になります。
    変数がa,b,cなら番地は&a,&b,&cで与えられます。
  3. 4つの変数を足し算したものをprintf 関数で出力します。
    足し算は変数名がabcなら a+b+c と式を書くことで合計値となります。
    このような式をprintfの( )内に2つ目の引数として直接書くことができます。
  4. scanfやprintfを使うので#include<stdio.h>を忘れないでください。
  5. main関数の戻り値は0にしてください。

[ index | prev | next ]