記事一覧

第02問

次のプログラムで、文字列のコピーを表示しようとしています。でも、間違いが含まれているため正く動きません。

どこが間違っているのか分かりますか?
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  char text[] = "Hello!";

  char *pCopyOfText = malloc(sizeof(text));
  if (pCopyOfText) {
    strncpy(pCopyOfText, text, sizeof(text));
    free(pCopyOfText);
  }

  printf("%s\n", pCopyOfText);

  return EXIT_SUCCESS;
}
期待される実行結果
Hello!
ええと、まずmalloc()でコピー先のメモリーを確保して、次にstrncpy()で文字列をコピーしてるってことですね。
でも、コピーした文字列が表示されないのよ。
printf()を実行するタイミングに注目してみましょう。

第01問の答え

答え

この行が間違い!
    pi += sizeof(int);
こう書くのが正解!
    pi += 1;
または、これでもOKです!
    pi++;

解説

これは、ポインタのインクリメントに関する出題でした。インクリメントとは、値を1つ増やすことです。今回のポインタは配列を指しているので、「配列内の次の要素に進む」ことを意味します。

ポインタを次の要素に進めるには、要素1つ分のサイズだけ値を増やさなければならないと考えがちです。でも、ポインタには型が指定されているので、1を足すだけで自動的にサイズが計算されるのです。

次のプログラムで、ポインタの動作を確認してみましょう。

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

int main(void) {
  int numbers[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    -1
  };

  int *pi = numbers;

  printf("pi               -> %d\n", *pi);
  printf("pi + 1           -> %d\n", *(pi + 1));
  printf("pi + sizeof(int) -> %d\n", *(pi + sizeof(int)));

  return EXIT_SUCCESS;
}
実行結果
pi               -> 1
pi + 1           -> 2
pi + sizeof(int) -> 5
ポインタに1を足すと、配列内の次の要素に進めるのが分かるでしょ。
あ、sizeof(int)を足したほうは4つ先の要素に進んでますよ!
それはね、今使ってるパソコンではintのサイズが4バイトだってことね。
なるほど〜。
ここがポイント!
ポインタを配列内の次の要素に進めたいときは、1を足すだけでOK!

修正後のプログラム

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

int main(void) {
  int numbers[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    -1
  };

  int nSum = 0;

  int *pi = numbers;
  while (*pi != -1) {
    nSum += *pi;
    pi += 1; /* ← この行を修正 */
  }

  printf("Sum = %d\n", nSum);

  return EXIT_SUCCESS;
}
実行結果
Sum = 55

第01問

次のプログラムで、配列に格納された整数値の合計を表示しようとしています。でも、1行だけ間違っているせいで正く動きません。

どこが間違っているのか分かりますか?
main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int numbers[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    -1
  };

  int nSum = 0;

  int *pi = numbers;
  while (*pi != -1) {
    nSum += *pi;
    pi += sizeof(int);
  }

  printf("Sum = %d\n", nSum);

  return EXIT_SUCCESS;
}
期待される実行結果
Sum = 55
ええと、1〜10の合計を計算しようとしてるんですね。
でも、C言語にありがちな間違いがあるのよ。
ポインタpiを使っている行に注目してみましょう。

ソフトウェアエンジニアに必要なこと

ユキ先輩、良いソフトウェアエンジニアになるためには、どんな能力が必要なんでしょうか。やっぱり「コミュニケーション能力」ですか?
そうねぇ、「コミュニケーション能力」も大事だけど、ちょっと本質的じゃないわね。
え、そうなんですか!?

「コミュニケーション能力」が重要?

どのような人にソフトウェアエンジニアの適性があるかというのは、よく聞かれる話です。例えば、次のような人が向いているといわれます。

  • コミュニケーション能力がある人
  • 学習意欲が高い人

何も間違ってはいませんが、こういう意見はあまり本質をとらえているとはいえないでしょう。なぜなら、コミュニケーションしなくていい仕事なんて、ほとんどありません。コミュニケーションや学習は、なにも「ソフトウェアエンジニアだから必要」というわけではないからです。

そっか。コミュニケーションが重要なのは、僕たちに限った話じゃないですね。

本当に必要な能力とは

では、ソフトウェアエンジニアにとって本当に必要な能力とは何でしょう?それは、設計力実装力(コーディング技術)です。

一見当たり前のことを言っているようですが、それぞれの能力をもう少し噛み砕いて言うと、以下のようになるでしょう。ソフトウェア開発に携わる以上、これらの能力は欠かせません。

  • 設計力:全体を俯瞰して大雑把に把握する力
  • 実装力:細部までキッチリと作り上げる力

うーん。こう説明されると、なんだかこの2つは相反する能力のように見えますけど……。
そのとおりよ。大切なのは、状況に応じて2つの能力のバランスをとることなの。

大雑把にしかできない人は、たくさんのバグに悩まされることになるでしょう。細部しか見ていない人は、筋の通った作り方がなかなかできません。

そのため、2つの能力はどちらも必要で、バランスをとることが重要なのです。

とはいえ、ここでやっかいな問題が1つあります。それは、「体力を使えばプログラムは完成する」という事実です。高度な設計や明確なコーディングができなくても、実は体力だけでなんとかなってしまうことも多いんですよね。

なかには、残業代さえもらえれば構わないという人もいるかもしれません。でも、体力勝負ばかりでは、予定通りに開発を進めることは難しいでしょう。たとえ納期に間に合ったとしても、品質はグチャグチャかもしれません。

ここがポイント!
ソフトウェアエンジニアには、設計力と実装力のバランスをとることが大切です。

体力勝負だけで作り上げたプログラムは、自分でもよく理解できていないことが多いものです。あとから修正や機能追加をしなければならなくなったときに初めて「この作り方ではダメだ」と気付く人もいるでしょう。

でも、うまくバランスをとるには、どうしたらいいんでしょうか?
それには、メンテナンスを最優先に考えて設計や実装を行うといいわよ。