연산자 상세 설명 - 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.  % 연산자의 두 피연산자는 정수여야 합니다. 나누기 후 나머지 반환

3. 시프트 연산자 

<< 왼쪽 시프트 연산자는 이진 시퀀스를 왼쪽으로 이동합니다.
>> 오른쪽 시프트 연산자는 이진 시퀀스를 오른쪽으로 이동합니다.

시프트 연산자에 대해 말하면 바이너리에 대해 이야기하려고 합니다.

정수의 이진 비트 표현에는 세 가지 형식이 있습니다. 원래 코드 보완 코드 역 코드

양의 정수의 원래 코드와 보수 코드는 동일합니다.

음수 정수의 보수와 보수를 계산해야 합니다.

부호 비트 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) | // (이진) 비트에
따라  또는 (해당하는 한 이진 비트는 1, 1, 모든 0은 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

프로그램 실행 결과

왜곡된 인터뷰 질문:

두 숫자의 교환을 실현하기 위한 임시 변수(세 번째 변수)를 생성할 수 없습니다.

#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 단항 연산자 소개 

!논리 역연산
- 음수
+ 양수
&
피연산자 유형 길이의 주소 크기(바이트)
~ 숫자의 이진 비트 반전
-- 전치사, 후치사 --
++ 전치사, 후위사 ++
* 간접 연산자(역참조 연산자)
( 유형) 캐스트 

C 언어에서 0은 거짓을 의미하고 0이 아닌 것은 참을 의미합니다.

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

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

1. sizeof(배열 이름), 배열 이름은 배열의 첫 번째 요소의 주소가 아니며, 배열 이름은 전체 배열을 나타내며 계산은 전체 배열의 크기입니다.이 경우를 제외하고 모든 배열 이름은 배열의 첫 번째 요소의 주소를 나타냅니다

 sizeof는 함수가 아닌 연산자로 타입별로 생성된 변수가 차지하는 메모리의 크기를 계산하며 단위는 byte이다.

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. 논리 연산자 

&& 논리 및
|| 논리 또는

논리 AND 왼쪽과 오른쪽이 모두 참일 때만 전체 식이 참

논리적이거나 왼쪽과 오른쪽 표현식이 모두 거짓인 경우에만 전체 표현식이 거짓입니다. 

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++는 첫 번째, 다음 ++, ay는 0에서 시작하여 내려가고, ++b, 첫 번째 b++를 사용하고 전체 표현식의 값을 사용하는 경우 가 이미 참이면 다음 d++는 실행되지 않으므로 프로그램의 실행 결과는 1 , 3 , 3 , 4입니다.

9. 조건 연산자 

특급1 ? 특급2 : 특급3

문장 1이 참이면 식의 결과는 문장 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;//실용적인 첨자 참조 연산자.
[ ]의 두 피연산자는 arr과 9입니다.

2. ( ) 함수 호출 연산자는
하나 이상의 피연산자를 허용합니다. 첫 번째 피연산자는 함수 이름이고 나머지 피연산자는 함수에 전달된 매개변수입니다.

참고: 매개변수가 없을 수 있습니다.

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의 바이트 길이이기도 하며 범용 레지스터의 길이이기도 합니다. 따라서 두 char 타입의 추가가 실제로 CPU에서 수행되더라도 먼저 CPU에서 정수 피연산자의 표준 길이로 변환되어야 합니다. 범용 CPU(general-purpose CPU)가 직접 2개의 8비트 바이트를 추가하는 것은 어렵습니다(비록 머신 명령어에 이러한 바이트 추가 명령이 있을 수 있음). 따라서 길이가 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

두 가지 예를 들어라 

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는 소성화 업그레이드가 필요하지 않습니다. b==0xb600은 거짓이지만 c는 발생하지 않는다. 정수를 개선하면 c==0xb6000000 식의 결과는 참이 된다.
프로그램의 결과 출력은 다음과 같다.

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바이트입니다
. )는 4바이트이지만 sizeof(c)는 1바이트입니다. 

12.2 산술 변환 

연산자의 피연산자가 다른 유형인 경우 피연산자 중 하나가 다른 유형으로 변환되지 않으면 연산을 진행할 수 없습니다. 다음 계층 구조를 일반 산술 변환이라고 합니다.

long double
double
float
unsigned long int
long int
unsigned int
int

피연산자의 유형이 위 목록에서 하위에 있는 경우 연산을 수행하기 전에 먼저 다른 피연산자의 유형으로 변환해야 합니다.

12.3 연산자의 속성 

복잡한 표현식의 평가에 영향을 미치는 세 가지 요소가 있습니다.
1. 연산자의 우선순위(인접한 연산자여야 함)
2. 연산자의 결합성
3. 평가 순서 제어 여부.
인접한 두 연산자 중 어느 것이 먼저 실행됩니까? 우선 순위에 따라 다릅니다. 둘 다 동일한 우선 순위를 갖는 경우 연관성에 따라 달라집니다.

우리가 작성하는 표현식이 연산자의 속성을 통해 고유한 계산 경로를 결정할 수 없다면 이 표현식에 문제가 있는 것입니다
.

예를 들어 코드는 여러 컴파일 환경에서 다른 결과를 생성합니다.

#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