• 検索結果がありません。

02: 変数と標準入出力

N/A
N/A
Protected

Academic year: 2021

シェア "02: 変数と標準入出力"

Copied!
24
0
0

読み込み中.... (全文を見る)

全文

(1)

12: コマンドライン引数

Linux にログインし、以下の講義ページを開いておく

こと

http://www-it.sci.waseda.ac.jp/teachers/w48369

2/CPR1/

C プログラミング入門

基幹

7 (水5)

1

2017-07-05

(2)

まとめ:ポインタを使った処理

内容

説明

呼び出し元の変数を書き換える 第 9 回

文字列を渡す・配列を渡す

第 10 回

ファイルポインタ

第 10 回

複数の値を返す

第 11 回

大きな領域を確保する

第 11 回

2

(3)

今回の内容

コマンドライン引数の取り扱い

シェルから引数 (オプション) を受け取る

技術的には二重ポインタ (double pointer) である

• ポインタへのポインタ

• 秋期の「Cプログラミング」で使うが、この講義ではあ

まり踏み込まない

文字列から数値への変換

コマンドライン引数は単なる文字列なので、数値として扱

うには変換が必要

3

(4)

ポインタと文字列と配列の復習

(5)

復習:アドレスとポインタ

メモリ上の位置を表す値

型を持つ

変数 a のアドレスは

&a

ポインタ変数に格納できる

アドレス値のことをポインタとも呼ぶことがある

5

int *p 100 int a 矢印が指す位置のア ドレスを持っている { int a = 100; int *p = &a; printf("%d", *p);

(6)

復習:アドレス演算と読み書き

デリファレンス演算子

*

で、アドレスが指す位置の内容を読

み書きできる

添え字演算子

[]

で、ポインタの指す位置をずらして読み書

きできる

ポインタ p にはアドレスが入っている

ポインタ p に対して p+1 は型の大きさ1つ分だけ動かした

アドレス

ポインタ p に対して *(p+n) と p[n] は同じ

6

p -23 85 メモリ上のデータをどんな値とみなすか は、ポインタの型で決まる p[0] == *(p+0) == *p == -23 p[1] == *(p+1) == 85 矢印が指す位置のア ドレスを持っている

(7)

復習:配列とポインタ

配列変数名は、式中で配列の先頭へのポインタとなる

配列変数を a とすると

a

そのものがアドレス

&a[0] (0 番要素のアドレス) と同じ

&a と書いてもよい

7

p 365 a[0] == *a == p[0] == *p == 365 int a[4] それぞれ型が異なる場合があるが詳 細は省略する。ポインタに関する専 門書を参照 p = &a という代入をした場合

(8)

復習:文字列(ポインタ)

文字列=「メモリ上の文字 (char 型の値) が並んでいる領

域」の先頭へのポインタ

なぜ char 型なのかは歴史的な事情による

日本語を含む場合でも、基本的には char でよい

文字列リテラルを書くと、その文字列がシステム領域に用意

され、その先頭ポインタを表す

文字列の終端は null 文字 (

'\0'

) である

なので、 null-terminated string と呼ばれる

8

'H' 'e' 'l' 'd' '!' '\n' '\0'

s

{ const char *s = "Hello world!\n"; 文字列データはシステム領域にある ローカル変数 ほかに、 ASCIIZ とか C string とも呼ばれる

(9)

復習:文字列(配列)

配列を文字列リテラルで初期化すると、その文字数 + 1 の大

きさの配列が生成される

末尾に null 文字 (

'\0'

) が自動的に付加される

9

'H' 'e' 'l' 'd' '!' '\n' '\0' { char s[] = "Hello world!\n"; ローカル変数 char s[]

(10)

コマンドライン引数処理

(11)

コマンドライン引数

シェルでコマンド名の後ろに書く文字列

ホワイトスペースで分割される (トークン化)

コマンドは受け取った引数を処理する

11

[user@host]$ gcc src.c -o src -Wall -Wextra

[user@host]$ ./src hello world

この場合、 5 個の引数を gcc というプログラムに渡している

C 言語で書いた自作のプログラムに引数を渡した

場合、どのように処理すればいいのか?

(12)

コマンドライン引数の受け取り方

main 関数として以下のプロトタイプを使う

int main(

int

argc,

char**

argv);

cf. 今までのは int main(void

);

引数名は何でもよいが慣用的に argc, argv または ac, av が

使われる

それぞれ、 argument count と argument vector (引数

の列) という意味

12

第2引数の書き方として、 • char **argv • char *argv[] のどちらでも、文法上は同じである。後者 の書き方をする人もいるので覚えておく

(13)

システムのメモリ領域

argv の内容

文字列へのポインタの配列

最後に null ポインタで終わる

13

./prog hello world 100 と実行した場合

'h' 'e' 'l' 'l' 'o' '\0' 'w' 'o' 'r' 'l' 'd' '\0' '1' '0' '0' '\0' '.' '/' 'p' 'r' 'o' 'g' '\0' argv[0] char **argv main 関数の自動変数の領域 argv[0][0] 規格では、 argv[0] に実 行したコマンドが必ず入る とは定められていないが、 多くの処理系でこうなる たとえば argv[1] が "hello" という文字列 だと思えばよい argv[1] argv[4] == NULL

(14)

システムのメモリ領域

argc の意味

null ポインタの入っている要素の番号を表す

n 個の引数を指定すると argc == n+1

14

./prog hello world 100 と実行した場合

'h' 'e' 'l' 'l' 'o' '\0' 'w' 'o' 'r' 'l' 'd' '\0' '1' '0' '0' '\0' '.' '/' 'p' 'r' 'o' 'g' '\0' argv[0] char **argv main 関数の自動変数の領域 argv[0][0] この例の場合 argc == 4 つまり、指定した引数の個数 + 1 argv[1]

(15)

例題:引数をすべて表示する

argv[i] を i = 1, ..., argc-1 まで表示

15

int main(int argc, char **argv)

{

int i;

printf("%d arguments:\n", argc-1); for(i = 0; i < argc; ++i)

{

printf("[%d] == \"%s\"\n", i, argv[i]); }

return 0; }

[user@host]$ ./arg hello 123

2 arguments [0] == "./arg" [1] == "hello" [2] == "123" argc までループさせない なぜなら、 argv[argc] == NULL なので表示でき ない プログラム名が arg の場合

(16)

例題:引数をすべて表示する (別の書き方)

argv はポインタ変数であり、直接移動させることもできる

16

int main(int argc, char **argv)

{

printf("%d arguments:\n", argc-1); for( ; *argv != NULL; ++argv)

{

printf("\"%s\"\n", *argv);

}

return 0; }

[user@host]$ ./arg hello 123

2 arguments "./arg" "hello" "123" *argv は argv[0] と同じで あり、 argv 自体を動かし ていくと、 *argv が表す文 字列が変わっていく プログラム名が arg の場合 NULL が現れるまで 動かすので、 argc は必要ない 初期化条件は空 ポインタを動かすだけでは何番目かがわからな い。必要なら変数を用意してカウントする

(17)

難しいと思う人は…

とりあえず、 argv[i] が、 i 番目の引数、と考えるだけで

OK

ただし、 i は 1 からカウント

最低限 p. 12 のプログラムが使えればよい

17

(18)

コマンドライン引数の注意

引数はあくまでも文字列である

たとえば、 100 と書いても、 "100" という文字列でしかな

数値として扱うには標準ライブラリ関数で変換する (次のス

ライドで説明)

引数が空文字列になる場合もある

たとえば、

./prog "" abc

と書いて実行すると、

argv[1] は空文字列、 argv[2] は "abc"

引数はシステム領域に作られるので、 (const はついていな

いが) 書き換えてはいけない

(19)

文字列から数値へ変換する

(20)

文字列→数値の変換

"100" という3文字 (+終端null) の文字列を int 型の 100

に変換したい

そうしないと、ループとか計算で使えない

代表的な2つの方法を紹介

1.

sscanf

2.

ato*, strto* 一族

20

// 例えば、 argv[1] == "3" だとして、 int i; int n = argv[1]; // 当然できない for(i = 0; i < n; ++i) { ...

(21)

文字列を数値に変換する (1) sscanf

sscanf() は、文字列を解析して変数に値を書きこむ

21

int main(int argc, char **argv)

{

int i;

printf("%d arguments:\n", argc-1); for(i = 1; i < argc; ++i)

{ int v = -1; sscanf(argv[i], "%d", &v); printf("[%d] == \"%s\" (%d)\n", i, argv[i], v); } return 0; }

[user@host]$ ./arg hello 123 abc 3.14 4 arguments [1] == "hello" (-1) [2] == "123" (123) [3] == "abc" (-1) [4] == "3.14" (3) プログラム名が arg の場合 整数として 変換できな い文字列 だった場合 は何もしな 整数として変換する 整数として変換できる ところまで使われる 変換対象の文字列

(22)

文字列を数値に変換する (1) sscanf

sscanf() は、文字列を解析して変数に値を書きこむ

22

int main(int argc, char **argv)

{

int i;

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); } return 0; }

[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) プログラム名が arg の場合 double として変換する 小数の値として解釈さ れている l (エル) は不要

(23)

文字列を数値に変換する (2) 変換関数

文字列を数値に変換する関数として<stdlib.h> に以下の 2

種類がある

atox() は簡単に使えるが、変換に失敗したかどうかを判断

できない

23

関数名 変換する型 備考 atoi int 範囲外の値だった場合の戻り値は未定義。 変換に失敗した場合は 0 を返す。 (関数名は ASCII to x という意味 ASCII は文字コードのこと)

atol long int atof double

strtol long int 変換に変換に失敗した場合は 0 を返す。

失敗した位置をポインタとして得られる。 strtol, strtoul は基数 (何進法表記か) を

指定する。

(関数名は string to x という意味) strtoul unsigned long int

strtod double

(24)

文字列を数値に変換する (2) 変換関数

なるべく右の strtox() を使うべき

24

{ // 整数に変換する例 int x; char *s = "2014.2"; x = atoi(s); printf("\"%s\" == %d\n", s, x); { int x; char *s = "2014.2"; char *p; x = strtol(s, &p, 10); printf("\"%s\" == %d", s, x); // 変換が完全に失敗した場合 if(str == p) { ... 変換の失敗位置が必要なければ、 NULL を渡してもよい p は変換に失敗した最初の位置である。 もし、文字列の先頭と同じなら、1文 字も解釈できなかったことになる 10進数を指定 #include <stdlib.h>

int atoi(const char *str);

long strtol(const char *str, char **str_end, int base );

参照

関連したドキュメント

が前スライドの (i)-(iii) を満たすとする.このとき,以下の3つの公理を 満たす整数を に対する degree ( 次数 ) といい, と書く..

LLVM から Haskell への変換は、各 LLVM 命令をそれと 同等な処理を行う Haskell のプログラムに変換することに より、実現される。

(注)本報告書に掲載している数値は端数を四捨五入しているため、表中の数値の合計が表に示されている合計

ダウンロードした書類は、 「MSP ゴシック、11ポイント」で記入で きるようになっています。字数制限がある書類は枠を広げず入力してく

各テーマ領域ではすべての変数につきできるだけ連続変量に表現してある。そのため

(注)本報告書に掲載している数値は端数を四捨五入しているため、表中の数値の合計が表に示されている合計

指針に定める測定下限濃度   :2×10 -2 Bq/cm 3 ,指針上、この数値を目標に検出することとしている値 測定器の検出限界濃度     :約1×10