Transcript Document

C プログラミング入門
総機1 (月1)
12: コマンドライン引数
Linux にログインし、以下の講義ページ
を開いておくこと
http://www-it.sci.waseda.ac.jp/
teachers/w483692/CPR1/
2015-06-29
1
まとめ:ポインタを使った処理
内容
呼び出し元の変数を書き換える
文字列を渡す・配列を渡す
ファイルポインタ
複数の値を返す
大きな領域を確保する
2015-06-29
第
第
第
第
第
C プログラミング入門 総機1 (月1)
説明
9回
10 回
10 回
11 回
11 回
2
今回の内容
コマンドライン引数の取り扱い
シェルから引数 (オプション) を受け取る
技術的には二重ポインタ (double pointer) である
• ポインタへのポインタ
• 秋期の「Cプログラミング」で使うが、この講義ではあ
まり踏み込まない
文字列から数値への変換
コマンドライン引数は単なる文字列なので、数値
として扱うには変換が必要
2015-06-29
C プログラミング入門 総機1 (月1)
3
復習:アドレスとポインタ
メモリ上の位置を表す値
型を持つ
変数 a のアドレスは &a
ポインタ変数に格納できる
アドレス値のことをポインタとも呼ぶことがある
int a
{
100
int *p
2015-06-29
int a = 100;
int *p = &a;
矢印が指す位置のア
ドレスを持っている
printf("%d", *p);
C プログラミング入門 総機1 (月1)
4
復習:アドレス演算と読み書き
デリファレンス演算子 * で、アドレスが指す
位置の内容を読み書きできる
添え字演算子 [] で、ポインタの指す位置を
ずらして読み書きできる
ポインタ p に対して *(p+n) と p[n] は同じ
-23
p[0] == *p
== 23
p
85
p[1] == *(p+1)
== 85
矢印が指す位置のア
ドレスを持っている
2015-06-29
メモリ上のデータをどんな値とみなすか
は、ポインタの型で決まる
C プログラミング入門 総機1 (月1)
5
復習:配列とポインタ
配列変数名は、式中で配列の先頭へのポイン
タとなる
配列変数を a とすると a そのものがアドレス
&a[0] (0 番要素のアドレス) と同じ
&a と書いてもよい
それぞれ型が異なる場合があるが詳
細は省略する。ポインタに関する専
門書を参照
int a[4]
365
a[0] == *a == p[0] == *p == 365
p
p = &a という代入をした場合
2015-06-29
C プログラミング入門 総機1 (月1)
6
復習:文字列
文字列は、メモリ上の文字 (char 型の値) が
並んでいる領域の先頭へのポインタである
なぜ char 型なのかは歴史的な事情による
日本語を含む場合でも、基本的には char でよい
文字列リテラルを書くと、その文字列がシステム
領域に用意され、その先頭ポインタを表す
文字列の終端は null 文字 ('\0') である
配列を文字列リテラルで初期化すると、自動的に
付加される
2015-06-29
C プログラミング入門 総機1 (月1)
7
コマンドライン引数
シェルでコマンド名の後ろに書く文字列
ホワイトスペースで分割される (トークン化)
コマンドは受け取った引数を処理する
[user@host]$ gcc src.c -o src -Wall -Wextra
この場合、 5 個の引数を gcc というプログラムに渡している
[user@host]$ ./src hello world
C 言語で書いたプログラムに引数を渡した場合、どのように処理
すればいいのか?
2015-06-29
C プログラミング入門 総機1 (月1)
8
コマンドライン引数の受け取り方
main 関数として以下のプロトタイプを使う
int main(int argc, char** argv);
cf. 今までのは int main(void);
引数名は何でもよいが慣用的に argc, argv
または ac, av が使われる
それぞれ、 argument count と argument
vector (引数の列) という意味
第2引数の書き方として、
• char **av
• char *av[]
のどちらでも、文法上は同じである。後者
の書き方をする人もいるので覚えておく
2015-06-29
C プログラミング入門 総機1 (月1)
9
argv の内容
文字列へのポインタの配列
最後に null ポインタで終わる
./prog hello world 100 と実行した場合
規格では、 argv[0] に実
行したコマンドが必ず入る
とは定められていないが、
多くの処理系でこうなる
char **argv
main 関数の自動変数の領域
argv[4] == NULL
システムのメモリ領域
argv[0][0]
argv[0]
argv[1]
たとえば argv[1] が
"hello" という文字列
だと思えばよい
2015-06-29
'.' '/' 'p' 'r' 'o' 'g' '\0'
'h' 'e' 'l' 'l' 'o' '\0'
'w' 'o' 'r' 'l' 'd' '\0'
'1' '0' '0' '\0'
C プログラミング入門 総機1 (月1)
10
argc の意味
null ポインタの入っている要素の番号を表す
n 個の引数を指定すると argc == n+1
この例の場合 argc == 4
つまり、指定した引数の個数 + 1
./prog hello world 100 と実行した場合
char **argv
argv[argc] == NULL
main 関数の自動変数の領域
システムのメモリ領域
argv[0][0]
argv[0]
argv[1]
'.' '/' 'p' 'r' 'o' 'g' '\0'
'h' 'e' 'l' 'l' 'o' '\0'
'w' 'o' 'r' 'l' 'd' '\0'
'1' '0' '0' '\0'
2015-06-29
C プログラミング入門 総機1 (月1)
11
例題:引数をすべて表示する
argv[i] を i = 1, ..., argc-1 まで表示
プログラム名が arg の場合
int main(int argc, char **argv)
{
int i;
[user@host]$ ./arg hello 123
2 arguments
[0] == "./arg"
[1] == "hello"
[2] == "123"
printf("%d arguments:\n", argc-1);
for(i = 0; i < argc; ++i)
{
printf("[%d] == \"%s\"\n", i, argv[i]);
}
return 0;
}
2015-06-29
argc までループさせない
なぜなら、 argv[argc]
== NULL なので表示でき
ない
C プログラミング入門 総機1 (月1)
12
例題:引数をすべて表示する (別の書き方)
argv はポインタ変数であり、直接移動させる
こともできる
プログラム名が arg の場合
int main(int argc, char **argv)
{
printf("%d arguments:\n", argc-1);
[user@host]$ ./arg hello 123
2 arguments
"./arg"
"hello"
"123"
for( ; *argv != NULL; ++argv)
{
printf("\"%s\"\n", *argv);
*argv は argv[0] と同じで
}
あり、 argv 自体を動かし
return 0;
ていくと、 *argv が表す文
NULL が現れるまで
}
字列が変わっていく
動かすので、 argc
は必要ない
初期化条件は空
ポインタを動かすだけでは何番目かがわからな
い。必要なら変数を用意してカウントする
2015-06-29
C プログラミング入門 総機1 (月1)
13
難しいと思う人は…
とりあえず、 argv[i] が、 i 番目の引数、
と考えるだけで OK
ただし、 i は 1 からカウント
最低限 p. 12 のプログラムが使えればよい
2015-06-29
C プログラミング入門 総機1 (月1)
14
コマンドライン引数の注意
引数はあくまでも文字列である
たとえば、 100 と書いても、 "100" という文字列
でしかない
数値として扱うには標準ライブラリ関数で変換す
る (次のスライドで説明)
引数が空文字列になる場合もある
たとえば、 ./prog "" abc と書いて実行すると、
argv[1] は空文字列、 argv[2] は "abc"
引数はシステム領域に作られるので、 (const
はついていないが) 書き換えてはいけない
2015-06-29
C プログラミング入門 総機1 (月1)
15
文字列を数値に変換する (1) sscanf
sscanf() は、文字列を解析して変数に値を
書きこむ
プログラム名が arg の場合
[user@host]$ ./arg hello 123
abc 3.14
4 arguments
[1] == "hello" (-1)
整数として
printf("%d arguments:\n", argc-1); [2] == "123" (123)
変換できな
[3] == "abc" (-1)
for(i = 1; i < argc; ++i)
い文字列
[4]
==
"3.14"
(3)
{
だった場合
int v = -1;
は何もしな
い
sscanf(argv[i], "%d", &v);
printf("[%d] == \"%s\" (%d)\n", i, argv[i], v);
}
整数として変換できる
return 0;
ところまで使われる
変換対象の文字列
int main(int argc, char **argv)
{
int i;
整数として変換する
}
2015-06-29
C プログラミング入門 総機1 (月1)
16
文字列を数値に変換する (1) sscanf
sscanf() は、文字列を解析して変数に値を
書きこむ
プログラム名が arg の場合
int main(int argc, char **argv)
{
int i;
double として変換する
[user@host]$ ./arg hello 123
abc 3.14
4 arguments
[1] == "hello" (-1.000000)
[2] == "123" (123.000000)
[3] == "abc" (-1.000000)
[4] == "3.14" (3.140000)
printf("%d arguments:\n", argc-1);
for(i = 1; i < argc; ++i)
{
double v = -1;
sscanf(argv[i], "%lf", &v);
printf("[%d] == \"%s\" (%f)\n", i, argv[i], v);
}
小数の値として解釈さ
l
(エル)
は不要
return 0;
れている
}
2015-06-29
C プログラミング入門 総機1 (月1)
17
文字列を数値に変換する (2) 変換関数
文字列を数値に変換する関数として
<stdlib.h> に以下の 2 種類がある
atox() は簡単に使えるが、変換に失敗したか
float ではない
どうかを判断できない
関数名
変換する型
備考
atoi
int
atol
long int
範囲外の値だった場合の戻り値は未定義。
変換に失敗した場合は 0 を返す。
atof
double
strtol
long int
strtoul
strtod
2015-06-29
(関数名は ASCII to x という意味
ASCII は文字コードのこと)
変換に変換に失敗した場合は 0 を返す。
失敗した位置をポインタとして得られる。
unsigned long int
strtol, strtoul は基数 (何進法表記か) を
指定する。
double
(関数名は string to x という意味)
C プログラミング入門 総機1 (月1)
18
文字列を数値に変換する (2) 変換関数
なるべく右の strtox() を使うべき
{
{
int x;
char *s = "2014.2";
x = atoi(s);
printf("\"%s\" == %d\n", s, x);
p は変換に失敗した最初の位置である
もし、文字列の先頭と同じなら、1文字も
解釈できなかったことになる
変換の失敗位置が必要なけれ
ば、 NULL を渡してもよい
int x;
char *s = "2014.2";
10進数を指定
char *p;
x = strtol(s, &p, 10);
printf("\"%s\" == %d", s, x);
// 変換が完全に失敗した場合
if(str == p) { ...
atoi, strtol のプロトタイプ
#include <stdlib.h>
int atoi(const char *str);
long strtol(const char *str, char **str_end, int base );
2015-06-29
C プログラミング入門 総機1 (月1)
19