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

ポインタ変数

N/A
N/A
Protected

Academic year: 2021

シェア "ポインタ変数"

Copied!
33
0
0

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

全文

(1)

プログラミング及び実習⑧

(2)

文字列処理の標準ライブラリ関数(1/2)

これらの関数を使うためには、ヘッダファイル<string.h>

をインクルードしておく必要がある。

(変数s:char *,変数t:const char *,変数n: int

関数 char *strcpy(s, t) char *strncpy(s, t, n) char *strcat(s, t) char *strncat(s, t, n) 機能 文字列tsにコピーし、sを返す。 文字列tの最初のn文字をsにコピーし、 sを返す。tがn文字より少ないとき、 strcpy()と等価になる。 文字列tを文字列sの終わりに連結し、 sを返す。 文字列tの最初のn文字をsの終わりに 連結し、sを返す。tがn文字より少ない とき、strcat()と等価になる。

(3)

文字列処理の標準ライブラリ関数(2/2)

関数 機能 int strcmp(s, t) 文字列sとtを比較。s==tなら0を、 s<tなら負の値を、s>tなら正の値を返す。 ここでの大小は辞書順である。 int strncmp(s, t, n) 文字列sとtの最初のn文字を比較。 s==tなら0を、s<tなら負の値を、 s>tなら正の値を返す。 ここでの大小は辞書順である。 int strlen(s) sの長さ(文字数)を返す。

(4)

strncpy(s, t, n)について

• strncpy(s, t, n)の動作は少し複雑である。nの値と文字列tの 長さ(文字数)の関係により、tのn文字分をsにコピーした後 にヌル文字’¥0’が自動付与されたりされなかったりする。 • すると、sを初期化しておかないと、危険である。 • したがって、「最初のn文字分をコピーする」という機能だけ を利用したいとき、以下のようにプログラムを書くと安全。 strncpy(s, t, n); m=strlen(t); if(m<n) s[m]=‘¥0’; else s[n]=‘¥0’; //つまり、末尾に強制的にヌル文字を付与する。

(5)

strncpy(s, t, n)の詳細

• 文字列変数

s

に文字列変数

t

の値を先頭か

n

文字コピーする。ただし、

t

の長さ(文字数)が

n

以上のときには

n

文字をコピーする。しかし、

‘¥0’は付け加え

ない。

t

の長さが

n

より少ない場合には残りの

文字を

‘¥0’

で埋める。

したがって、①の場合、sの初期化なしでは

この関数は使えない。

参考

(6)

strncpy(s, t, n)の使用例

#include <stdio.h> #include <string.h> int main(void){ char s[16]="abcdefg", t[]="1234"; strncpy(s, t, 4); // ①の場合:つまり、strlen(t)>=n puts(s); strncpy(s, t, 10);//②の場合:つまり、strlen(t)<n puts(s); return 0; } 実行結果: 1234efg → s: 1234efg¥0 1234 → s: 1234¥0¥0¥0¥0¥0¥0 参考 元のsの後半 tの値がコピー される

(7)

7

1.以下の関数呼び出しの値(返値)を求めよ

1)strcmp(“ghi”, “ja”); /*

:=0, <0, >0

*/

2)strncmp(“gk”, “gkh”, 2); /*

:=0, <0, >0

*/

3)strlen(“xyz”);

2.以下の関数を呼び出した場合のsの値を求めよ

1)strcpy(s, “Ryukoku Uni.”);

2)strncpy(s, “Ryukoku Uni.”, 7);

s[7]=‘¥0’;

3)strcpy(s, “Ryukoku ”);

strcat(s, “Uni.”);

4) strcpy(s, “Ryukoku ”);

strncat(s, “University”, 3);

演習問題1

空白1つ

(8)

演習問題2

以下のプログラムを作成しなさい。

(1) 文字列変数(文字の一次元配列)

s1

を、

文字列

Ryukoku Uni.

で初期化して宣言し、

文字列変数

s2

を初期化なしで宣言する。

(2)

s1

の値を

s2

にコピーし、

s2

の値を出力

する。

(9)

関数strcpy(s, t)ーその①

void strcpy(char s[], char t[]) { int i=0; while(t[i] != '¥0'){ s[i]=t[i]; i++; } s[i]='¥0'; }

void strcpy(char s[], char t[]) { int i=0; while((s[i]=t[i]) != '¥0') i++; } = s[i]='¥0'; なぜ不要?

(10)

関数strcpy(s, t)ーその②

void strcpy(char *s, char *t) { int i=0; while(t[i] != '¥0'){ s[i]=t[i]; i++; } s[i]='¥0'; } このようにポインタ変数で宣言する場合、 文字列変数をchar *型とも呼ばれる。

(11)

関数strcpy(s, t)ーその③

void strcpy(char *s, char *t) { while(*t != '¥0'){ *s=*t; s++; t++; } *s='¥0'; }

(12)

復習:ポインタ

*t or t[0] t メモリ R t+1 y *(t+1) or t[1]

void strcpy(char *s, char *t) { while(*t != '¥0'){ *s=*t; s++; t++; } *s='¥0'; } int main(void) { char t[]=“Ryukoku”, s[64]; *s=*t s R s+1 y *(s+1)=*(t+1) s[0]=t[0] s[1]=t[1]

(13)

関数strcpy(s, t)ーその④

(標準関数に近い!)

char *strcpy(char *s, char *t) { int i=0; while((*(s+i)=*(t+i)) != '¥0') i++; return s; } =

char *strcpy(char *s, char *t) { int i=0; while((s[i]=t[i]) != '¥0') i++; return s; }

(14)

標準関数strcpy()の戻り値の利用

include <stdio.h>

include <string.h>

int main(void)

{

char s1[]="Ryukoku Uni.", s2[64],

*s3

;

s3

=strcpy(s2, s1);

puts(s2);

puts(s3);

puts(

strcpy(s2,s1)

);

return 0;

(15)

演習問題3

(16)

例1:変数名チェック

• 文字列データを入力し、それがC言語の変数名と

言えるかどうかを調べる

使用できる文字: (大小)英文字、数字、アンダースコア(_) 。 ただし、最初の文字は数字であってはならない。 (識別される文字数: 最初から8文字目まで) 出力例: 変数名=asd asd 変数名=_12 _12 変数名=a 2 a 2は変数名として適切でない 変数名=a12345678 空白

(17)

17

プログラム(1/2)

#include <stdio.h> #include <ctype.h> #include <string.h> #define N 64 int main(void) { int i, flag; char s[N]; while(1){ flag=0; printf("変数名=");

if(fgets(s, N, stdin) == NULL) break;

s[strlen(s)-1]=‘¥0’; // 改行記号を取り除く

if(strlen(s)>8) flag=1;

else if(!isalpha(s[0]) && s[0] != '_') flag=1;

改行記号があると、次のページの 出力(printfの方)が少し困る。

(18)

プログラム(2/2)

else{

for(i=1; s[i] != '¥0'; i++){

if(!isalnum(s[i]) && s[i] != '_'){ flag=1; break; } } } if(flag==0) puts(s); else printf("%sは変数名として適切でない¥n", s); } // while(1) return 0; }

(19)

補足説明

fgets()で読み込んだ

s

は以下のようなもの

s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] s[10] s[11] s[12] R y u k o k u U n i ¥n ¥0

s[strlen(s)-1]=‘¥0’;

s

は以下になる

s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] s[10] s[11] s[12] R y u k o k u U n i ¥0 ¥0

つまり、sの中から改行記号¥nを取り除いた。

なお、たとえば

strcmp(s, “abc”)

が正しく機能できるのは、

s

に改行記号がないもの。

(20)

例2:ファイルデータの整形

• 以下のipt.datファイルをopt.datファイルに

変換するプログラム。但し、ipt.datの中の

空白は空白かタブであり、opt.datの中の名

前と点数の間は1個のタブである。

(21)

データ

opt.dat

Student

Score

Tanaka 79

Nakata 73

Hashimoto 89

Nakahashi 67

Nakamoto 99

ipt.dat

Student

Score

Tanaka 79

Nakata

73

Hashimoto 89

Nakahashi

67

Nakamoto 99

(22)

プログラム

#include <stdio.h> #define N 64 int main(void) { char s1[N], s2[N], line[N];

while(fgets(line, N, stdin) != NULL){ if(line[0]==‘¥n’) printf(“¥n”); else{ sscanf(line, “%s %s”, s1, s2); printf("%s¥t%s¥n", s1, s2);} } return 0;

(23)

23

例3:データの更新

さらに特定の人名を修正する場合 #include <stdio.h> #include <string.h> #define N 64 int main(void) { char s1[N], s2[N], line[N];

while(fgets(line, N, stdin)) != NULL){ if(line[0]==‘¥n’) printf(“¥n”);

else{

sscanf(line, "%s %s", s1, s2);

if(strcmp(s1, “Nakamoto”)==0) strcpy(s1, “Naka”);

printf("%s¥t%s¥n", s1, s2);} }

return 0; }

(24)

演習問題4

以下の処理を実行すると、どのような出力

が得られるか。

(1) int a[]={1,2,3,4};

for(sum=0,i=0; i<4; i++)

sum += a[i];

(2) char a[]=“1234”;

for(sum=0,i=0; i<4; i++)

sum += a[i]-’0’;

(25)

25

演習問題5

以下のようなプログラムを作成しなさい。 (1)文字列変数a,b,cを用意する。 (2)文字列変数aとbにキーボードから任意の文字列を入力 する。 (3)文字列変数cにaの内容をコピーする。 (4)cの中身を出力する (5)cの内容にbの内容を連結する。 (6)cの中身を出力する。 (7)上記(2)~(6)の処理を文字列QUITが入力されるまで 繰り返す。 ヒント:「任意の文字列(本問題ではQUIT)でループを抜け 出す」ような機能を実現するために、いつもの while(fgets...)をやめてwhile(1)とかにしてループの中で 文字列比較の関数を使うとよいでしょう。

(26)

演習問題5の実行例

./a.out

Input a:

This is a test 入力

Input b:

:OK!

入力

c: This is a test

c: This is a test:OK!

Input a:

Ryukoku 入力

Input b:

University

入力

c:

Ryukoku

c:

Ryukoku University

Input a:

QUIT

入力

終了

(27)

第8回実習課題(1/5)

1.標準関数strncpy()と類似した機能を持つ関数

void StrnCpy(char *s, char *t, int n)

を作成し、

main()からそれを呼び出すようなプログラムを作

成してその動作を確認しなさい(

ex08-strncpy.c

)。

ただし、

*t

に格納されている文字列の長さを

m

すれば、

m<n

の場合に

*s

の(0から数えての)

m

字目に、

m>=n

の場合に

*s

n

文字目にヌル文字

(¥0)を与える。なお、

m

を求めるのに関数

strlen()

を使わないこと。

(28)

28

第8回実習課題(2/5)

2.任意の2文字以上の文字列を読み込み、その文字数nをも とめ、文字列を中央[n/2]で分割するプログラム( ex08-strpart.c)を作成しなさい。なお、[n/2]はn/2の整数部分(たと えば、[5/2]=2, [4/2]=2)である。 ただし、文字数を求めるのにstrlen()を使うこと。また、fgets で文字列を読み込むとき、改行記号¥nがヌル記号¥0の前 に入っていることに注意すること。 実行例 $./a.out This is a pen. 入力 [This is][ a pen.] 出力 [email protected] 入力 [taro@12][3.ac.jp] 出力 Ctrl-D 入力

(29)

第8回実習課題(3/5)

3.回文チェックプログラム(

ex08-kaibun.c

)を作成しな

さい。

例えば、

taaa aaat

が入力されたら

taaa aaat

は回文である

と出力し、

taaaaaaaa

が入力されたら

taaaaaaaa

は回文ではない

と出力する。また、

Ctrl-D

でプログラムを終了させる。

(30)

第8回実習課題(4/5)

4.文字列と文字をキーボードから読み込み、その文字の 出現回数を求めるプログラム(ex08-csearch.c)を作成しな さい。ただし、上記処理は文字列として99を入力するま で繰り返す。次のスライドの「注意」を見ること。 実行例: $./a.out this is a pen. 入力 i 入力 2 出力 pattern matching 入力 x 入力 0 出力 99 入力

(31)

課題4についての注意

• fgets()とgetchar()を繰り返しの中で

併用

すると、

getchar()で読み残した

¥n

がfgets()で読んでしまい、

想定通りの処理ができない

• 解決方法として以下のようにするとよい

– 方法1 fgets(line, …); ch=line[0]; – 方法2 ch=getchar(); getchar(); – つまり、ダミーリードして¥nを取り除く

(32)

第8回実習課題(5/5)

5.

This is a pen

のような入力文を

[This][is][a][pen]

のように単語切り出し・整形(出力)するプロ

グラム(

ex08-words.c

)を作成しなさい。

発展課題

(33)

課題5のヒント

文字を1つずつ読んでいき、 • 前が空白、現在が空白でないなら[記号と現文字を 出力する • 前が空白でない、現在が空白であるなら]記号を出 力する のように出力していく。 前の状態と現在の状態は例えば fgm(=1:空白、=0:空白でない)、fgのような変数を設け て使えばよい (6回目の講義プリントの例との考え方が似ている)

参照

関連したドキュメント

Maurer )は,ゴルダンと私が以前 に証明した不変式論の有限性定理を,普通の不変式論

Maurer )は,ゴルダンと私が以前 に証明した不変式論の有限性定理を,普通の不変式論

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

ドリル教材 教材数:6 問題数:90 ひきざんのけいさん・けいさんれんしゅう ひきざんをつかうもんだいなどの問題を収録..

 □ 同意する       □ 同意しない (該当箇所に☑ をしてください).  □ 同意する       □ 同意しない

パスワード 設定変更時にパスワードを要求するよう設定する 設定なし 電波時計 電波受信ユニットを取り外したときの動作を設定する 通常

次亜塩素酸ナトリウムは蓋を しないと揮発されて濃度が変 化することや、周囲への曝露 問題が生じます。作成濃度も

“〇~□までの数字を表示する”というプログラムを組み、micro:bit