Subsections

11章 H25.1.23: 文字列とポインタ

11.1 本日の課題

課題1
文字列を扱うライブラリイ関数のような機能を持つ関数を, * のみおよび [] のみを用いて作成せよ. program 8, program 9 を参考とすること.

課題2
program 10 から program 15 について, それぞれの冒頭にある設問に答えよ.

全ての課題について, 解答と作成したプログラム(.cが付くファイル)を 講義終了時にメールで提出する. 本文に, 学籍番号, 氏名を明記すること.

11.2 文字列とポインタ

文字列の初期化

program 1

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	// 文字列に限った初期化
	char strA[10] = "ABC"; // 9文字 + '\0' のための領域を確保
	char strB[] = "ABC";   // ABCの3文字 + '\0' のための領域を確保

	// 配列の初期化
	char strC[10] = {'A', 'B', 'C', '\0'}; // 9文字 + '\0' のための領域を確保
	char strD[] = {'A', 'B', 'C', '\0'};   // ABCの3文字 + '\0' のための領域を確保

	printf("strA = %s\n", strA);
	printf("strB = %s\n", strB);
	printf("strC = %s\n", strC);
	printf("strD = %s\n", strD);
	
	return EXIT_SUCCESS;
}

program 2

// 文字型ポインタ変数の初期化.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char strA[10] = "ABC"; // 9文字 + '\0' のための領域を確保
	char strB[10] = "XYZ"; // 9文字 + '\0' のための領域を確保

	// ポインタの初期化
	char *ptrA = strA; // 配列名 str は char str[10] の先頭のアドレス.
	
	// 文字列に限った初期化
	char *ptrB = "ABC"; // "ABC" は "ABC"という文字列のアドレス.
	char *ptrC;
	
	printf("ptrA = %s\n", ptrA);
	printf("ptrB = %s\n", ptrB);

	// ポインタ変数への代入
	ptrC = strB;
	printf("ptrC = %s\n", ptrC);
	
	// 文字列へのポインタに限った代入
	ptrC = "abc"; // "abc" は "abc"という文字列のアドレス.
	printf("ptrC = %s\n", ptrC);
	
	return EXIT_SUCCESS;
}

文字列への代入

program 3

// 文字列は, 1文字ずつ処理する.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char str[10]; // 9文字 + '\0' のための領域を確保

	str[0] = 'A';
	str[1] = 'B';
	str[2] = 'C';
	str[3] = '\0';
	
	printf("str = %s\n", str);
	
	return EXIT_SUCCESS;
}

11.3 配列名とポインタ変数

配列名はポインタ定数とみなせばよい. 関数の引数のときは, 配列名はポインタ変数とみなされる.

program 4

// 配列名はポインタ変数のように使う.
// 配列名は定数. アドレスを変えることはできない.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char strA[10] = "ABC";
	char strB[10];

	// [] を用いた表記
	printf("str[0] = %c\n", strA[0] );
	printf("str[1] = %c\n", strA[1] );
	printf("str[2] = %c\n", strA[2] );

	// * を用いた表記
	printf("*str = %c\n", *strA );
	printf("*(str+1) = %c\n", *(strA+1) );
	printf("*(str+2) = %c\n", *(strA+2) );

	// 配列名は定数であるので値を変更できない.
	// strA = strB; // 誤り
	// strA += 1;   // 誤り
	
	return EXIT_SUCCESS;
}

program 5

// ポインタ変数を配列名のように使う.
// ポインタ変数は変数. アドレスを変えることができる.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char strA[10] = "ABC";
	char strB[10] = "abc";
	char *ptr;

	ptr = strA; // ポインタ変数は, 確保されている領域のアドレスを代入する必要がある.

	// [] を用いた表記
	printf("ptr[0] = %c\n", ptr[0] );
	printf("ptr[1] = %c\n", ptr[1] );
	printf("ptr[2] = %c\n", ptr[2] );

	// * を用いた表記
	printf("*ptr = %c\n", *ptr );
	printf("*(ptr+1) = %c\n", *(ptr+1) );
	printf("*(ptr+2) = %c\n", *(ptr+2) );

	// ポインタ変数は変数であるので値を変更できる.
	ptr = strB; // 正しい
	printf("*ptr = %c\n", *ptr );
	
	ptr += 1;   // 正しい
	printf("*ptr = %c\n", *ptr );

	return EXIT_SUCCESS;
}

program 6

// 関数の引数としての配列名はポインタ変数

#include <stdio.h>
#include <stdlib.h>


// str はポインタ変数と同じで, 配列要素記領域確保は起きない.
// char str[] は char *str と全く同じ意味.
void func(char str[]) // <<== ここに注目.
{
	char strB[10] = "abc";
	
	// [] を用いた表記
	printf("str[0] = %c\n", str[0] );
	printf("str[1] = %c\n", str[1] );
	printf("str[2] = %c\n", str[2] );

	// * を用いた表記
	printf("*str = %c\n", *str );
	printf("*(str+1) = %c\n", *(str+1) );
	printf("*(str+2) = %c\n", *(str+2) );

	// 関数の引数としての配列名は変数であるので値を変更できる.
	str = strB; // 正しい
	printf("str[0] = %c\n", str[0] );
	str += 1;   // 正しい
	printf("str[0] = %c\n", str[0] );
}

int main(void)
{
	char strA[10] = "ABC";

	func(strA);
	return EXIT_SUCCESS;
}

program 7

// 関数の引数としての配列名はポインタ変数

#include <stdio.h>
#include <stdlib.h>


// str はポインタ変数と同じで, 配列要素記領域確保は起きない.
// char *str は char str[] と全く同じ意味.
void func(char *str) // <<== ここに注目.
{
	char strB[10] = "abc";
	
	// [] を用いた表記
	printf("str[0] = %c\n", str[0] );
	printf("str[1] = %c\n", str[1] );
	printf("str[2] = %c\n", str[2] );

	// * を用いた表記
	printf("*str = %c\n", *str );
	printf("*(str+1) = %c\n", *(str+1) );
	printf("*(str+2) = %c\n", *(str+2) );

	// 関数の引数としての配列名は変数であるので値を変更できる.
	str = strB; // 正しい
	printf("str[0] = %c\n", str[0] );
	str += 1;   // 正しい
	printf("str[0] = %c\n", str[0] );
}

int main(void)
{
	char strA[10] = "ABC";

	func(strA);
	return EXIT_SUCCESS;
}

11.4 文字列の操作

program 8

#include <stdio.h>
#include <stdlib.h>

// * を用いた表記
int string_length_A(char *str)
{
	int length;

	length = 0;

	while(*str != '\0'){
		length++;
		str++;
	}
	
	return length;
}

// []を用いた表記
int string_length_B(char str[])
{
	int length, i;

	i = 0;
	length = 0;

	while(str[i] != '\0'){
		length++;
		i++;
	}
	
	return length;
}

int main(void)
{
	char *string = "ABC";
	int len;

	len = string_length_A(string);
	printf("A: length = %d\n", len);

	len = string_length_B(string);
	printf("B: length = %d\n", len);

	return EXIT_SUCCESS;
}

program 9

// C言語らしい表現

#include <stdio.h>
#include <stdlib.h>

// * を用いた表記
int string_length_A(char *str)
{
	int length;

	length = 0;

	while(*str++)
		length++;
	
	return length;
}

// []を用いた表記
int string_length_B(char str[])
{
	int length, i;

	length = 0;

	for(i=0; str[i]; i++)
		length++;
	
	return length;
}

int string_length_C(char str[])
{
	int length, i;

	i = 0;
	length = 0;

	while(str[i++])
		length++;
	
	return length;
}

int main(void)
{
	char *string = "ABC";
	int len;

	len = string_length_A(string);
	printf("A: length = %d\n", len);

	len = string_length_B(string);
	printf("B: length = %d\n", len);

	len = string_length_C(string);
	printf("C: length = %d\n", len);

	return EXIT_SUCCESS;
}

文字列を扱うライブラリ関数の例
機能 ヘッダファイル 間数名
文字列の長さ string.h strlen
文字列の中から文字を探す string.h strchr
文字列のコピー string.h strcpy
文字列の結合 string.h strcat
文字列の比較 string.h strcmp
大文字にする ctype.h toupper
小文字にする ctype.h tolower

文字列の表現方法を変えることで, 配列, ポインタ変数, 文字列の使い方を覚えましょう.

#include <stdio.h>

char *strchr_A(char str[], int c)
{
  int pos = 0;

  while(str[pos]){
    if(str[pos] == c)
      return str+pos;
    pos++;
  }
  return NULL;
}

char *strchr_B(char *str, int c)
{
  while(*str){
    if(*str == c)
      return str;
    str++;
  }
  return NULL;
}

int main(void)
{
  char *string = "ABCDEFG";
  char *pos;

  pos = strchr_A(string, 'E');

  printf("*pos = %c\n", *pos);
  printf("pos = %s\n", pos);

  return 0;
}

#include <stdio.h>

char *strcpy_A(char destination[], char source[])
{
  int pos = 0;

  while(source[pos]){
    destination[pos] = source[pos];
    pos++;
  }
  destination[pos] = '\0';

  return destination;
}

char *strcpy_B(char *destination, char *source)
{
  int pos = 0;

  while( *(source+pos) ){
    *( destination + pos ) = *( source + pos );
    pos++;
  }
  *( destination + pos ) = '\0';

  return destination;
}

char *strcpy_C(char *destination, char *source)
{
  while(*source != '\0'){
    *destination = *source;
    destination++;
    source++;
  }
  *destination = '\0';

  return destination;
}

char *strcpy_D(char *destination, char *source)
{
  while(  ( *destination++ = *source++ )  != '\0')
    ;
  return destination;
}

char *strcpy_E(char *destination, char *source)
{
  while( *destination++ = *source++ )
    ;
  return destination;
}

int main(void)
{
  char strA[50] = "xyz";
  char *strB = "ABCDEFG"; // こちらはポインタのみでも構わない.
  char des[50]; // 領域の確保が必要 !!!

  strcpy_A(des, strA);
  printf("des = %s\n", des);

  strcpy_A(des, strB);
  printf("des = %s\n", des);

  strcpy_A(des, "abcdefg");
  printf("des = %s\n", des);

  return 0;
}

#include <stdio.h>
#include <string.h>

int strcmp_A(char a[], char b[])
{
  int pos;
  int a_len = strlen(a);
  int b_len = strlen(b);

  if(a_len != b_len)
    return a_len - b_len; // length mismatched.

  for(pos=0; pos<a_len; pos++){
    if(a[pos] != b[pos])
      return a_len - pos; // found a mismatched character.
  }

  return 0; // matched.
}

int strcmp_B(char *a, char *b)
{
  int pos;
  int a_len = strlen(a);
  int b_len = strlen(b);

  if(a_len != b_len)
    return a_len - b_len; // length mismatched.

  for(pos=0; pos<a_len; pos++){
    if( *(a+pos) != *(b+pos) )
      return a_len - pos; // found a mismatched character.
  }

  return 0; // matched.
}

int strcmp_C(char *a, char *b)
{
  int a_len = strlen(a);
  int b_len = strlen(b);

  if(a_len != b_len)
    return a_len - b_len;

  while(*a++ == *b++){
    if(*a == '\0')
      return 0;
  }

  return -1;
}

int main(void)
{
  char *strA = "ABCdE";
  char *strB = "ABCDE";

  printf("%d\n", strcmp_A(strA, strB));

  return 0;
}

11.5 プログラム

program 10

// (a) 誤りを指摘せよ.
// (b) 正しく動作するように修正せよ.

#include <stdio.h>

int main(void)
{
	char *ptr;

	ptr[3] = 'a';
	printf("ptr[3]=%c\n", ptr[3]);
	
	return 0;
}
#include <stdio.h>

int main(void)
{
  char *ptr; // ポインタ変数を宣言しただけではどこを指しているか
  // (何番目のアドレスが代入されているか)は不定.

  char str[100];

  ptr = str; // アドレスを代入してから参照すること.

  ptr[3] = 'a';
  printf("ptr[3]=%c\n", ptr[3]);
	
  return 0;
}

program 11

// (a) 誤りを指摘せよ.
// (b) 正しく動作するように修正せよ.

#include <stdio.h>

void func(char *ptr)
{
	ptr[3] = 'a';
}

int main(void)
{
	char *ptr;

	func(ptr);
	printf("ptr[3]=%c\n", ptr[3]);
	
	return 0;
}
#include <stdio.h>

void func(char *ptr)
{
	ptr[3] = 'a';
}

int main(void)
{
	char *ptr; // ポインタ変数を宣言しただけではどこを指しているか
	// (何番目のアドレスが代入されているか)は不定.
	char str[100];
	ptr = str;// アドレスを代入してから参照すること.
	
	func(ptr);
	printf("ptr[3]=%c\n", ptr[3]);
	
	return 0;
}

program 12

// (a) 誤りを指摘せよ.
// (b) 正しく動作するように修正せよ.

#include <stdio.h>

int main(void)
{
	char *ptr = "ABC";

	ptr[1] = 'a';
	printf("ptr[1]=%c\n", ptr[1]);
	
	return 0;
}
#include <stdio.h>

int main(void)
{
  char *ptr = "ABC"; // この文字列リテラル "ABC" は定数.

  char str[100]; // 文字列(配列)の要素ための領域を確保する必要がある.
  // str の要素はは変数として用いることができる.
  
  //  ptr[1] = 'a'; // "ABC" が記憶されている領域に代入することはできない.

  ptr = str;
  ptr[1] = 'a'; // str の要素の領域に代入することはできる.

  printf("ptr[1]=%c\n", ptr[1]);
  
  return 0;
}

program 13

// (a) 誤りを指摘せよ.
// (b) 正しく動作するように修正せよ.

#include <stdio.h>

void func(char *ptr)
{
	ptr[1] = 'a';
}

int main(void)
{
	char *ptr = "ABC";

	printf("ptr[1]=%c\n", ptr[1]);
	func(ptr);
	printf("ptr[1]=%c\n", ptr[1]);
	
	return 0;
}
// (a) 誤りを指摘せよ.
// (b) 正しく動作するように修正せよ.

#include <stdio.h>

void func(char *ptr)
{
	ptr[1] = 'a';
}

int main(void)
{
	char *ptr = "ABC";

	printf("ptr[1]=%c\n", ptr[1]);
	func(ptr);
	printf("ptr[1]=%c\n", ptr[1]);
	
	return 0;
}

program 14

// 誤っている箇所とその理由を述べよ.

#include <stdio.h>

void func(char *ptr)
{
	ptr[20] = 'a';
}

int main(void)
{
	char str[10] = "ABC";
	
	func(str);
	printf("str[20]=%c\n", str[20]);
	
	return 0;
}
#include <stdio.h>

void func(char *ptr)
{
  ptr[5] = 'a'; // 領域が確保されているのは ptr[0], ptr[1], ..., ptr[9]まで.
  //文字列の場合には, 文字の次に終端文字('\0') 1文字分の領域が必要.
}

int main(void)
{
  char str[10] = "ABC"; // str は最大で 9文字と'\0'を記憶できる文字列.
	
  func(str);
  printf("str[5]=%c\n", str[5]);
  
  return 0;
}

program 15

// 誤っている箇所とその理由を述べよ.

#include <stdio.h>

int main(void)
{
	char str[10] = "ABC";
	char *ptr;

	ptr = str;
	ptr[20] = 'a';
	printf("str[20]=%c\n", str[20]);
	
	return 0;
}
// 誤っている箇所とその理由を述べよ.

#include <stdio.h>

int main(void)
{
  char str[22] = "ABC"; // str[20]  に代入する必要がある場合には
  // '\0' の分も含めて, 最低でも char str[22]; と宣言する必要がある.
  char *ptr;

  ptr = str;
  ptr[20] = 'a';
  printf("str[20]=%c\n", str[20]);
	
  return 0;
}
#include <stdio.h>
#include <string.h>

int main(void)
{
  char str[22] = "ABC"; // str[20]  に代入する必要がある場合には
  // '\0' の分も含めて, 最低でも char str[22]; と宣言する必要がある.
  char *ptr;

  ptr = str;

  // strの初期化により, この時点で str[3] に '\0' が代入されている.
  // str[4] から str[21] に代入しても文字列としては変化がない.
  // 文字列として扱いたい場合で, str[20]に何かを代入したい時は,
  // その前では '\0' が含まれないようにする必要がある.

  strcpy(ptr, "ABC45657890123456789");
  ptr[20] = 'a';
  printf("str[20]=%s\n", str);

  str[21] = '\0'; // 各要素を操作する場合は,
                  // 終端文字がどこにあるかを意識する必要がある.
                  // ない場合は, 最後に入れる.
  return 0;
}

11.6 自由課題

program 16
/*
  配列名
  --------

  以下のプログラムでは, char 型のポインタ変数 ptr に char 型の配列の先頭の要素の
  アドレス &str[0] を代入している.
  このとき &str[0], str, ptr はすべて配列 str の先頭の要素 str[0] のアドレスを表している.
  このことがそれ以降の3つの  printf 文で確められる.

  自分の環境で実行したときの結果を答えよ.

  配列名 (この例では str) はその配列の先頭の要素 (この例では str[0]) のアドレスを
  表すことになっていることに注意すること.

 */

#include <stdio.h>

int main(void)
{
    char str[10] = "abcd";
    char *ptr;

    ptr = &str[0];

    printf("str = %p\n", &str[0]);
    printf("str = %p\n", str);
    printf("ptr = %p\n", ptr);

    return 0;
}

program 17

/*
  ポインタの演算  ptr++;
  ---------------

  ptr++;  ptr += 1;  ptr = ptr + 1; は, すべて ptr に代入されているアドレスの
  次の(配列)要素のアドレスを, ptr に代入する演算である.
  今 str[0] のアドレスが ptr に代入されているとすると, これらの演算を行うと,
  ptr には str[1] のアドレスが 代入されることになる.

  (a) 下のプログラムにより, 自分の環境で実行したとき, ptrに代入されている値が
  どのように変化するかを答えよ.
  
  (b) 同様に ptr++; の行(*)を ptr += 2; などに変更し, そのときに ptr に代入されている
  値がどのように変化するかを答えよ.
 */

#include <stdio.h>

int main(void)
{
    char str[10] = "abcd";
    char *ptr;

    ptr = str;
    printf("str = %p\n", &str[0]); // str[0] のアドレス
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr); // *ptr はptrに代入されたアドレスにある変数の値. ここでは str[0]

    ptr++; // (*)
    printf("str = %p\n", &str[1]); // str[1] のアドレス
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr);

    return 0;
}

program 18

/*
  ポインタの演算  ptr++;
  ---------------

  ptr++;  ptr += 1;  ptr = ptr + 1;
  何度もこれらの演算を繰り返すと, 次々と次の要素のアドレスが代入されることになる.

  (a) 下のプログラムにより, 自分の環境で実行したとき, ptrに代入されている値が
  どのように変化するかを答えよ.

  (b) これらの演算により, ptr に代入されているアドレスは何バイトづつ増加するかを
  答えよ.
 */

#include <stdio.h>

int main(void)
{
    char str[10] = "abcd";
    char *ptr;

    ptr = str;
    printf("&str[0] = %p\n", &str[0]);
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr);

    ptr++; // (*)
    printf("&str[1] = %p\n", &str[1]);
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr);

    ptr++; // (**)
    printf("&str[2] = %p\n", &str[2]);
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr);

    ptr++; // (***)
    printf("&str[3] = %p\n", &str[3]);
    printf("ptr = %p\n", ptr);
    printf("*ptr = %c\n", *ptr);

    return 0;
}

program 19

/*
  ポインタの演算  ptr++;
  ---------------

  以下は double 型のポインタ 変数 ptr の値を1つづつ増加させる演算を行ったときの
  アドレスを調べるプログラムである.

  (a) 下のプログラムにより, 自分の環境で実行したとき, ptrに代入されている値が
  どのように変化するかを答えよ.

  (b) これらの演算により, ptr に代入されているアドレスは何バイトづつ増加するかを
  答えよ.

  (c) このとき double 型の変数のサイズを調べ, (b)で得られた結果と比較せよ.

  (d) 上の(a)から(c)で調べたことを, int 型配列とint型ポインタについて調べよ.

 */

#include <stdio.h>

int main(void)
{
    double x[6] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0};
    double *ptr;

    ptr = x;
    printf("&x[0] = %p\n", &x[0]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    ptr++;
    printf("&x[1] = %p\n", &x[1]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    ptr++;
    printf("&x[2] = %p\n", &x[2]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    ptr++;
    printf("&x[3] = %p\n", &x[3]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    return 0;
}

program 20

/*
  ポインタの演算  ptr += 3;
  ---------------

  以下は double 型のポインタ 変数 ptr の値を3増加させる演算を行ったときの
  アドレスを調べるプログラムである.

  (a) 下のプログラムにより, 自分の環境で実行したとき, ptrに代入されている値が
  どのように変化するかを答えよ.

  (b) これらの演算により, ptr に代入されているアドレスは何バイト増加したかを
  答えよ.

  (c) このとき double 型の変数のサイズを調べ, (b)で得られた結果と比較せよ.

  (d) 上の(a)から(c)で調べたことを, char  型配列とchar 型ポインタについて調べよ.

  (e) 上の(a)から(c)で調べたことを, int 型配列とint型ポインタについて調べよ.

 */

#include <stdio.h>

int main(void)
{
    double x[6] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0};
    double *ptr;

    ptr = x;
    printf("&x[0] = %p\n", &x[0]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    ptr += 3;
    printf("&x[3] = %p\n", &x[3]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    return 0;
}

program 21

/*
  ポインタの演算  ptr + 2;
  ---------------

  以下は double 型のポインタ 変数 ptr の値に3を加えたときの
  アドレスを調べるプログラムである.

  (a) 下のプログラムにより, 自分の環境で実行したとき,
  ptrおよびptr+2  により得られるアドレスを答えよ.
  また, これらのアドレスにある値  *ptr および *(ptr+2) を答えよ.

  (b) 上の(a)で調べたことを, char  型配列とchar 型ポインタについて調べよ.

  (c) 上の(a)で調べたことを, int 型配列とint型ポインタについて調べよ.

 */

#include <stdio.h>

int main(void)
{
    double x[6] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0};
    double *ptr;

    ptr = x;
    printf("&x[0] = %p\n", &x[0]);
    printf("ptr   = %p\n", ptr);
    printf("*ptr = %f\n", *ptr);

    printf("&x[2] = %p\n", &x[2]);
    printf("ptr+2 = %p\n", ptr+2);
    printf("*(ptr+2) = %f\n", *(ptr+2));

    return 0;
}

program 22

/*
  ポインタ変数の引数
  ------------------

  以下のプログラムは
  double 型ポインタ変数 double *address を引数とする関数 funcB に
  配列 double x[6] を渡している.

  (a) 各自の環境で実行した時に,
  配列 x の最初の要素のアドレスと ポインタ変数 address に代入された値を答えよ.

  (b) main 関数内にスコープを持つ 配列 x の要素の値は,
  関数 funcB を呼び出す前後で, 同じか異なるか答えよ.
  また, その理由を答えよ.

  (c) main関数内で 配列 double x[6] を int a[6] とし,
  関数 funcB の引数を double *address から int *ptr に変更し,
  (a)と同様に, 配列 a のアドレスと ptr に代入されたアドレスを
  比較せよ.
  
 */

#include <stdio.h>


void funcA(double value)
{
    printf("funcA: &value      = %p\n", &value); // アドレス
    printf("funcA: value       = %f\n", value); // value に代入されている値.

	value = 200.0; // 呼出し側の main 関数の中で用いられている変数には影響しない.
}


// ポインタ変数を引数とする関数
void funcB(double *address)
{
    printf("funcB: address     = %p\n", address); // 先頭の要素のアドレス
    printf("funcB: *address    = %f\n", *address); // address に代入されているアドレスにある値.
    printf("funcB: address[3]  = %f\n", address[3]); // 
    printf("funcB: &address[3] = %p\n", &address[3]); // address[3] のアドレス

	address[3] = 100.0;
}

int main(void)
{
    double x[6] = {10.0, 20.0, 30.0, 40.0, 50.0, 60.0};

	printf("main:  x    = %p\n", x);
	printf("main: &x[3] = %p\n", &x[3]);
	printf("main:  x[3] = %f\n", x[3]);

	funcB(x); // 引数の受け渡しは address = x の代入とみなせばよい.
	
	printf("main:  x    = %p\n", x);
	printf("main: &x[3] = %p\n", &x[3]);
	printf("main:  x[3] = %f\n", x[3]);

    return 0;
}

program 23

/*
  配列名の引数
  ------------------
  関数の引数として用いたとき, ポインタ変数と配列名はともにアドレスを表す.
  void funcB(double *address) とvoid funcC(double address[]) における
  引数 address は同じように扱うことができる.
  
  以下のプログラムは
  double 型配列 double address[] を引数とする関数 funcC に
  配列 double x[6] を渡している.

  (a) 各自の環境で実行した時に,
  配列 x の最初の要素のアドレスと ポインタ変数 address に代入された値を答えよ.

  (b) main 関数内にスコープを持つ 配列 x の要素の値は,
  関数 funcB を呼び出す前後で, 同じか異なるか答えよ.
  また, その理由を答えよ.

  (c) main関数内で 配列 double x[6] を int a[6] とし,
  関数 funcB の引数を double address[] から int ptr[] に変更し,
  (a)と同様に, 配列 a のアドレスと ptr に代入されたアドレスを
  比較せよ.
  
*/


#include <stdio.h>


void funcA(double value)
{
    printf("funcA: &value      = %p\n", &value); // アドレス
    printf("funcA: value       = %f\n", value); // value に代入されている値.

	value = 200.0; // 呼出し側の main 関数の中で用いられている変数には影響しない.
}

//   ポインタ変数を引数とする関数
void funcB(double *address)
{
    printf("funcB: address     = %p\n", address); // 先頭の要素のアドレス
    printf("funcB: *address    = %f\n", *address); // address に代入されているアドレスにある値.
    printf("funcB: address[3]  = %f\n", address[3]); // 
    printf("funcB: &address[3] = %p\n", &address[3]); // address[3] のアドレス

	address[3] = 100.0;
}

//   配列を引数とする関数
void funcC(double address[])
{
    printf("funcB: address     = %p\n", address); // 先頭の要素のアドレス
    printf("funcB: *address    = %f\n", *address); // address に代入されているアドレスにある値.
    printf("funcB: address[3]  = %f\n", address[3]); // 
    printf("funcB: &address[3] = %p\n", &address[3]); // address[3] のアドレス

	address[3] = 100.0;
}

int main(void)
{
    double x[6] = {10.0, 20.0, 30.0, 40.0, 50.0, 60.0};

	printf("main:  x    = %p\n", x);
	printf("main: &x[3] = %p\n", &x[3]);
	printf("main:  x[3] = %f\n", x[3]);

	funcC(x);
	
	printf("main:  x    = %p\n", x);
	printf("main: &x[3] = %p\n", &x[3]);
	printf("main:  x[3] = %f\n", x[3]);

    return 0;
}

11.7 文字列を扱うライブラリ関数: size_t

size_t はオブジェクトのサイズを表すための型. コンパイラにより予め typedefにより定義されている.

環境によって全記憶域の大きさは異なるが, サイズを返す関数の場合, 全記憶域のサイズを返す必要がある. unsigned int型やunsigned long int型を返り値として指定すると 表現できる数の範囲が異なり, 環境ごとにプログラムを変更する必要がある. そこで, 予め十分大きなサイズを表現できる型がsize_t として定義されているので, これを用いる.

program 24

#include <stdio.h>
#include <stdlib.h>

// 長さは, size_t 型を使う.
// コンパイラが環境に応じて十分大きなサイズを表現できる変数を選択する.
// unsizend int, unsigned long int, unsigned long long int など.

size_t str_len(char str[])
{
	 size_t i;

	 for(i=0; str[i]; i++)
		  ;
	 return i;
}

int main(void)
{
	 char *strA =  "ABCDEFG";
	 char strB[] = "0123456";
	 char strC[16] = "0123456";

	 printf("str_len(strA) = %u\n", str_len(strA));
	 printf("str_len(strB) = %u\n", str_len(strB));
	 printf("str_len(strC) = %u\n", str_len(strC));
	 
	 return EXIT_SUCCESS;
}

(c)1999-2013 Tetsuya Makimura