演算子の詳しい解説 - C言語

目次

1. 演算子の分類:

2. 算術演算子

3. シフト演算子 

3.1 左シフト演算子

3.2 右シフト演算子

4. ビット演算子 

5. 代入演算子 

6. 単項演算子

6.1 単項演算子の概要 

7. 関係演算子

8. 論理演算子 

9. 条件演算子 

10. カンマ式 

11. 添字参照、関数呼び出し、構造体メンバー 

12. 式の評価 

12.1 暗黙的な型変換 

12.2 算術変換 

12.3 演算子のプロパティ 


1. 演算子の分類:

  算术操作符
  移位操作符
  位操作符
  赋值操作符
  单目操作符
  关系操作符
  逻辑操作符
  条件操作符
  逗号表达式
  下标引用、函数调用和结构成员

2. 算術演算子

                                  +   -  *  /  %

1. % 演算子に加えて、他のいくつかの演算子が整数と浮動小数点数に作用します。
2.  / 演算子の場合、両方のオペランドが整数の場合、整数の除算が実行されます。そして、浮動小数点数が存在する限り、浮動小数点数の除算が実行されます。
3.  % 演算子の 2 つのオペランドは整数である必要があります。除算後の剰余を返します

3. シフト演算子 

<< 左シフト演算子はバイナリ シーケンスを左にシフトします
>> 右シフト演算子はバイナリ シーケンスを右にシフトします

シフト演算子といえばバイナリについて話そうとしています

整数のバイナリ ビット表現には 3 つの形式があります。 元のコード 補数コード 逆コード

正の整数の元のコードと補数コードは同じです

1の補数と負の整数の補数を計算します

符号ビット 0 - 正の数を示します

            1 - 負の数を示します

整数は 2 の補数でメモリに格納されます

               int a = 5
原码 000000000000000000000000000000000101
反码 000000000000000000000000000000000101
补码 000000000000000000000000000000000101
                               int a = -5
原码 10000000000000000000000000000101
反码 11111111111111111111111111111010 (原码的符号位不变,其他位取反的就是补码)
补码 11111111111111111111111111111011  (反码+1就是补码)

3.1 左シフト演算子

左シフト: バイナリ シーケンスの左側を破棄し、右側に 0 を追加します。

int main()
{
	int a = 5;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

 プログラム実行結果

int main()
{
	int a = -5;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

プログラム実行結果

注: コンパイラによって出力される値はすべてオリジナルのコードです 

元コードの負数の補数と補数コードの変換規則

3.2 右シフト演算子

右にシフトして2つに

算術右シフト: 右側は破棄され、左側は元の符号ビットで埋められます。

論理右シフト: 右側を破棄し、左側に 0 を追加します。 

算術右シフトか論理右シフトかはコンパイラ次第です

注: シフト演算子の場合、負のビットをシフトしないでください。これは標準では定義されていません。  

4. ビット演算子 

& // (バイナリ) ビットに従って、および (対応するバイナリ ビットが 0 である限り、それは 0 であり、すべての 1 のみが 1 です) | // (バイナリ) ビットに従って、または (対応するバイナリ ビットが 0 である
限り 、バイナリ ビットは 1、それは 1、すべて 0 のみ)
^ //排他的または(バイナリ)ビットによる(対応するバイナリ ビットは 0、異なるビットは 1)
注: オペランドは整数である必要があります

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	int d = a | b;
	int e = a ^ b;
	printf("%d\n", c);
	printf("%d\n", d);
	printf("%d\n", e);

	return 0;
}
00000000000000000000000000000011  -> 3的补码
11111111111111111111111111111011  -> -5的补码

(再次提醒,编译器打印出来的是原码,而正数原码反码补码相同)
00000000000000000000000000000011  -> 3&-5的结果 结果是3

(补码)
11111111111111111111111111111011  -> 3|-5的结果
10000000000000000000000000000101  (原码) 结果是-5

(补码)
11111111111111111111111111111000  -> 3^-5的结果
10000000000000000000000000001000  (原码) 结果是-8

プログラム実行結果

ひねくれた面接の質問:

2 つの数値の交換を実現するための一時変数 (3 番目の変数) を作成できません

#include <stdio.h>
int main()
{
   int a = 10;
   int b = 20;
   a = a^b;
   b = a^b;
   a = a^b;
   printf("a = %d b = %d\n", a, b);
   return 0;
}

初心者はそれを知ることを期待せず、そのような方法があることを知ってください。

5. 代入演算子 

代入演算子は、これまで満足できなかった値を取得できるようにする優れた演算子です。つまり、自分自身を再割り当てすることができます。

int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值

複合代入演算子 

+= -= *= /= %= >>= <<= &= |= ^=

例: int x += 2 ; //x = x+2

6. 単項演算子

6.1 単項演算子の概要 

! 論理逆演算
- 負の値
+ 正の値
&
アドレスを取る オペランドの型の長さのサイズ (バイト単位)
~ 数値の 2 進ビットごとの反転
-- 前置詞、後置置 --
++ 前置詞、後置置 ++
* 間接演算子 (逆参照演算子)
(タイプ) キャスト 

C 言語では、0 は false を意味し、0 以外は true を意味します

int num =10;
if(num)
{
   printf("hehe\n);
}

//!num 就是假 if语句不进入

1. sizeof (配列名)、配列名は配列の最初の要素のアドレスではありません、配列名は配列全体を表し、計算は配列全体のサイズです。この場合を除き、すべての配列名は配列の最初の要素のアドレスを表します。 

 sizeof は関数ではなく演算子で、型によって作成された変数が占めるメモリのサイズをバイト単位で計算します。

sizeof() の式は計算に関与しません。 

sizeof に関する厄介な質問

#include <stdio.h>
int i;
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

Q: プログラムは何を出力しますか?

説明: まず、グローバル変数が初期化されていない場合、デフォルトで 0 に設定されるため、この時点の i の値は 0 であり、sizeof 演算子の戻り値は size_t で、size_t は符号なしの数値を表します。ここでこれは、式は算術変換 (i > sizeof(i)) を実行する必要があり、コンパイラーは左の i を符号なし整数データに自動的に変換します。-1 に対応する符号なし整数は、4 または 8 を超える非常に大きな数です。したがって、最終結果は「>」を出力します。

& はアドレスと * 間接アクセス演算子 (逆参照演算子) を受け取ります

int main()
{
	//& 取地址操作符
	//* 解引用操作符(间接访问操作符)

	int a = 10;
	int* pa = &a;
	*pa = 20;//* - 解引用操作符
	//
	//*&a ==> a;
	return 0;
}

7. 関係演算子

> >= < <= != ==

何も言うことはありません 

8. 論理演算子 

&& 論理的および
|| 論理的 or

論理 AND 左と右の式が両方とも true の場合にのみ、式全体が true になります。

論理的、または左と右の式が両方とも false の場合にのみ、式全体が false 

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++;

	i = a++ || ++b || d++;

	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	return 0;
}

分析: i = a++ || ++b || d++; a++ は最初に a を使用し、その後 ++ を使用し、ay は 0 から始まり、下に進み、++b、最初の b++ が使用され、式全体の値が使用されます。がすでに true である場合、次の d++ は実行されないため、プログラムの実行結果は 1 、 3 、 3 、 4 になります。

9. 条件演算子 

経験値1 ? exp2 : exp3

ステートメント 1 が true の場合、式の結果はステートメント 2 になり、それ以外の場合はステートメント 3 になります。 

int main()
{
	int a = 3;
	int b = 5;
	int m = (a > b ? a : b);
	printf("%d\n", m);
	return 0;
}

10. カンマ式 

カンマ式は、カンマで区切られた複数の式です。
カンマ式は左から右に順番に実行されます。式全体の結果は最後の式の結果になります 

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("a=%d b=%d\n", a, b);
	printf("%d\n", c);
	return 0;
}

11. 添字参照、関数呼び出し、構造体メンバー 

1. [ ] 添字参照演算子

オペランド: 配列名 + インデックス値

int arr[10];//配列を作成します
arr[9] = 10;//実用的な添字参照演算子。
[ ] の 2 つのオペランドは arr と 9 です。

2. ( ) 関数呼び出し演算子は
1 つ以上のオペランドを受け入れます。最初のオペランドは関数名で、残りのオペランドは関数に渡されるパラメーターです。

注: パラメータを指定することはできません

3. 構造体のメンバにアクセスする
 構造体 メンバ名
-> 構造体ポインタ -> メンバ名 

#include <stdio.h>
struct Stu
{
   char name[10];
   int age;
   char sex[5];
   double score;

};

void set_age1(struct Stu stu)
{
   stu.age = 18;
}

void set_age2(struct Stu* pStu)
{
   pStu->age = 18;//结构成员访问
}

int main()
{
   struct Stu stu;
   struct Stu* pStu = &stu;//结构成员访问
   stu.age = 20;//结构成员访问
   set_age1(stu);
   pStu->age = 20;//结构成员访问
   set_age2(pStu);
   return 0;
}

12. 式の評価 

式が評価される順序は、演算子の優先順位と結合性によって部分的に決まります。
同様に、一部の式のオペランドは、評価中に他の型に変換する必要がある場合があります。 

12.1 暗黙的な型変換 

C の整数算術演算は常に、少なくともデフォルトの整数型の精度で実行されます。この精度を実現するために、式内の文字オペランドと短整数オペランドは、使用前に単純な整数型に変換されます。この変換は、整数昇格と呼ばれます。


整数昇格の重要性:
式の整数演算は、CPU の対応する計算デバイスで実行される必要があります。CPU の整数演算ユニット (ALU) のオペランドのバイト長は、一般に int のバイト長です。これは、CPU のバイト長、汎用レジスタの長さでもあります。したがって、2 つの char 型の加算が実際に CPU によって実行される場合でも、最初に CPU で整数オペランドの標準長に変換する必要があります。汎用 CPU (汎用 CPU) では、8 ビット 2 バイトを直接加算することは困難です (機械語命令にはそのようなバイト加算命令があるかもしれませんが)。したがって、長さが int の長さよりも小さい可能性がある式内のさまざまな整数値は、計算のために CPU に送信される前に int または unsigned int に変換する必要があります。 

文字a、b、c;
...
a = b + c;

b と c の値は、加算演算が実行される前に通常の整数に変換されます。

加算演算が完了すると、結果は a に格納される前に切り捨てられます。

全体的な改善をどのように実行するか?

整数昇格は、変数のデータ型の符号ビットに従って昇格されます。

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

2つの例を挙げてください 

int main()
{
	char c1 = 3;
	//首先要进行整型提升
	//00000000000000000000000000000011
	//00000011 - c1
	char c2 = 127;
	//首先要进行整型提升
	//00000000000000000000000001111111
	//01111111 - c2
	

	char c3 = c1 + c2;
	//00000000000000000000000000000011
	//00000000000000000000000001111111
	//00000000000000000000000010000010
	// 这里要发生截断
	//10000010 - c3
	// 此时c1是字符型,但我们是要以整型形式打印,所以还要进行整型提升。
	//11111111111111111111111110000010 补码
	//11111111111111111111111110000001 反码 
	//10000000000000000000000001111110 原码
	//-126
	
	printf("%d\n", c3);//
	
	return 0;
}
int main()
{
   char a = 0xb6;
   short b = 0xb600;
   int c = 0xb6000000;
   if(a==0xb6)
     printf("a");
   if(b==0xb600)
     printf("b");
   if(c==0xb6000000)
     printf("c");
   return 0;
}

この例では、a と b は可塑化してアップグレードする必要がありますが、c は可塑化してアップグレードする必要はありません。a と b は可塑化してアップグレードすると負の数になるため、式の結果は a==0xb6、 b==0xb600 は false ですが、c は発生しません 整数が改善されると、式 c==0xb6000000 の結果は true になります。
プログラムによって出力される結果は次のとおりです。

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	return 0;
}

c が式の演算に参加している限り、可塑的プロモーションが発生し、式 +c がプロモートされるため、sizeof(+c) は 4 バイトになります。式 -c も可塑的プロモーションを受けるため、sizeof(
-c ) は 4 バイトですが、sizeof(c) は 1 バイトです。 

12.2 算術変換 

演算子のオペランドの型が異なる場合、一方のオペランドが他方のオペランドの型に変換されない限り、操作は続行できません。以下の階層を通常の算術変換といいます。

long double
double
float
unsigned long int
long int
unsigned
int

オペランドの型が上記のリストの下位にある場合は、操作を実行する前に、まず他のオペランドの型に変換する必要があります。

12.3 演算子のプロパティ 

複雑な式の評価に影響を与える要因は 3 つあります。
1. 演算子の優先順位 (隣接する演算子である必要があります)
2. 演算子の結合性
3. 評価の順序を制御するかどうか。
隣接する 2 つの演算子のうちどちらが最初に実行されますか? 優先順位によって異なります。両方の優先度が同じ場合、それは結合性によって異なります。

作成した式が演算子の属性を通じて一意の計算パスを決定できない場合、この式には問題があります

たとえば、コードはいくつかのコンパイル環境で異なる結果を生成します。

#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}

おすすめ

転載: blog.csdn.net/m0_63562631/article/details/125821487