C言語でポインタを使うと間接演算子「*(アスタリスク)」を使うことになりますが、この間接演算子にインクリメント演算子「++」が加わると、どちらを先に演算するのか混乱してしまうときがあります。以下の例について、どういう演算かわかりますか?
p はポインタ変数です。
1. ++*p;
2. *++p;
3. *p++;
4. (*p)++;
1. は、pが指す値自体をインクリメントして参照します。
2. は、pのポインタ位置をインクリメントしてpが指す値を参照をします。
3. は、pが指す値を参照してから、pのポインタ位置をインクリメントします。
4. は、pが指す値を参照してから、pが指す値自体をインクリメントします。
間違いやすいのは、たぶん 3.じゃないかと思います。後置演算の「++」が「*p」にかかるのか「p」にかかるのか、というところです。3. の場合は「p」にかかります。
実際にこれらの演算を行って結果を表示するプログラムを載せておきます。結果を推測してみてください。
コピペするならこちらをお使いください。
#include <stdio.h>
int main( void )
{
char *p;
char a[] = { 1, 3, 5, 7 };
p = a;
/* pの指す値 */
printf( "0: %d\n", *p );
/* pの指す値をインクリメント */
printf( "1: %d\n", ++*p );
/* ポインタをインクリメントしてからpの指す値を参照 */
printf( "2: %d\n", *++p );
/* pの指す値を参照後にポインタをインクリメント */
printf( "3: %d\n", *p++ );
printf( "4: %d\n", *p );
/* pの指す値を参照後にpの指す値をインクリメント */
printf( "5: %d\n", (*p)++ );
printf( "6: %d\n", *p );
return 0;
}
結果は以下となります。
$ ./a.out
0: 1
1: 2
2: 3
3: 3
4: 5
5: 5
6: 6
2020/11/8 追記
配列の添字がついたときの演算子の優先順位も試してみました。ほとんど使うことはないだろうし、混乱するので読み飛ばしてもらっても構いません。。以下の違いです。
5. *++argv[0]
6. (*++argv)[0]
やりたいことは以下のプログラムと同じです。
#include <stdio.h>
int main( int argc, char *argv[] )
{
while( *++argv )
printf( "%s\n", *argv );
return 0;
}
実行時の引数を画面に表示(印字)するだけのプログラムです。引数に「apple」と「orange」をつけて実行すると
$ ./a.out apple orange
apple
orange
という結果が画面に表示されます。これをわざわざ配列を使って処理します。*++argv[0] と (*++argv)[0] が登場します。ソースコードを先に載せておきます。
#include <stdio.h>
int main( int argc, char *argv[] )
{
while( --argc )
{
printf( "%c", (*++argv)[0] );
while( *++argv[0] )
printf( "%c", *argv[0] );
printf( "\n" );
}
return 0;
}
whileで2重ループにしています。文字列(ここでは文字配列と書いておきます)へのポインタ配列を1つづつ進ませつつ、その中で文字配列を1つづつ印字しています。イメージ図を書いておきます。[ ] は *(アスタリスク)や ++(インクリメント演算子)より結合が強いです。
配列を使わずにポインタで処理する場合も載せておきます。
(*++argv)[0] は **++argv に変更
*++argv[0] は *++*argv に変更
としていますが、ここまでくるとよくわからなくなります・・
#include <stdio.h>
int main( int argc, char *argv[] )
{
while( --argc )
{
printf( "%c", **++argv );
while( *++*argv )
printf( "%c", **argv );
printf( "\n" );
}
return 0;
}
実行結果は同じになります。