第 6 章 C 言語入門
6.9 いくつかのプログラム
6.9.1 printf 関数の利用法
printf 関数で第2引数にある文字列リテラル中に %dと書かれた部分は, 変換指定と呼ばれ, 変換指定
の出現順に,その後にある引数の値が変換指定の指定にしたがって表示される.
printf("b = %d, c = %d\n",b c) ;
とした場合には,b[=[の後にbの値が%dにしたがって表示され,,[c[=[の後にcの値が%dにしたがっ て表示される.
変換指定の書式は%の後に続く次のもので次の順序で指定される.
• 変換指定の意味を修飾する0個以上のフラグ. フラグ文字と意味は以下の通り.
- 変換結果を左詰めにする. これがないと変換結果は右詰めになる.
+ 符号付きの変換結果を常に+または-ではじめる. これがないと,非負の値には符号はつかない.
空白 符号付きの変換結果が符号で始まらない場合,または結果の文字数が0の場合,変換結果の前に 空白をつける. 「空白」と+がともに指定されたときには「空白」は無視される.
# o変換に関しては先頭に0をつける. xまたはX変換に関しては先頭に0xまたは0Xをつける. e, E, f,F, g, G変換に関しては, 小数点文字の後に数値が続かないときにでも,小数点文字を表示す る. g, G変換に関しては,後ろに続く0を結果から取り除かない. それ以外に関しては不定.
0 d,i,o,u,x,X,e,E,f,g,G変換に関しては,0をフィールド幅に左詰め込みに利用する.
• 省略可能なフィールド幅. 値を変換した結果がこの文字数よりも少ないときには空白を詰め込む.
• 省略が可能な精度. 精度の形式は. の後に10進整数を指定する.
– d,i,o,u,x,X変換に関しては,出力すべき最小の桁数.
– e,E,f変換に関しては,小数点文字の後に出力すべき桁数.
– g,G変換に関しては,最大の有効桁数.
– s変換に関しては,文字列から書き出すことの出来る最大の文字数.
• 省略が可能なh,l,Lのいずれか.
h d, i, o, u,x, X 変換の場合には, 対応する実引数が short または unsigned shortであることを 示す.
l d,i,o,u,x,X変換の場合には,対応する実引数がlongまたはunsigned longであることを示す.
L e,E,f,g,G変換の場合には,対応する実引数がlong doubleであることを示す.
• 変換形式を示す文字.
d,i int型の実引数を[-]dddd形式で10進表記する.
o,u, x, X unsigned int 型の実引数を,符号なし8進(o),符号なし10進 (u),符号なし16進 (x, X)表記する. xの時には文字abcdefを用い,Xの時には文字ABCDEFを用いる.
Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp
f double型の実引数を[-] dddd.ddddの10進表記にする. 精度が省略されたときには6 であると 解釈する. また,最終桁は適切な桁数への丸めを行う.
e double型の実引数を[-] d.ddde + ddまたは,[-] d.ddde - ddの10進表記にする. 精度が省 略されたときには6 であると解釈する. また,仮数部の最終桁は適切な桁数への丸めを行う. E変 換の場合には,指数を表す文字をeではなく,Eを用いる.
g,G double 型の実引数を有効桁数を指定する精度に従い,fまたはe 形式で変換する. (Gの場合は E 形式)変換の結果得られる値の指数が−4 より小さい, または精度以上の場合には, eまたは E 形式を用いる.
c int型の実引数をunsigned char型に変換し,その結果の文字を出力する.
s 実引数は文字型の配列へのポインタでなければならない. 配列内の文字を文字列終端まで表示する.
p 実引数はvoid へのポインタでなければならない. そのポインタの値を処理系定義の方法で表示可 能文字に変換する.
% 文字%を出力する. 対応する実引数はない. 変換指定全体が%%でなければならない.
したがって,以下のように利用することが出来る. (詳しくはオンライン・マニュアルman -s 3S printf を参照.)
char a = ’a’ ;
int b = -1 ;
long c = 10L ;
unsigned int d = 2U ; char s[3] = "ab" ; double x = 1.0e-4 ; double y = 1.0e-5 ; long double z = 1.0L ; printf("a = %c\n", a) ; printf("a = %x\n", a) ;
printf("b = %d, c = %ld\n",b, c) ; printf("b = %d, c = %+ld\n",b, c) ; printf("b = % .3d, c = % .3ld\n",b, c) ; printf("b = %0.3d, c = %0.3ld\n",b, c) ; printf("c = %3ld\n",c) ;
printf("d = %u\n", d) ; printf("d = %0.5u\n", d) ; printf("d = %X\n", d) ; printf("d = %.3x\n", d) ; printf("d = %#.3x\n", d) ; printf("s = %p\n", (void *)s) ; printf("x = %f\n", x) ;
printf("y = %e\n", y) ; printf("x = %G\n", x) ; printf("y = %g\n", y) ; printf("z = %LE\n", z) ;
a[=[a a[=[61
b[=[-1,[c[=[10 b[=[-1,[c[=[+10 b[=[-001,[c[=[[010 b[=[-001,[c[=[010 c[=[[10
d[=[2 d[=[00002 d[=[2 d[=[002 d[=[0x002 s[=[effff9c8 x[=[0.000100 y[=[1.000000e-05 x[=[0.0001 y[=[1e-05
z[=[1.000000E+00
6.9.2 プログラムの演習
Exercise 6.9.1 色々な型の演算の値を出力するプログラムを書け. 例えば,次のようなものである.
#include <stdio.h>
int a,b ;
int main(int argc, char **argv) {
printf("%d + %d = %d\n", a, b, a + b);
return 0 ; }
Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp
Exercise 6.9.2 次のプログラムの出力結果がなぜそのようになるかを考えよ.
#include <stdio.h>
int x=2, y, z ;
int main(int argc, char **argv) {
y = z = x ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y == z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = (y == z) ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; return 0 ;
}
ヒント: =,==の意味と結合規則を考えよ. =と ==は入力ミスをおかしやすく,バグに直結するミスで あることに注意.
Exercise 6.9.3 次のプログラムの出力結果がなぜそのようになるかを考えよ.
#include <stdio.h>
int x, y, z ;
int main(int argc, char **argv) {
x = y = z = 1 ; ++x || ++ y && ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y = z = 1 ;
++x && ++ y || ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y = z = 1 ;
++x && ++ y && ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y = z = -1 ;
++x && ++ y || ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y = z = -1 ;
++x || ++ y && ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; x = y = z = -1 ;
++x && ++ y && ++z ;
printf("x=%d,y=%d,z=%d\n",x,y,z) ; return 0 ;
}
ヒント:&&,||,++の意味と評価順序を考えよ. これは,変数の値によっては,評価が行われない部分が あり,このようなコードを書いてはいけない典型的な例である.
Exercise 6.9.4 次のプログラムの出力結果がなぜそのようになるかを考えよ.
Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp
#include <stdio.h>
int x, y, z ;
int main(int argc, char **argv) {
x = 3 ; y = 2 ; z = 1 ; printf("%d\n", x | y & z) ; printf("%d\n", x | y & ~z) ; printf("%d\n", x ^ y & ~z) ; printf("%d\n", x & y && z) ; x = 1 ; y = -1 ;
printf("%d\n", !x | x) ; printf("%d\n", ~x | x) ; printf("%d\n", x ^ x) ;
x <<= 3 ; printf("x = %d\n", x) ; y <<= 3 ; printf("y = %d\n", y) ; y >>= 3 ; printf("y = %d\n", y) ; return 0 ;
}
この中には,処理系依存になっているものがある. どれが処理系依存かを考えよ.
Exercise 6.9.5 次のプログラムの出力結果がなぜそのようになるかを考えよ.
#include <stdio.h>
char c ;
unsigned char n ; double d ;
float f ; long l ; int i ;
int main(int argc, char **argv) {
c = 0x7F ; printf("c=%X\n",c) ; c = 0x80 ; printf("c=%X\n",c) ; n = 0x7F ; printf("n=%X\n",n) ; n = 0x80 ; printf("n=%X\n",n) ; i = l = f = d = 100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; d = f = l = i = 100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; i = l = f = d = 100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; d = f = l = i = (float)100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; i = l = f = d = (float)100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; i = l = f = d = (double)100/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; i = l = f = d = 100.0/3 ;
printf("i=%8d\tl=%.8ld\tf=%.8f\td=%.8f\n", i,l,f,d) ; return 0 ;
}
ヒント:算術変換が各所に含まれている.
Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp
Exercise 6.9.6 short,int,unsigned int,longのそれぞれの型の変数が何バイトの記憶領域をとるか を表示するプログラムを書け.
Exercise 6.9.7 int, longのそれぞれの型で表現される最大の数に1 を加えたらどうなるかを考察せよ.
Exercise 6.9.8 正の浮動小数点数の小数点以下を四捨五入した値を求めるプログラムを書け.
Exercise 6.9.9 AND, OR, NOTからXORを作れ.
Exercise 6.9.10 Example 6.8.6はどうしてかを考察せよ.
Exercise 6.9.11 intが16ビット,long が32ビットの時,-1L < 1U,-1L > 1ULとなる. 何故か?