• ベストアンサー

ポインタのポインタについて

VisualC++2008 の、Windowsフォームアプリケーションを使用しています。 自作クラスの中に組み込む線形リスト的なものを、後で移動したりする場合実体のコピーをしなくて済むよう 今、「アドレス」を格納する配列を作りたいです。 しかし、ポインタのポインタに対して色々実験してみたのですが どうもあと一歩ぐらいのところで手が届きません フォームのイベントで、試しに以下のように書いてみると Text = ""; char *a[8], b =7 ,*c = new char[7], i =7; while (i) { c[i] = --i; a[i] = &c[i]; } a[7] = &b; while (i<8) Text += *(*a+i++); delete c; これを実行したとき フォームのTextが 「01234567」 となってほしいのですが 「0123456-3」 となってしまいます。 試してみたのですが その直後にbを調べると、やはりちゃんと7になっており また a[7] と &b をifおよび==を用いて比較してみても 同じになっています。 なお、この-3というのはどうも、解放処理の後でそこにアクセスした場合はこの場合 「-35」 となり、確保中の時に代入してないアドレスにアクセスすると 「-3」 となるような感じです。 このような、連続していないアドレスへの参照を配列化しておいて取り出す事は出来ないのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

えぇと, 「配列的な書き方」とか「ポインタ的な書き方」ってのは厳密に区別する必要はないと思いますよ. 今どきのコンパイラなら (それなりな最適化オプションをかければ) 同じコードにするはずですから, プログラム上で読みやすい書き方をすべきでしょう. でフォームの話はやったことないので見なかったことにして (<ぉぃ), 未定義動作関係の話だけします. c[i] = --i; という文が未定義動作になるのは, 直接的には「オブジェクトの値を変更する場合, その値は新しい値を決めるため以外に使ってはいけない」という規定によります. 今の例に即してもっとかみくだいていうと「右辺のデクリメントがどのタイミングで実行されるか確定していない」ことと「左辺と右辺のどちらを先に計算するかが定まっていない」ことの 2点が問題になります. つまり, コンパイラはこの文を (例えば) --i, c[i] = i; と解釈してもいいし c[i] = i-1, --i; と解釈してもかまいません (未定義動作なので「全く関係ない動作」を含む他のどのような解釈もありうるが). ... あれ? *(*a+i++) って, (*a)[i++] と等価ですよね.... *a[i++] と等価にしたいなら **(a+i++) では?

LongSecret
質問者

お礼

ありがとうございます。 えっと、そこなんですが・・・ 私は基本的に、式というのは右から左へ実行されるはずだと思っていました。 しかし インクリメント、デクリメントが含まれる場合は、つまりそういう感じに微妙になる、ということでしょうか? **(a+i++) でできました! ありがとうございます。 ちょっと私は内容の趣旨を勘違いしていたかもしれません。 下のを変形させてみると(これもできました)私がやりたかった事はこんな感じの事です。 Text =""; char **a=new char*[8], *d[8], b=7 ,c[7], i=7; char *f =c+7 ,**g =a+7, **h =d; a[7] =&b; while (i) { *--f =--i; *--g =f; } for (;i<8;i++) *h++ = *g++; delete a; while (i) Text +=*d[--i]; こういう事ならば、毎回iの値を確認しなくてすむので、そういう比較においてはこちらの方が高速ですよね?

LongSecret
質問者

補足

ちなみに同様の内容について、この書き方ならば、両辺に同じものが含まれている事はないので、問題ない、という事でしょうか?

その他の回答 (3)

  • FBL
  • ベストアンサー率0% (0/3)
回答No.4

ガイドラインを読んでみて、これだけなら問題ないはず・・・? と思ったので、連絡・状況整理用に別IDで回答欄を使わせてください。 質問者です。 「質問・問題の解決につながる投稿」ですので、種類は「回答」にさせていただきます。 ちゃんと前置・後置という言葉があるんですね。 今回の質問は大丈夫そうなので締め切りとさせていただきます。 そして、残っている2点についてかなりかみ砕いて新質問を立てました。 後で見て分かりやすいようにここにリンクをはっておきますので、よろしければよろしくお願いいたします。 一時的なポインタを作る場合との速度比較についてです http://oshiete1.goo.ne.jp/qa4645062.html インクリメント、デクリメントの順序について http://oshiete1.goo.ne.jp/qa4645073.html

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.3

>配列的な書き方しか出来ないのは最速処理は出来なく、 pをポインタ、nをスカラとするとき、 *(p+n) は p[n] と同値です。おそらく、どちらの書き方でも 処理速度は同じでありましょう。

LongSecret
質問者

お礼

ありがとうございます。 すみません。 直されるとさきほど本でチェックしました。そうですよね? ポインタのややこしさに気を取られて微妙に履き違えた感じの事を書いてしまったっぽいです。 私が言いたかったのは、下記のような内容です。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

あ, とりあえず c[i] = --i; という文は未定義動作となるので書いちゃダメです. そこが問題となっている可能性はありませんかね.

LongSecret
質問者

お礼

ありがとうございます。 なんか教えて!gooに投稿してみたら思ったより見にくくて・・・ 申し訳ありません この右側は、実際には先行デクリメントっていうのかな・・? です。 もしそれでもNGということならば 知っておきたいです。 どういう風に未定義動作扱いになるのでしょうか? こちらでやっている分には、そういう事をやる分だけには問題なく、ビルドは出来、ちゃんと先にiが-1されてからiがc[i]に格納される という風に、動作も出来るようです。 2個目のwhile文の中身の右辺の部分の括弧を外す、つまり **a+i++; てやったら出来ちゃいました。 にゃんかムズイですね。 ここも配列っぽく書くと *a[i++]; ってなりますが、こちらでもできました。 さらに 受け渡しは出来るのか確認するべく、ちょいと改造して新たな実験をしてみました。 Text =""; char **a=new char*[8], *d[8], b=7 ,c[7], i=7; while (i) { c[i] =--i; a[i] =&c[i]; } a[7] =&b; for (;i<8;i++) d[i] =a[i]; delete a; while (i) Text +=*d[--i]; 結果は、ちゃんと 「76543210」 になりました。 ただ、これで本当に合ってるのかどうか…というと、ちゃんと把握できてないのでちょっと不安です これは、ちゃんとaからdにアドレスの配列を受け渡せた、って事でOKでしょうかね…?

LongSecret
質問者

補足

・・・ってよく考えたら **a+i++ は、違いますね。 やり方の問題で一見結果としては同じに見えただけで、これでは単に*c+i++と同じ事ですよね(苦笑) c[i] = --i; を c[i] = --i+1; などとやってみたら 12345678 になってしまいました。やはり違う。 しかし *a[i++] ならば間違いないようです 12345677 と、正しい解を出せました! うーむ、しかし配列的な書き方しか出来ないのは最速処理は出来なく、悔しいので、ポインタ的書き方が2重ポインタで出来るならそっちも知りたいです。

関連するQ&A

  • ポインタ変数とポインタのポインタ

    ポインタ変数の宣言 char *a[]; をしたとき僕の中では a[0],a[1]...という、ある文字列A,B,C...の最初のアドレスを指すポインタが、配列になっているものを宣言していると理解していました。 しかしこの次に、ポインタのポインタが出てきました。僕はこれを、 ある変数を指し示すアドレスのアドレスである、と理解しました。 この2つは1つめはいくつかのアドレスを指し示すもの、2つ目は1つのアドレスを指し示すものであるとして、僕の中で異なったものであると理解していましたが、参考書「C標準コースウェア」によると プログラムにおいて、関数でポインタ配列を受け取るときchar *p[]はchar **pとしてもよい と書かれており、またその実例として、 (9-5) #include <stdio.h> void disp (char *p[],int n){ int i; for (i= 1;i<n;i++){ printf("%s\n",p[i]); } } int main(void){ char *girl[] = {"Arica","Candy","Lisa"}; disp (girl,sizeof(girl)/sizeof(girl[0])); return 0; } というプログラムが書かれていました。 ここで一気に訳が分からなくなりました。 char *girl[] = {"Arica","Candy","Lisa"}; と宣言されているため、 girl[0]はAricaという文字列の最初のアドレスを指すポインタ、 *girl[0]はAricaという文字列を直接指し示していると解釈しています。 girlは{"Arica","Candy","Lisa"}という文字列の配列の最初のアドレスを指し示していると考えました。 sizeof(girl)を使った時に不思議なのですが、 girlはどのように配列の終わりを理解しているのでしょうか? (配列の要素数を渡していない点が不思議です。) また、 disp側が受け取ったのは*girl[]であり、いくつかのポインタの配列ですが、渡したものはgirlという要素数がないポインタ1つだけです。 そして最初の疑問が出てくるわけですが、*p[]を**pと書きかえてみると、 文字列のアドレスを示すgirlという名の1つのポインタを渡すと、pという名のポインタのポインタで受け取るというのも、よくわからなくなっています。 おそらくポインタ配列に対する理解がどこかでずれているようですが、自分でどこがわからないのかわからなくなっています。 どうかご教授ください。

  • ポインタのポインタの必要性

    書物によるとポインタのポインタの使用例として「ポインタの配列」はポインタを使ってアクセスすることができます。」[*]とありますが、どうしてポインタのポインタが必要なのかがいまいちピンと来ません。 どういう場合なのかを知りたく思っています。 [*]サンプルスクリプト ===================================================== char *mnthp[3] = {/* ポインタの配列の宣言 */ "January", "February", "March" }; char **p1;/* 「ポインタのポインタ」の宣言 */ int i, j; p1=mnthp;/* 「ポインタのポインタ」にポインタの配列 */ /* の先頭番地を設定 */ /***** 例1 *****/ for (i = 0; i < 3; i++) {/* 「ポインタのポインタ」の値を変えずに */ printf("%s\n", *(p1 + i));/* 相対的に文字列を出力 */ } ==> このようなことをしなくとも printf("%s", mnthp[i]); で値を参照出来ると思われる。 ===================================================== [*] http://www9.plala.or.jp/sgwr-t/c/sec10-4.html 宜しくお願い致します。

  • ポインタ配列のアドレスについて

    #include <stdio.h> int main() { int i; char *ary[] = { "a" , "b" , "c" }; for(i=0; i<3; i++) printf("%u\n" , &ary[i]); return 0; } /*実行結果 6618616 6618620 6618624 */ 上記のプログラムを実行したときポインタ配列*aryがchar型だろうがdouble型だろうがアドレスが4つとびになるのですがなぜなんでしょう?アドレスを求めるプログラム自体が間違っているのでしょうか?

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • ポインタ

    文字列"apple", "orange", "strawberry"へのポインタをポインタ配列の各要素に代入した後,その文字列の文字を逆順に表示するようにプログラムを考えているのですが、 while文の中のjはそれぞれについて考える必要がありますか? ポインタを使って文字数を数得られそうですが出来ませんでした。 [実行例] ポインタ配列[0]の文字列の逆はelppaです. ポインタ配列[1]の文字列の逆はegnaroです. ポインタ配列[2]の文字列の逆はyrrebwartsです. #include<stdio.h> #define COUNT 3 int main(void) {   char * words[COUNT] = {"apple", "orange", "strawberry"};   int i, j;   for(i = 0; i < COUNT; i++) {    j =  ?  ;    printf("ポインタ配列[%d]の文字列の逆は", i);    while(   ?   ) {     printf("%c", *(words[i] + j));     j--;    }    printf("です.\n");   }   return 0; }

  • ポインタ配列

    "one","two","three","four","five","six","seven","eight","nine","ten" のポインタ配列の文字列を、ASCIIコード順に並べ変えようと思ったのですが、 もうどこが間違っているかさえわからないぐらいになってしまいました。 まだまだはじめたばかりなもので、わからないことだらけなんで、 できるだけわかりやすい説明おねがいします。 関数の引数に問題があるのじゃないかと思ったのですが、 何かいいアドバイスありましたら、お願いします。 #include <stdio.h> /* 関数のプロトタイプ宣言 */ int strmp(char *,char *); void cpy(char *,char *); int main (void) { /* ポインタ配列の定義 */ char *x[10]={"oneee","twooo","three","fourr","fivee","sixxx","seven","eight","ninee","tennn"}; /* ポインタのポインタの定義 */ char **pp=x; char k[100]; char *p=k; int i,t,a,b,c,d; a=0; /* ポインタ配列を自作関数を使って、ASCIIコードの大きいほうからに並び替える */ for(i=0;i<9;i++) { for(t=1;t<10;t++) { a=strmp(*(pp+i),*(pp+t)); if(a<0) { cpy(p,*(pp+i) ); cpy(*(pp+i),*(pp+t) ); cpy(*(pp+t),p); } } } for(i=0;i<10;i++) { printf("%s ,",x[i]); } printf("\n"); return 0; } /* 文字の比較をする関数 */ int strmp(char *x,char *y) { int i; for(i=0;*(x+i)==*(y+i);i++) { if( *(x+i)=='\0') { return 0; } } return *(x+i)-*(y+i); } /* 文字をコピーする関数 */ void cpy(char *a,char *b) { int i; for(i=0;*(b+i)!='\0';i++) { *(a+i)=*(b+i); } *(a+i)='\0'; }

  • ポインタ

    long ToLittleEndian(char *a,long bytesize){ long i; char lb; char hb; long lsize; lsize=bytesize/2; for (i=0;i<lsize;i++){ hb=*(a++); lb=*(a--); *(a++)=lb; *(a++)=hb; } return 0; } ポインタのアドレスがはみだすとどうなるのでしょうか? 上記のようなコードの場合、最後の処理でポインタaが1バイト分はみ出してしまいますが、 存在しないアドレスを参照しようとするとエラーになるかと思いますが ポインタを動かすだけだと問題ないのでしょうか? 処理系やコンパイラに依るのでしょうか。 初心者ですがよろしくお願いします。

  • 関数のポインタ

    char* check(int a,int b,int c) { return(\"yes\"); とした場合char* checkは何を表しているのでしょうか。 また、 char *str_copy(char d[], char s[]) { char *t=d; while(*d++ = *s++) ; return t; とした場合との違いはあるのでしょうか。 ポインタを返すときに*をつけると教わったのですがいまいち理解できなかったのですがどのような処理が行われているのでしょうか、お願いします。

  • 構造体とポインタ配列

    現在C言語の勉強をしております。 環境はwindowsXP、コンパイラはVC6.0です。 構造体と、ポインタの配列についてなのですが、 以下のような構造体が宣言されている時に、リスト構造にデータがいくつか入っているとします。 // 構造体 typedef struct address { unsigned char names[NAME_SIZE+1]; /* 名前 */ char tels[TEL_SIZE + 1]; /* 電話番号 */ struct address *prev; /* 前へのポインタ */ struct address *next; /* 次へのポインタ */ }Address, *a_pt; そのリスト構造を先頭要素か順番にポインタ配列に格納するには以下の方法ではおかしいでしょうか? /* ポインタ配列を用意する */ Address *array[MAX_COUNT]; /* top_ptは先頭のポインタです */ pt = top_pt; /* データがなくなるまで配列へ格納する */ while(pt != NULL){ array[count++] = pt; pt = pt->next; } /* 配列の最後はNULLとする */ array[count] = NULL; また、配列の中身を確認する方法としては、 printf("配列の中身:%s\n", array[0]->names); では、アドレスが表示されてしまうのかな・・と思ったら、accessViolationで落ちてしまいました・・・。 中身はどうしたらデバッグ出来ますでしょうか? そもそも、以下の2つは何か違いはありますか? Address *ptA[100]; a_pt ptB[100]; 皆さん、どうかよろしくお願いいたします。 理解不能な場合はご指摘ください。

  • ポインタについて

    #include<stdio.h> int main(void){ int i; char *pt = "Hello World"; char a[11]; pt = &a; for(i=0;i<=11;i++){ printf("%c %x\n",pt,&a[i]); pt++; } return(0); } H abcd80(←アドレス) E abcd81(←アドレス) のように表示したいんですが、アドレスだけうまくできてHELLO WORLD のほうがうまく表示できません。 夏休みの間ブランク空けたんでポインタ全て抜けてしまったみたいです^^;  よろしくおねがいします。

専門家に質問してみよう