Today アクセスカウンター Yesterday アクセスカウンター

ホワット・ア・ワンダフル・ワールド

私は知識に何ものかを付け加え,また他の人々がより多くのものを付け加える手助けをした --- G.H.ハーディ

全記事一覧 << 2009/06 12345678910111213141516171819202122232425262728293031 2009/08 >>

プロフィール

  • Author:あろは (alohakun)
  • 京都で GCC やデバッガや仮想ハードウェアを開発しているサラリーマン。

    本ブログの内容は,あくまでも個人的な感想や意見であり,会社の意見を代表するものでは一切ありません.

    連絡先 : alohakun ___at___ gmail.com
    mixi : http://mixi.jp/show_friend.pl?id=182927
    twitter : http://twitter.com/alohakun













    あわせて読みたい


    この日記のはてなブックマーク数


    スカウター : ホワット・ア・ワンダフル・ワールド


    Map

FC2カウンター

ブロとも申請フォーム

この人とブロともになる

C の構文の変態さ

2008/04/05(土) 16:28:58

そもそも C に文字列型なんてありません.文字列みたいなアレは,文字の配列型です.

ポインタと配列型は,全く違うものです.そもそも typeof (GCC 拡張) で取れる型が違う,sizeof で取れるオブジェクトサイズも違う.

まぁ,文字列リテラルが存在していて,唯一複合型なのに静的 (static) 領域に初期化して置いとくことができたりして,いろいろややこしいのですが.

あと,配列の要素アクセスは,常にポインタ演算経由で行われるのも,ややこしいところですね.配列とポインタを同一視してしまうというもっともありがちな混乱は,ここに由来するのだと思います.アセンブリ言語のレベルでは,配列なんていう高級な概念は存在しなくて,メモリと番地しかありません.ポインタは番地を入れる変数 (メモリやレジスタ)

そんなこんなで,もともとアセンブリに由来する C は同じことを表現する方法が非常にたくさんあることで有名ですが,似ているようで違うものもたくさんあります.それが諸悪の根源ですね.

C99 で複合リテラルが入ったので,さらに変態的な書き方がいろいろ可能になってます.

どれが同じ意味で,どれが違うのか,全部ちゃんとわかってますか ?


#include <stdio.h>

int main() {

  char a[] = "abcdefghi";
  puts(a);
  printf("%d\n", sizeof(a));
  putchar(a[1]);
  putchar('\n');
  putchar(*(a + 1));
  putchar('\n');
  putchar(1[a]);
  putchar('\n');
  a[1] = 'z';
  puts(a);
  // a++;         ******* error: lvalue required as increment operand ******
  // puts(a);
  
  char *p1 = (char[]){'a','b','c','d','e','f','g','h','i','\0'};
  puts(p1);
  printf("%d\n", sizeof(p1));
  p1[1] = 'z';
  puts(p1);
  p1++;
  puts(p1);
  
  char *p2 = (char[]){"abcdefghi"};
  puts(p2);
  printf("%d\n", sizeof(p2));
  p2[1] = 'z';
  puts(p2);
  p2++;
  puts(p2);

  char* const p3 = (char[]){'a','b','c','d','e','f','g','h','i','\0'};
  puts(p3);
  printf("%d\n", sizeof(p3));
  p3[1] = 'z';
  puts(p3);
  // p3++;        ****** error: increment of read-only variable 'p3' ******
  // puts(p3);
  
  const char* p4 = (char[]){'a','b','c','d','e','f','g','h','i','\0'};
  puts(p4);
  printf("%d\n", sizeof(p4));
  // p4[1] = 'z'; ****** error: assignment of read-only location ******
  // puts(p4);
  p4++;
  puts(p4);

  char *p5 = "abcdefghi";
  puts(p5);
  printf("%d\n", sizeof(p5));
  // p5[1] = 'z'; ******  セグメンテーション違反です ******
  // puts(p5);
  p5++;
  puts(p5);

  const char* const p6 = (char[]){'a','b','c','d','e','f','g','h','i','\0'};
  puts(p6);
  printf("%d\n", sizeof(p6));
  // p6[1] = 'z'; ****** error: assignment of read-only location ******
  // puts(p6);
  // p6++;        ****** error: increment of read-only variable 'p6' ******
  // puts(p6);
  
  const char p7[] = "abcdefghi";
  puts(p7);
  printf("%d\n", sizeof(p7));
  // p7[1] = 'z'; ****** error: assignment of read-only location ******
  // puts(p7);
  // p7++;        ****** error: lvalue required as increment operand ******
  // puts(p7);

  /* error: initializer element is not constant */
  // static const char* p8 = (char[]){'a','b','c','d','e','f','g','h','i','\0'};

  char (*p9)[10] = (char (*) [10])&"0123456789abcdefghi";
  puts(*p9);
  printf("%d\n", sizeof(*p9));
  p9++; // 10 バイト進む
  puts(*p9);

  return 0;
}


最後の例の意味がちゃんとわかれば,ポインタを理解できていると思って良いかもしれません.

配列の要素アクセス a[10] は *(a + 10) のシンタックスシュガーだから,*(10 + a) と同じ,すなわち 10[a] と書いても同じ意味,とかはわりと有名な話ですが.a に 10 を足したとき,アドレスがいくつ進むのか ? とかが,a の型の大きさによって決まるのです.

要するに sizeof(*a) ぶん増えます.char だったら 1 (バイト),4 バイト整数だったら 4, 文字の要素数10の配列型 (sizeof は 10) だったら,10 増えます.

追記

同じ意味でも,最適化が絡んでくるとびみょーに違ったりする場合もあります.

参考 : const char* const p = "ABC"; と const char q[] = "ABC"; はどちらがよいか、みたいな与太
C 言語/コンパイラ/BinaryHacksTB:0CM:0 このエントリーを含むはてなブックマーク | livedoorクリップ livedoorクリップ BuzzurlにブックマークBuzzurlにブックマーク newsing it!
コメント
コメントの投稿

管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://alohakun.blog7.fc2.com/tb.php/931-08131b84

最近のコメント

リンク

このブログをリンクに追加する

最近のトラックバック

人生の残り日数

日本人男性の平均寿命は 28700日.

RSSフィード

カテゴリー

Copyright(C) 2006 ホワット・ア・ワンダフル・ワールド All Rights Reserved.
Powered by FC2ブログ. FC2ブログ 一戸建て template designed by 遥かなるわらしべ長者への挑戦.