文字列の配列
文字列も配列化することができる。
#include<stdio.h> int main(void){ int i; char cs[][10] = {"banana","orange","pear"}; for(i=0;i<3;i++){ printf("cs[%d] =\"%s\"\n",i,cs[i]); } return 0; }
文字列配列の要素である文字列の読み込み
配列の各要素に、標準入力から文字列を読み込んで表示する。#include<stdio.h> int main(void){ int i; char cs[3][128]; for(i=0;i<3;i++){ printf("cs[%d]:",i); scanf("%s",cs[i]); } for(i=0;i<3;i++){ printf("cs[%d] = \"%s\"\n",i,cs[i]); } return 0; }
文字列
文字列リテラルは、整数での50や、浮動小数点数での3.14といった定数のようなもの。
オブジェクトに格納することによって、自由に取り扱うことができる。
文字の配列に文字列を格納・表示してみる
#include<stdio.h> int main(void){ char str[4]; str[0] = 'A'; str[1] = 'B'; str[2] = 'C'; str[3] = '\0'; printf("文字列strは%sです。\n",str); return 0; }
末尾のナル文字'\0'は、文字列の終端を示す「目印」である。
文字配列の初期化は、以下のいずれかの形式で行う。
char str1[] = {'A','B','C','\0'}; char str2[] = "ABC";
さっき書いたプログラムは以下のように簡略できる。
#include<stdio.h> int main(void){ char str[] = "ABC"; printf("文字列strは%sです。\n",str); return 0; }
文字列の読み込み
#include<stdio.h> int main(void){ char name[40]; printf("お名前は?"); scanf("%s",name); printf("こんにちは、%sさん。\n",name); return 0; }
復習 〜配列〜
そろそろ夏休みだし、もっと勉強しようと思う。
点数を読み込んで合格者の一覧を表示する
#include<stdio.h> #define NUMBER 5 int main(void){ int i; int snum = 0; int tensu[NUMBER]; int succs[NUMBER]; puts("点数を入力してください。"); for(i=0;i<NUMBER;i++){ printf("%2d番",i+1); scanf("%d",&tensu[i]); if(tensu[i] >= 60){ succs[snum++] = i; } } puts("合格者一覧"); puts("---------"); for(i=0;i<snum;i++){ printf("%2d番 (%3d点) \n",succs[i] + 1,tensu[succs[i]]); } return 0; }
多次元配列
多次元配列の要素とアドレスがどうなるのか確かめてみる
#include <stdio.h> int main() { int a[3][2] = {10,20,30,40,50,60}; int i,j; for(i=0;i<3;i++){ for(j=0;j<2;j++){ printf("a[%d][%d] = %d\n",i,j,a[i][j]); } } printf("\n"); /*各要素のアドレスを確かめる*/ for(i=0;i<3;i++){ for(j=0;j<2;j++){ printf("&a[%d][%d] = %p (a[%d]+%d) = %p\n", i,j,&a[i][j],i,j,(a[i]+j)); } } printf("\n" ); /* a[m][n] を *(*(a+m)+n)と書けるのか確かめる */ for(i=0;i<3;i++){ for(j=0;j<2;j++){ printf("*(*(a+%d)+%d) = %d\n", i,j,*(*(a+i)+j)); } } return 0; }
実行結果
a[0][0] = 10 a[0][1] = 20 a[0][2] = 30 a[1][0] = 30 a[1][1] = 40 a[1][2] = 50 a[2][0] = 50 a[2][1] = 60 &a[0][0] = 0013F8F8 (a[0]+0) = 0013F8F8 &a[0][1] = 0013F8FC (a[0]+1) = 0013F8FC &a[1][0] = 0013F900 (a[1]+0) = 0013F900 &a[1][1] = 0013F904 (a[1]+1) = 0013F904 &a[2][0] = 0013F908 (a[2]+0) = 0013F908 &a[2][1] = 0013F90C (a[2]+1) = 0013F90C *(*(a+0)+0) = 10 *(*(a+0)+1) = 20 *(*(a+1)+0) = 30 *(*(a+1)+1) = 40 *(*(a+2)+0) = 50 *(*(a+2)+1) = 60
一次元配列とさほど変わらないことが分かった。
ちょっと実用的な例
テストの平均点と、個人別総合得点を求めるプログラム。define NO 3 int main() { int point[][2] = { 80,80, 100,98, 60,80, }; int i,j,sum = 0,p_sum[NO]; double ave; for(i=0;i<NO;i++){ sum += point[i][0]; } ave = (double)sum/NO; printf("英語の平均点は%5.2fです。\n",ave); sum=0; for(i=0;i<NO;i++){ sum += point[i][1]; } ave = (double)sum/NO; printf("数学の平均点は%5.1fです。\n",ave); for(i=0;i<NO;i++){ p_sum[i] = 0; } for(i=0;i<NO;i++){ for(j=0;j<2;j++){ p_sum[i] += point[i][j]; } printf("出席番号%dの総得点%d\n",i+1,p_sum[i]); } return 0; }
配列とアドレス
配列の要素は原則として、順番にメモリ上に並んでいる。
配列の各要素のアドレスを調べてみよう
#include <stdio.h> int main() { int a[4] = {1,2,3,4},i; for(i=0;i<4;i++){ printf("&a[%d] = %p\n",i,&a[i]); } printf("\na = %p\n",a); return 0; }
実行結果
&a[0] = 0xbf8c0de0 &a[1] = 0xbf8c0de4 &a[2] = 0xbf8c0de8 &a[3] = 0xbf8c0dec a = 0xbf8c0de0
確かに4バイトずつつながっている。
&a[0] = 0xbf8c0de0 a = 0xbf8c0de0
配列の名前は、その配列の先頭要素のアドレスを表している。
もしそうなら、aというアドレスより4バイト進めたらa[1]のアドレスになるのではないか、
確かめてみる
#include <stdio.h> int main() { int a[] = {10,20,30,40,},i,*p; p = a; for(i=0;i<4;i++){ printf("&a[%d] = %p, a[%d] = %d, *(p+%d) = %d\n", i,&a[i],i,a[i],i,*(p+i)); } return 0; }
実行結果
&a[0] = 0xbfd9e2a8, a[0] = 10, *(p+0) = 10 &a[1] = 0xbfd9e2ac, a[1] = 20, *(p+1) = 20 &a[2] = 0xbfd9e2b0, a[2] = 30, *(p+2) = 30 &a[3] = 0xbfd9e2b4, a[3] = 40, *(p+3) = 40
a[1]のアドレスは(p+4)ではなくて(p+1)なのかな、実行結果を見ると1加えると4バイト進んでいる。
確かめてみる
#include <stdio.h> int main() { int *p,i,a[] = {20,40,80,10}; p = a; for(i=0;i<4;i++){ printf("&a[%d] = %p, (p+%d) = %p, (a+%d) = %p\n", i,&a[i],i,p+i,i,a+i); } return 0; }
実行結果
&a[0] = 001BFD18, (p+0) = 001BFD18, (a+0) = 001BFD18 &a[1] = 001BFD1C, (p+1) = 001BFD1C, (a+1) = 001BFD1C &a[2] = 001BFD20, (p+2) = 001BFD20, (a+2) = 001BFD20 &a[3] = 001BFD24, (p+3) = 001BFD24, (a+3) = 001BFD24
このことからa[n]は*(a+n)と同じということが分かる。
C言語ポインタ
ポインタ
ポインタをつかって変数の値を入れ替える。#include <stdio.h> void swap(int *,int *); int main(void) { int a,b; a = 10; b = 20; swap(&a,&b); printf("a = %d , b = %d\n",a,b); return 0; } void swap(int *x,int *y) { int z; z = *x; *x = *y; *y = z; return; }
ポインタは変数のアドレスを格納する。
関数において、引数に変数のアドレスを渡すことにより、参照呼び出しと同じことができる。
オセロつくる
C言語でオセロつくろう。
でも石を置くところまで書いて躓き中。
#include <stdio.h> int main(void){ int circle[8][8]; int i,j,x,y; int flag = 1; int ans; int player = 1; for(i=0;i<8;i++){ for(j=0;j<8;j++){ circle[i][j] = 0; } } for(;;){ if(flag == 1){ printf("Do you play Osero? (Yes:1,No:0) "); scanf("%d",&ans); if(ans == 1){ printf("Let's start Osero!!\n"); circle[3][3] = 1; circle[3][4] = 2; circle[4][3] = 2; circle[4][4] = 1; flag = 0; }else{ break; } } for(i=0;i<8;i++){ for(j=0;j<8;j++){ if(circle[i][j]==0){ printf("□ "); }else if(circle[i][j] == 1){ printf("● "); }else if(circle[i][j] == 2){ printf("× "); } } printf("\n"); } printf("Player%d\n",player); printf("Please input x and y.\n"); printf("x:"); scanf("%d",&x); printf("y:"); scanf("%d",&y); if(x >=0 && x < 8 && y >=0 && y < 8){ if (player == 1){ if(circle[y][x] == 0){ circle[y][x] = 1; }else{ printf("すでに置かれています。\n"); } }else if(player == 2){ if(circle[y][x] ==0){ circle[y][x] = 2; }else{ printf("すでに置かれています。\n"); } } } if(player == 1){ player = 2; }else{ player = 1; } } return 0; }
結構条件分岐つかいそうだな。