Cの勉強などなど

Rを読む時に必要そうなところを中心にピックアップ。C言語入門 (ASCII SOFTWARE SCIENCE Language)を参考にしつつ。

構造体と配列

なんちゃら型の配列というのができるらしい。Javaの型付きのArrayListみたいな感じか。

#include <stdio.h>

struct pencil
{
  int hardness;
  char maker;
  int number;
};

main()
{
  int i;
  struct pencil p[3];
  p[0].hardness = 2;
  p[0].maker = 'F';
  p[0].number = 1;

  p[1].hardness = 3;
  p[1].maker = 'H';
  p[1].number = 9;


  p[2].hardness = 2;
  p[2].maker = 'T';
  p[2].number = 7;

  printf("\tH\tM\tN\n");
  printf("=========================\n");  
  for(i = 0;i <= 2;++i){
    printf("\t%d\t%c\t%d\n",p[i].hardness,p[i].maker,p[i].number);
  }
  return 0;
}

結果。

/tmp% ./a.out   
	H	M	N
=========================
	2	F	1
	3	H	9
	2	T	7

構造体ポインタ

構造体変数を指すポインタのことを構造体ポインタというらしい。

#include <stdio.h>

struct pencil
{
  int hardness;
  char maker;
  int number;
};

main()
{
  struct pencil p0;
  struct pencil *p0_ptr = &p0;
  p0.hardness = 2;
  p0.maker = 'F';
  p0.number = 1;
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  return 0;
}

結果。

/tmp% ./a.out   
Hardness is 2 2.

構造体のメンバ(フィールドみたいなやつ)を得るための方法が構造体変数の時と構造体ポインタの時で違う。

  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);

構造体ポインタの時はアロー演算子で得る。Perlみたいだな、ってPerlがまねしたのか。

ポインタは何がうれしいのか

ふむ。関数の引数のところには確かにうれしいかもしれない。

但し、構造体は、たくさんのメンバーを持ち、データ量が大きくなる場合が多いので、関数のパラメータに構造体本体を指定すると、メンバーのコピーに時間がかかり、プログラムの実行速度が低下する。このため、関数間で構造体の受け渡しをする場合は、ポインタを用いる方法が一般的である。

$BBh(B8$B2s!'9=B$BN$H%]%$%s%?(B

読むほうはなんとかできそうになってきている気もするが、自分で書く時の意思決定がまだいまいちできていないので書きまくる必要がある。

Cでメンバの変更とかやってみる

というかポインタを関数の引き数に取る、というのはどういうことなのかをちょっと実験するのである。

#include <stdio.h>

struct pencil
{
  int hardness;
  char maker;
  int number;
};

void changeHardness(struct pencil *p)
{
  p->hardness = 10000;
}

main()
{
  struct pencil p0;
  struct pencil *p0_ptr = &p0;
  p0.hardness = 2;
  p0.maker = 'F';
  p0.number = 1;
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  changeHardness(&p0);
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  return 0;
}

おお、setterっぽいことができている。コピーとかせずにそのまま値を変更してるんかね。

/tmp% ./a.out   
Hardness is 2 2.
Hardness is 10000 10000.

さて、ここで

  changeHardness(&p0);

の部分がなんで&なのかがよく分かってない。この辺を調べてみる。

ポインタとアドレス

整理。&がアドレス演算子で、*が間接演算子とか呼ばれるもの。間接演算子とか意味分かんない名前だけど、ただのアドレスの内容を取り出す演算のことである。

んーっと、だから前の

  changeHardness(&p0);

ってところは関数の引数にアドレスだけ渡して(中身は渡さない)、

void changeHardness(struct pencil *p)
{
  p->hardness = 10000;
}

このところでそのアドレスをポインタとして受け取っているわけだな。ポインタなので、アロー演算子を使っていると。ふむ。アドレスを受け取って、*で中身。大事なことなので2回言いました。

んー、そうなると関数の中で構造体のメンバー変更、ではなくて、戻り値返させて代入すればよくね?と思ったので書いてみた。

#include <stdio.h>

struct pencil
{
  int hardness;
  char maker;
  int number;
};

void changeHardness(struct pencil *p)
{
  p->hardness = 10000;
}

int changeHardness2(struct pencil *p)
{
  return 20;
}

main()
{
  struct pencil p0;
  struct pencil *p0_ptr = &p0;
  p0.hardness = 2;
  p0.maker = 'F';
  p0.number = 1;
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  changeHardness(&p0);
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  p0.hardness = changeHardness2(&p0);
  printf("Hardness is %d %d.\n",p0.hardness,p0_ptr->hardness);
  return 0;
}

動いてしまったのでどっちがいいのかよく分からなくなってしまった。

/tmp% ./a.out   
Hardness is 2 2.
Hardness is 10000 10000.
Hardness is 20 20.

が、構造体の2つ以上のメンバを変更するときには戻り値返させて、というのは面倒な気もする。そういう時はポインタ使って関数の中で変更をしてもらったほうがありがたいかなーという気がした。