List11-4 のプログラムをもとに,以下の変更を加えなさい。ただし,アドレス演算子 & は使用しないコードを考えてください。
- "LISP"の先頭要素のアドレス,"C"の先頭要素のアドレス,"Ada"の先頭要素のアドレスを,それぞれ,charへのポインタ型であるp[0],p[1],p[2]へ代入するコードを追加してください。コードを追加する場所は,変数の型宣言文(
char *p[3] = {"PAUL", "X", "MAC"};
)の直後にしてください。
- その後に,"LISP"の先頭要素のアドレス,"C"の先頭要素のアドレス,"Ada"の先頭要素のアドレスを,それぞれ表示するコードを追加してください。その際,変数aを使うコードと,変数pを使うコードの,二通りのコードを追加してください。
教員からの解答例
/*
文字列の配列
*/
#include <stdio.h>
int main(void)
{
int i;
char a[][5] = {"LISP", "C", "Ada"};
char *p[] = {"PAUL", "X", "MAC"};
/* "LISP", "C", "Ada"のそれぞれの先頭要素のアドレスをp[]に代入 */
for(i = 0; i < 3; i++)
p[i] = a[i];/* a[i] とすると,2次元配列a[][]の,i行目の先頭部分のアドレスを取り出せる。
それを,ポインタ型の変数p[i]に,代入すればよい。 */
/* 変換指定子を%pとすると,a[0]やp[0]などを,ポインタ型変数と見なし,
その入れ物に入っている値(すなわちアドレス値)を画面表示する */
printf("\"LISP\"の先頭要素のアドレス(a[0][0]のアドレス):%p\n", a[0]);
printf("\"LISP\"の先頭要素のアドレス(p[0]の値そのもの):%p\n", p[0]);
printf("\"C\"の先頭要素のアドレス(a[1][0]のアドレス):%p\n", a[1]);
printf("\"C\"の先頭要素のアドレス(p[1]の値そのもの):%p\n", p[1]);
printf("\"Ada\"の先頭要素のアドレス(a[2][0]のアドレス):%p\n", a[2]);
printf("\"Ada\"の先頭要素のアドレス(p[2]の値そのもの):%p\n", p[2]);
/* 追加コードここまで */
for(i = 0; i < 3; i++)
/* 変換指定子を%sとすると,a[0]やp[0]などを,char型の配列名と見なし,
その配列の各要素に入っている値(すなわち文字)を,ナル文字にぶつかるまで,画面表示しつづける */
printf("a[%d] = \"%s\"\n", i, a[i]);
for(i = 0; i < 3; i++)
printf("p[%d] = \"%s\"\n", i, p[i]);
return (0);
}
間違いの例その1
#include <stdio.h>
int main(void)
{
int i;
char a[][5]={"LISP","C","Ada"};
char *p[]={"PAUL","X","MAC"};
for (i=0;i<3;i++)
p[i]=a[i];
for(i=0;i<3;i++){
printf("&a[%d]=%p\n",i,a+i);
printf("&p[%d]=%p\n",i,p+i); // *(p+i) としなければならぬ。注意! 理由は以下の図面を参照せよ。
printf("a[%d]=\"%s\"\n",i,a[i]);
printf("p[%d]=\"%s\"\n",i,p[i]);
}
return 0;
}
間違いの例その2
アドレス演算子 & を使用している例(問題文をよく読みなさい!)
/*
文字列の配列
*/
#include <stdio.h>
int main(void)
{
int i;
char a[][6] = {"LISP", "C", "Ada"};
char *p[] = {"PAUL", "X", "MAC"};
p[0] = &a[0][0]; /* アドレス演算子 & を使用するとすれば,このようにできます。
だが,問題は, & を使用せずに同じことを実現して下さいということです。注意! */
p[1] = &a[1][0];
p[2] = &a[2][0];
printf("\"LISP\"の先頭アドレス(a):%p\n", &a[0][0]);/* ここも,アドレス演算子 & を使用するとすれば,このようにできます。
& を使用しないとすればどうなるか考えて下さい。
(&a[0][0]と同じ値が入っているのは何ですか?)
↑
このようなことを頭を使って考えることが,そのまま定期試験勉強になるんです! */
printf("\"LISP\"の先頭アドレス(p):%p\n", p[0]);
printf("\"C\"の先頭アドレス(a):%p\n", &a[1][0]);
printf("\"C\"の先頭アドレス(p):%p\n", p[1]);
printf("\"Ada\"の先頭アドレス(a):%p\n", &a[2][0]);
printf("\"Ada\"の先頭アドレス(p):%p\n", p[2]);
for (i = 0; i < 3; i++)
printf("a[%d] = \"%s\"\n", i, a[i]);
for (i = 0; i < 3; i++)
printf("p[%d] = \"%s\"\n", i, p[i]);
return(0);
}
テキスト298ページの演習11-4
教員からの解答例
/*
文字列を走査して表示する
*/
#include <stdio.h>
/*--- 文字列を表示(改行はしない) ---*/
void put_string(const char *s)
{
while(*s)
putchar(*s++); /* charへのポインタ型 s の指している値 *s (char型,すなわち1文字)を表示し,それからsの値が一つ増える (s++) 。
後置増分演算子 ++ と間接演算子 * では,後置増分演算子のほうが優先順位が高いが,
後置増分演算子 s++ は,sの値が増える前の値を返すので,結果としては上記のような順番の動作となる。
s は,charへのポインタ型であると同時に,char型を要素とする配列の配列名として扱うこともできる。
sの値を一つ増やすと,配列 s の一つ後ろの要素のアドレスを指すことになる。 */
}
int main (void)
{
char str[128];
printf("文字列を入力してください:");
scanf("%s", str);
printf("あなたは") ;
put_string(str);
printf("と入力しました。\n");
return(0);
}
テキスト303ページの演習11-7
教員からの解答例
/*
文字列内の英字を大文字/小文字に変換
*/
#include <ctype.h>
#include <stdio.h>
/*--- 文字列内の英字を大文字に変換 ---*/
void str_toupper(char *str)
{
while(*str){
*str = toupper(*str); /* charへのポインタ型 str の指している値 *str を関数toupper()で大文字にして,
その戻り値を,*str に代入している。 */
str++; /* charへのポインタ型strを一つインクリメントしている。 */
}
}
/*--- 文字列内の英字を小文字に変換 ---*/
void str_tolower(char *str)
{
while(*str){
*str = tolower(*str);
str++;
}
}
int main(void)
{
char str[128];
printf("文字列を入力してください:");
scanf("%s", str);
str_toupper(str);
printf("大文字:%s\n", str);
str_tolower(str);
printf("小文字:%s\n", str);
return(0);
}