C 언어 - 바이너리/시프트 연산자/비트 연산자_학습 노트

메모리 내 데이터 저장에 대한 자세한 내용을 보려면 C 언어 - 메모리 내 데이터 저장_스터디 노트를 찾아보세요.

바이너리

바이너리 관련 개념 지식

이진법은 컴퓨터를 위해 특별히 고안된 숫자 체계라고 할 수 있는데, 이 체계에는 0과 1이라는 두 개의 숫자만 있고, 캐리 규칙은 "두 개가 하나를 전달한다"입니다.

바이너리는 여전히 컴퓨터에게 매우 의미가 크며, 컴퓨터는 전자회로를 통해 연결된 일련의 하드웨어를 통해 관련 계산을 수행합니다.

컴퓨터 회로에서 이진수는 일반적으로 논리 신호를 사용하여 표현됩니다. 즉, 회로에는 하이 레벨과 로우 레벨의 두 가지 상태만 있습니다. 그 중 하이 레벨은 이진수로 1을 나타내고, 로우 레벨은 이진수로 0을 나타냅니다.

이진수를 십진수로

이진 시스템의 각 비트의 가중치는 오른쪽에서 왼쪽으로 2의 0제곱, 2의 1제곱, 2의 2제곱... 등입니다.
예: 이진수 1101을 십진수 13으로 변환
여기에 이미지 설명을 삽입하세요.

10진수를 2진수로

10진수를 2로 나누고 나머지를 취하여 역순으로 배열하는 방법을 이용하여 2진수로 변환한다.

구체적인 방법은 다음과 같습니다. 십진 정수를 2로 나누면 몫과 나머지를 얻을 수 있습니다. 그런 다음 몫을 2로 나누면 몫과 나머지를 얻을 수 있고, 몫이 1보다 작아질 때까지 계속해서 다음을 사용합니다. 이진수로 먼저 얻은 나머지는 숫자의 하위 유효 비트를 이진수의 상위 유효 비트로 사용하고 나머지 나머지는 순서대로 배열합니다.
여기에 이미지 설명을 삽입하세요.

원본코드, 역코드, 보완코드

정수의 이진 표현 방법에는 원본 코드, 보완 코드, 보완 코드의 세 가지가 있습니다.

세 가지 표현 방식은 부호 비트와 숫자 비트 두 부분으로 구성되며, 이진수열에서 가장 높은 1비트가 부호 비트이고 나머지가 숫자
비트이다. 그 중 부호비트가 0이면 그 숫자가 양수라는 뜻이고, 부호비트가 1이면 음수라는 뜻이고, 1이면 음수라는 뜻이다.

양의 정수의 원래코드, 보수코드, 보수코드는 동일하다.

예외는 음의 정수입니다. 음의 정수의 세 가지 코드는 서로 다른 표현 방법을 갖습니다:
원래 코드: 부호 비트는 1이고 숫자 비트는 양수의 숫자 비트와 같습니다. (5와 -5의 숫자 비트는 동일하며 -5의 부호 비트는 1, 5의 부호 비트는 0입니다.)
역코드: 원래 코드의 부호 비트는 변경되지 않고 숫자 비트가 반전됩니다. 역코드를 얻기 위해 비트 단위로.
보완 코드: 보완 코드 + 1을 누르면 보완 코드를 얻을 수 있습니다.
예: (C언어에서는 VS2020 환경에서 int형, 값 -5, 바이너리 관련 표현은 다음과 같습니다.)
여기에 이미지 설명을 삽입하세요.
Integer의 경우 데이터가 보수 형태로 메모리에 저장됩니다.

여기서 지식을 확장해 보겠습니다. 왜 컴퓨터에서 값은 항상 2의 보수로 표현되고 저장됩니까
? 그 이유는 2의 보수를 사용하면 부호 비트와 숫자 비트를 균일하게 처리할 수 있고 동시에 덧셈과 뺄셈도 가능하기 때문입니다. (CPU에는 가산기만 있음) 또한, 보완 코드와 원본 코드를 서로 변환하는 연산 과정도 동일하며, 별도의 하드웨어 회로 설계 없이 구현 가능하다.

시프트 연산자(시프트 연산자의 피연산자는 정수만 가능)

<<왼쪽 시프트 연산자(왼쪽은 버리고 오른쪽에는 0을 추가)

여기에 이미지 설명을 삽입하세요.

>>오른쪽 시프트 연산자(오른쪽 시프트 연산에는 두 가지 유형이 있음)

1. 논리적 오른쪽 시프트: 왼쪽을 0으로 채우고 오른쪽을 버립니다.

여기에 이미지 설명을 삽입하세요.

2. 산술 오른쪽 시프트: 왼쪽을 원래 값의 부호 비트로 채우고 오른쪽을 버립니다.

여기에 이미지 설명을 삽입하세요.

추신: 시프트 연산자의 경우 음수 비트(예: 3 <<-1 또는 3 >> -1)를 이동하지 마십시오. 이는 표준에 정의되어 있지 않습니다.
추신: 일반적으로 논리적 오른쪽 시프트가 더 일반적이며 VS2020 환경의 오른쪽 시프트 연산도 논리적 오른쪽 시프트입니다.

교대 연산자의 적용

  1. 신속하게 거듭제곱을 계산합니다
    . 숫자를 여러 거듭제곱만큼 왼쪽으로 이동하는 것은 2의 여러 거듭제곱을 곱하는 것과 같습니다.
    숫자를 여러 거듭제곱만큼 오른쪽으로 이동하는 것은 2의 여러 거듭제곱으로 나누는 것과 같습니다.
int num = 10;
# 10乘以23次方
result = x << 3

# 10除以2的n次方
result = x >> 3
  1. 시프트 연산자와 비트 연산자를 함께 사용하는 애플리케이션 시나리오는 다양합니다.

비트 연산자

"&" 비트 AND

두 해당 이진 비트의 경우 두 비트가 모두 1인 경우에만 결과는 1이고, 그렇지 않으면 0입니다.

//例如:5 & 8 = 0
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00000000 //结果为0

"|" 비트 OR

해당하는 두 개의 이진 비트 중 하나가 1이면 결과는 1이고 두 비트가 모두 0인 경우에만 결과가 0입니다.

//例如:5 | 8 = 13
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00001101 //结果为13

"^" 비트 OR

두 해당 이진 비트의 경우 두 비트가 다르면 결과는 1이고 두 비트가 같으면 결과는 0입니다.

//例如:5 ^ 8 = 13
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00001101 //结果为13

"~" 비트 부정

피연산자의 모든 비트(부호 비트 포함)를 뒤집습니다.

//例如:~ 5 = -6
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
————————————————————————————————————————————————————————————————————————————————
11111111 11111111 11111111 11111010 //一个负数的补码
10000000 00000000 00000000 00000101 //反码
10000000 00000000 00000000 00000110 //原码,结果为-6

비트 연산자의 마법

  1. 두 숫자를 교환하기 위해 임시 변수를 생성할 수 없습니다(즉, 세 번째 변수를 사용할 수 없습니다).
#include <stdio.h>
int main()
{
    
    
	int a = 10;
	int b = 20;
	printf("交换前:a = %d b = %d\n", a, b);

	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("交换后:a = %d b = %d\n", a, b);
	return 0;
}

출력 결과:

交换前:a = 10 b = 20
交换后:a = 20 b = 10
  1. 정수에 대해 메모리에 저장된 이진수 1의 수를 구합니다.
#include <stdio.h>
int main()
{
    
    
	int num = -1;
	int i = 0;
	int count = 0;//计数
	while (num)
	{
    
    
		count++;
		num = num & (num - 1);
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

출력 결과:

二进制中1的个数 = 32
  1. 정수 배열에서는 하나의 숫자만 한 번 나타나고 다른 배열은 쌍으로 나타나므로 한 번만 나타나는 숫자를 찾으세요.
    예:
    배열에 1 2 3 4 5 1 2 3 4가 있습니다. 5만 한 번 나타나고 다른 숫자는 두 번 나타납니다. 5를 찾으세요.
#include <stdio.h>

int find_single_dog(int arr[], int sz)
{
    
    
    int ret = 0;
    int i = 0;
    for (i = 0; i < sz; i++)
    {
    
    
        ret ^= arr[i];
    }
    return ret;
}
int main()
{
    
    
    int arr[] = {
    
     1,2,3,4,5,1,2,3,4 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int dog = find_single_dog(arr, sz);
    printf("%d\n", dog);


    return 0;
}

출력 결과

5

지식 포인트 축적

1. n&(n-1)

이 코드 줄을 한 번 실행하면 n의 2의 보수에서 가장 오른쪽 1이 사라집니다.

즉, 이 코드 줄을 여러 번 실행하면 n의 2의 보수에 1이 몇 개 있는지 표시됩니다. 코드는 아래와 같이 표시됩니다.

#include <stdio.h>
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
    
    
		n = n & (n - 1);
		count++;
	}
	printf("%d", count);
}

이 표현식은 n이 2의 거듭제곱인지 여부를 확인하는 데에도 사용할 수 있습니다. 코드는 아래와 같이 표시됩니다.

#include <stdio.h>
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	if ((n & (n - 1) )== 0)
	{
    
    
		printf("YES\n");
	}
	else
	{
    
    
		printf("NO\n");
	}
}

추천

출처blog.csdn.net/yjagks/article/details/132529283