*p++と*++pの違い


次に、自増演算子の2つの用法を例によって徹底的に理解する(自減の用法はこれと類似しており、プラス1がマイナス1になったにすぎない).
1、++iとi++の違い
リスト1(コードのコメントに注意):
#include <stdio.h>

int main(void)
{
    int a, b, i = 7;

    i++; //   i = i + 1;
    ++i; //   i = i + 1;

    a = i++; //   a = i; i = i + 1;
    b = ++i; //   i = i + 1; b = i;
    
    printf("a = %d, b = %d
", a, b); return 0; }

例出力結果:
a = 9, b = 11

例では、7行目と8行目の役割は、変数iに1を加えるだけであり、このときiの値は9に増加し、次に10行目の変数aはiの値(すなわち9)を先に取得し、その後iに1を加え、11行目の変数iは先に1を加え、その後bに与えられるので、bの値は11となる.
リスト2のような少し複雑な例です.
#include <stdio.h>

int main(void)
{
    int a = 5;

    int *p = &a;

    int b = (*p)++; //   b = a++;  b = a; a = a + 1;

    int c = ++(*p); //   c = ++a;  a = a + 1; c = a; 
    
    printf("b = %d, c = %d
", b, c); printf("(*p)++ = %d, ++(*p) = %d
", (*p)++, ++(*p)); return 0; }

例出力結果:
b = 5, c = 7
(*p)++ = 8, ++(*p) = 8

この例では、*pによって間接的にaを操作するにすぎず、他の自己増加演算子の使い方はリスト1と類似している.9行目の*pは必ず括弧で囲まなければなりません.そうしないと意味が違います.11行目の++(*p)は、オペランドpに対して演算子*が1つしか計算されていないため、演算子の優先度や結合性に関係なく++*pと書くこともできます.
なお、C言語では関数の各パラメータの評価順序が指定されていないため、15行目のコードは移植不可であり、異なるコンパイラで異なる結果が生じる可能性がある(この例ではGCCは++(*p)、後計算(*p)++であるため、両方とも8に等しい).
知識点:
(1)、副作用
式に対して値を求めると同時に、いくつかの変数の値を修正し、その中で値を修正する行為はC言語の中で副作用と呼ばれている.それはC言語にとって、計算の目的は式に対して値を求めることである.例えば、文int a=5であり、その意味は先に5を求めることであり、それから5を変数aに与えることであり、その後の賦値はこの式の副作用である.自己加算および自己減算演算子は、副作用のために使用され、1または1を加算する以外に、自身に値を付与します.
(2)、演算子の優先度
C言語では、演算子の優先度を15段階に分け、次の表のように、上から下へ、最優先度から最優先度へと順に(記憶を容易にするために15段階を11段階に分け、各クラスに名前を付けた).
初等演算子
括弧()、中括弧[]、メンバーアクセス演算子を含む.和->
単項演算子
自増++と自減--、正負記号+と-、間接演算*とアドレス演算&、タイプ変換(type)、sizeof、論理逆!、位取反~など.
算術演算子
2つのレベルが含まれており、(*、/、%)を乗算してから増減します(+、-).
シフト演算子
左シフト<<と右シフト>.
関係演算子
<未満、<=以下、<=以上、>=以上を含む.
判定演算子
等しい=と等しくない!=を含む.
ビット論理演算子
三級に分けて、位と&、位異または^と位または|の順である.
論理演算子
2段階に分けて、論理と&&と論理または||の順になります.
条件演算子
? :
代入演算子
=、+=、-=、*=、/=、%=、&=、^=、|=、<=、>>=を含む.
カンマ演算子
,
(3)、結合性
同じオペランドの場合、同じ優先度を持つオペレータが2つある場合に、どのオペレータを先に実行するかを決定する問題は、結合性によって決定されます.
同じ優先度のオペレータは、同じ結合性を有します.右結合性とは、式の右端の操作が最初に実行され、右から左に順に実行されることです.C言語では、右結合性を持つオペレータは、対応する3つのクラスのみで、それぞれ一元演算子、条件演算子、賦値演算子です.
注意:C言語の優先度と結合性は、同じオペランドの場合です.式24/8*2のように、オペランド8の場合、/と*の優先度は同じであるため、それらの左結合性から、式は24/8を計算して3を得、その後、3*2を計算して6を得ることが分かる.
C言語では同一演算子に関する複数のオペランドの計算順序は規定されていない(&&,|,?:和,演算子を除く)式a=8*9+20*4のように,オペランド9と20については優先度によって先乗後加が判断できるが,式中の2つの*は同じオペランドを共有していないため,左から右への結合性は適用されず,8*9と20*4の計算順序は不定であり,8*9と20*4を先に計算するか20*4をコンパイラが決定する.
上記の例では、8*9と20*4のどちらが先に実行しても最終結果の一致には影響しませんが、「b=3;a=(b++)*(b++)*(b++);このような例では、異なるコンパイラの最後のaの値は9に等しいか、12に等しいか、16に等しいかのいずれかである.したがって,実際の応用ではこのような不確実性は現れず,自分のニーズに応じて「b=3;c=b++;a=c*c;このような形式.
2、*p++と*++pの違い
例えば、リスト3のように:
#include <stdio.h>

int main(void)
{
    int arr[] = {1, 2, 3, 4};
    int *p = arr;

    int a = *p++; //   a = *(p++);  a = *p; p = p + 1;
    
    int b = *++p; //   b = *(++p);  p = p + 1; b = *p;
    
    printf("a = %d, b = %d
", a, b); return 0; }

例出力結果:
a = 1, b = 3

8行目のオペランドpについては、*と++の優先度は同じであるが、それらの右結合性から、この式では++の優先度が*より高い、すなわち*p++が*(p+)に等価であると考えられる.
一方、10行目のオペランドpでは、演算子++が1つしかないので、++pを計算して結果を出し、間接的に演算します.