TLDR
LValueとRValueの話をさらっとまとめた備忘録のようなものです。
d = c++++
のようにそのまま連続させるのは無理でしたが他の演算子を組み合わせれば出来ました。
導入
以下文中のインクリメントは++と書きます。
まずはシンプルに試します。
#include<stdio.h>
int main(void){
int n = 0;
printf("%d",n++++));
return 0;
}
lvalue required as increment operand
動きません。コンパイラにエラーを吐かれます。前置や後置を入れ替えて試してみましたがどうやら無理そう。
エラー文を読むと++のオペランドにはLValueが要求されるとのこと。LValueってなんだろう。
LValueとRValue
C言語においてすべての式は値を持つわけですが、それがLValueとRValueの二つのいずれかに分類されるようです。
lvalue simply means an object that has an identifiable location in memory (i.e. having an address).
- In any assignment statement “lvalue” must have the capability to store the data.
- lvalue cannot be a function, expression (like a+b) or a constant (like 3 , 4 , etc.).
LValueとはメモリ内に識別可能なアドレスを持つオブジェクトです、とあります。
オブジェクトって何?C言語ってオブジェクト指向じゃないよね?と思いさらに検索。説明を見つけました。
引用の引用ですが
In C, an object is anything that takes up storage. C 2011 online draft:
- Terms, definitions, and symbols
...
3.15
1 object
region of data storage in the execution environment, the contents of which can represent values
とあります。C言語では実行環境において値を表すデータストレージ内の領域をオブジェクトと呼ぶとのこと。
他にもLValueとRValueについてこんな説明を見つけました。
Overview
An lvalue refers to an object that persists beyond a single expression. An rvalue is a temporary value that does not persist beyond the expression that uses it.
つまり式の評価後も残るオブジェクトはLValue、それ以外の一時的な値がRValueに相当するそうです。
ちなみに具体的にLvalueを列挙するとこんな感じになるようです。学習途中の身からすると全ては理解できませんが。
- The name of the variable of any type i.e. , an identifier of integral, floating, pointer, structure, or union type.
- A subscript ([ ]) expression that does not evaluate to an array.
- A unary-indirection (*) expression that does not refer to an array
- An l-value expression in parentheses.
- A const object (a nonmodifiable l-value).
- The result of indirection through a pointer, provided that it isn’t a function pointer.
- The result of member access through pointer(-> or .)
ポインタのインクリメント++
ポインタの++ならば評価後の値はアドレスなんだし連続で++できるのでは?と思いましたがこれもできませんでした。
#include<stdio.h>
#include <unistd.h>
int main(void){
int n[3] = {10,20,30};
int *p = n;
printf("%p",p++++);
return 0;
}
lvalue required as increment operand
p++
の値はアドレスを表す一時的な値、RValueであってこれ自体がメモリ内にアドレスを持つわけじゃないってことですね。ややこしいです。
しかし間接参照演算子(*)を使うことで次のように()を挟みつつですが++を連続させるコードが書けます。
#include<stdio.h>
#include <unistd.h>
int main(void){
int n[3] = {10,20,30};
int *p = n;
printf("%d",(*p++)++);
return 0;
}
10
*p++
はn[0]
そのものでありLValueであるということです。
したがって左辺にインクリメントが来るこういう奇妙なコードも書けます。
#include<stdio.h>
#include <unistd.h>
int main(void){
int n[3] = {10,20,30};
int *p = n;
*p++ = (*p++)++;
printf("%d",n[0]);
return 0;
}
11
次コードはどうでしょうか。
#include<stdio.h>
#include <unistd.h>
int main(void){
int n[4] = {10,20,30,40};
int *p = n;
printf("%d",(*(&*p++)++)++);
return 0;
}
lvalue required as increment operand
動きません。アドレス演算子(&)の評価後の値はRValueです。
インクリメント++をいっぱい使いたい
ポインタのポインタを使うことでさらに++を使えます。下のコードでは3つだけですが同様にポインタのポインタのポインタの、、、としていくことで1つの式の中でいくつでも++を使えそうです。
#include<stdio.h>
#include<math.h>
int main(void) {
int n[3][3];
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
n[i][j] = pow(10,i+1)*(j+1);
}
}
int *p1[3] = {n[0],n[1],n[2]};
int **p2 = p1;
printf("%d", (*(*p2++)++)++);
return 0;
}
10
終わりに
ここまで読んでいただきありがとうございました。
余談ですがC#がC++++という名前じゃないのもC++++がプログラム上で意味をなさないからだそうです。