a
とを追加するロジックを誰でも説明できますb
か?
#include <stdio.h>
int main()
{
int a=30000, b=20, sum;
char *p;
p = (char *) a;
sum = (int)&p[b]; //adding a and b!
printf("%d",sum);
return 0;
}
a
とを追加するロジックを誰でも説明できますb
か?
#include <stdio.h>
int main()
{
int a=30000, b=20, sum;
char *p;
p = (char *) a;
sum = (int)&p[b]; //adding a and b!
printf("%d",sum);
return 0;
}
はここ+
に隠されています:
&p[b]
この式は次と同等です
(p + b)
したがって、実際には次のようになります。
(int) &p[b] == (int) ((char *) a)[b]) == (int) ((char *) a + b) == a + b
これは、オブジェクトを指す必要があるため、技術的には未定義の動作を呼び出すことに注意してください(char *) a
。また、オブジェクトの外側またはオブジェクトを超えるポインター演算は、未定義の動作を呼び出します。
C 標準では、E1[E2]
と同等であると言われてい*((E1) + (E2))
ます。したがって:
&p[b] = &*((p) + (b)) = ((p) + (b)) = ((a) + (b)) = a + b
p[b]
は、配列のb 番目p
の要素です。それは書くようなもの*(p + b)
です。
さて、追加するとき&
は、次のように記述します。p + b * sizeof(char)
これはp + b
. さて、
あなたは(int)((char *) a + b)
どちらが....a + b
+
しかし..キーボードにまだある場合は、それを使用してください。
@gerijeshchauhanがコメントで明らかにしたように、*
逆&
の操作であるため、互いにキャンセルします。そう&*(p + b)
ですp + b
。
p は char へのポインタになります
a は char へのポインタに変換されるため、p はアドレス a のメモリを指します。
次に、添字演算子を使用して、p が指すアドレスを超えて b のオフセットにあるオブジェクトを取得します。b は 20 で、p+20=30020 です。次に、結果のオブジェクトでアドレスオブ演算子が使用され、アドレスが int に変換され、a+b の効果が得られます。
以下のコメントがわかりやすいかもしれません。
#include <stdio.h>
int main()
{
int a=30000, b=20, sum;
char *p; //1. p is a pointer to char
p = (char *) a; //2. a is converted to a pointer to char and p points to memory with address a (30000)
sum = (int)&p[b]; //3. p[b] is the b-th (20-th) element from address of p. So the address of the result of that is equivalent to a+b
printf("%d",sum);
return 0;
}
参考:こちら
char *p;
p
ポインタです(サイズが1バイトの要素への)
p=(char *)a;
p
アドレスでメモリを指すようになりましたa
sum= (int)&p[b];
p
ポインタは配列として使用できますp[]
(この配列の開始アドレス (メモリ内) はa
)
p[b]
b番目の要素を取得することを意味します-この要素のアドレスはa+b
[ (開始アドレス) a
+ b
(b 番目の要素 * 要素のサイズ (1 バイト)) ]
&p[b]
要素のアドレスを取得することを意味しますp[b]
が、そのアドレスはa+b
intへのポインタを使用する場合(ほとんど4バイト)
int* p
p = (int*)a;
合計は a+(4*b)になります
int a=30000, b=20, sum;
char *p; //1. p is a pointer to char
p = (char *) a;
a
タイプは でint
、値は です30000
。上記の代入は、値30000
を からint
に変換しchar*
、結果を に格納しp
ます。
整数をポインターに変換するセマンティクスは、C 標準によって (部分的に) 定義されています。N1570ドラフトのセクション 6.3.2.3 パラグラフ 5 を引用します。
整数は、任意のポインター型に変換できます。前に指定された場合を除き、結果は実装定義であり、正しく配置されていない可能性があり、参照された型のエンティティを指していない可能性があり、トラップ表現である可能性があります。
(非規範的な) 脚注付き:
ポインターを整数に、または整数をポインターに変換するためのマッピング関数は、実行環境のアドレッシング構造と一致するように意図されています。
int
標準は、型と型の相対的なサイズについて保証しませんchar*
。どちらかが他方よりも大きくなる可能性があり、変換によって情報が失われる可能性があります。この特定の変換の結果が有効なポインター値になる可能性はほとんどありません。トラップ表現の場合、割り当ての動作は未定義です。
使用する可能性が高い典型的なシステムでは、char*
は少なくとも と同じ大きさint
であり、整数からポインターへの変換は、おそらく整数の表現を構成するビットをポインター値の表現として再解釈するだけです。
sum = (int)&p[b];
p[b]
は定義上、 と同等です。*(p+b)
ここで、+
はポインター演算を示します。ポインターは を指しchar
、achar
は定義により 1 バイトであるため、この加算により、ポインター先のアドレスb
がメモリ内でバイト (この場合は 20) だけ進められます。
しかしp
、おそらく有効なポインターではないため、それに対して算術演算を実行しようとしたり、その値にアクセスしようとしたりすると、未定義の動作が発生します。
実際には、ほとんどの C コンパイラは、余分なチェックを実行しないコードを生成します。重点が置かれているのは、正しくないコードの検出ではなく、正しいコードの高速実行です。したがって、以前の割り当てで に対応するアドレスに設定する場合、そのアドレスに 、または 20を追加すると、おそらく に対応するアドレスが得られます。p
30000
b
30020
そのアドレスは(p+b)
;の結果です。これで、[]
演算子は暗黙的*
にそのアドレスに演算子を適用し、そのアドレスが指すオブジェクトを提供します。概念的には、これはchar
integer に対応するアドレスに格納されたオブジェクト30020
です。
すぐ&
にそのオブジェクトに演算子を適用します。&
演算子の結果に適用することは[]
、ポインターの加算を行うことと同等であるという特別な場合の規則があります。上記の参照標準草案の 6.5.3.2p2 を参照してください。
したがって、この:
&p[b]
次と同等です。
p + b
上で述べたように、これchar*
は整数値に対応する (タイプ の)アドレスを生成します30020
。もちろん、整数からポインターへの変換が特定の方法で動作し、無効なポインターを作成してアクセスするという未定義の動作が想定されます。値は驚くべきことは何もしません。
最後に、キャスト演算子を使用して、このアドレスを type に変換しますint
。ポインター値の整数への変換も実装定義であり、おそらく未定義です。6.3.2.3p6 の引用:
任意のポインター型を整数型に変換できます。前に指定された場合を除き、結果は実装定義です。結果が整数型で表現できない場合、動作は未定義です。結果は、任意の整数型の値の範囲内にある必要はありません。
char*
aが an より大きいことは珍しくありませんint
(たとえば、32 ビットint
と 64ビットのシステムでこれを入力していますchar*
)。char*
ただし、この場合、値は範囲内の値を変換した結果であるため、オーバーフローは比較的安全ですint
。特定の値を からint
に変換しchar*
たり、 に戻したりしint
て元の結果が得られるという保証はありませんが、少なくとも範囲内の値については、通常はそのように機能します。
そのため、コードがたまたま実行されている実装によって、多くの実装固有の仮定がたまたま満たされている場合、このコードは と同じ結果をもたらす可能性があり30000 + 20
ます。
ちなみに、私はこれが失敗するシステムに取り組んだことがあります。Cray T90 は、ハードウェア アドレスが 64 ビット ワードを指すワード アドレス マシンでした。バイト アドレッシングのハードウェア サポートはありませんでした。しかしchar
、それは 8 ビットだったのでchar*
、void*
ポインタはハードウェアで構築および操作する必要がありました。ポインターは、使用されない上位 3 ビットにバイト オフセットが格納されたchar*
64 ビット ワード ポインターで構成されていました。ポインターと整数の間の変換では、これらの上位ビットは特別に扱われませんでした。それらは単にコピーされました。そのためptr + 1
、(char*)(int)ptr + 1)
非常に異なる結果が得られる可能性があります。
+
しかし、演算子を使用せずに 2 つの小さな整数を足すことができたので、これで終わりです。
ポインター演算の代わりに、bitops を使用することができます。
#include <stdio.h>
#include <string.h>
unsigned addtwo(unsigned one, unsigned two);
unsigned addtwo(unsigned one, unsigned two)
{
unsigned carry;
for( ;two; two = carry << 1) {
carry = one & two;
one ^= two;
}
return one;
}
int main(int argc, char **argv)
{
unsigned one, two, result;
if ( sscanf(argv[1], "%u", &one ) < 1) return 0;
if ( sscanf(argv[2], "%u", &two ) < 1) return 0;
result = addtwo(one, two);
fprintf(stdout, "One:=%u Two=%u Result=%u\n", one, two, result );
return 0;
}
/*
by sch.
001010101 = 85
001000111 = 71
---------
010011100 = 156
*/
#include <stdio.h>
#define SET_N_BIT(i,sum) ((1 << (i)) | (sum))
int sum(int a, int b)
{
int t = 0;
int i = 0;
int ia = 0, ib = 0;
int sum = 0;
int mask = 0;
for(i = 0; i < sizeof(int) * 8; i++)
{
mask = 1 << i;
ia = a & mask;
ib = b & mask;
if(ia & ib)
if(t)
{
sum = SET_N_BIT(i,sum);
t = 1;
/*i(1) t=1*/
}
else
{
t = 1;
/*i(0) t=1*/
}
else if (ia | ib)
if(t)
{
t = 1;
/*i(0) t=1*/
}
else
{
sum = SET_N_BIT(i,sum);
t = 0;
/*i(1) t=0*/
}
else
if(t)
{
sum = SET_N_BIT(i,sum);
t = 0;
/*i(1) t=0*/
}
else
{
t = 0;
/*i(0) t=0*/
}
}
return sum;
}
int main()
{
int a = 85;
int b = 71;
int i = 0;
while(1)
{
scanf("%d %d", &a, &b);
printf("%d: %d + %d = %d\n", ++i, a, b, sum(a, b));
}
return 0;
}