숫자가 너무 크면 다음 메모리 위치로 넘겨 집니까?


30

나는 C 프로그래밍을 검토하고 있으며 나를 괴롭히는 몇 가지가 있습니다.

이 코드를 예로 들어 보겠습니다.

int myArray[5] = {1, 2, 2147483648, 4, 5};
int* ptr = myArray;
int i;
for(i=0; i<5; i++, ptr++)
    printf("\n Element %d holds %d at address %p", i, myArray[i], ptr);

int가 최대 2,147,483,647의 최대 값을 보유 할 수 있음을 알고 있습니다. 따라서 그 이상으로 이동하면 다음 메모리 주소로 "유출"되어 해당 주소에서 요소 2가 "-2147483648"으로 표시됩니까? 그러나 출력에서 ​​여전히 다음 주소가 4, 5를 보유한다고 표시하기 때문에 실제로 의미가 없습니다. 번호가 다음 주소로 넘겨지면 해당 주소에 저장된 값이 변경되지 않습니다. ?

MIPS Assembly에서 프로그래밍 한 것을 기억하고 프로그램 단계에서 주소가 값이 변경되는 것을 단계별로 변경하는 것을 기억합니다.

내가 잘못 기억하지 않는 한 여기에 또 다른 질문이 있습니다. 특정 주소에 할당 된 숫자가 유형 (예 : myArray [2])보다 큰 경우 후속 주소에 저장된 값에 영향을 미치지 않습니까?

예 : 주소 0x10010000에 int myNum = 40 억이 있습니다. 물론 myNum은 40 억을 저장할 수 없으므로 해당 주소에 음수로 표시됩니다. 이 큰 수를 저장할 수는 없지만 이후 주소 0x10010004에 저장된 값에는 영향을 미치지 않습니다. 옳은?

메모리 주소는 특정 크기의 숫자 / 문자를 저장할 수있는 충분한 공간을 가지고 있으며 크기가 한계를 초과하면 다르게 표시됩니다 (int에 40 억을 저장하려고 시도하지만 음수로 표시됨). 따라서 다음 주소에 저장된 숫자 / 문자에는 영향을 미치지 않습니다.

내가 배 밖으로 가면 미안해 나는 이것으로부터 하루 종일 주요 뇌 방귀를 앓고 있습니다.


10
문자열 오버런 과 혼동 될 수 있습니다 .
Robbie Dee

19
숙제 : 수정이 있도록 간단한 CPU 않습니다 유출. 당신은 논리가 훨씬 더 복잡해 짐을 알 수 있습니다. "기능"은 처음부터 유용하지 않고 어디에서나 보안 허점을 보장합니다.
phihag

4
정말 큰 숫자가 필요한 경우 큰 숫자에 맞도록 사용하는 메모리의 양을 늘리는 숫자 표현을 가질 수 있습니다. 프로세서 자체는이를 수행 할 수 없으며 C 언어의 기능은 아니지만 라이브러리는이를 구현할 수 있습니다. 일반적인 C 라이브러리는 GNU 다중 정밀도 산술 라이브러리 입니다. 라이브러리는 산술보다 성능 비용이 높은 숫자를 저장하기 위해 메모리를 관리해야합니다. 많은 언어들이 이런 종류의 것을 내장하고 있습니다 (비용을 피하지는 않습니다).
Steve314

1
간단한 테스트를 작성하십시오. 저는 C 프로그래머가 아니라 라인을 따라 무언가를 int c = INT.MAXINT; c+=1;보고 c에 무슨 일이 있었는지보십시오.
JonH

2
@ 존 : 문제는 정의되지 않은 동작의 오버플로입니다. AC 컴파일러는 해당 코드를 발견 하고 무조건 오버플로 하기 때문에 도달 할 수없는 코드라고 추론 할 수 있습니다 . 도달 할 수없는 코드는 중요하지 않으므로 제거 할 수 있습니다. 최종 결과 : 코드가 남아 있지 않습니다.
MSalters

답변:


48

아니 그렇지 않아. C에서 변수에는 작업 할 고정 된 메모리 주소 세트가 있습니다. 4 바이트가있는 시스템에서 작업 ints하고 int변수를 설정 한 2,147,483,647다음 추가 1하는 경우 변수에는 일반적으로 포함 -2147483648됩니다. (대부분의 시스템에서. 동작은 실제로 정의되어 있지 않습니다.) 다른 메모리 위치는 수정되지 않습니다.

본질적으로 컴파일러는 유형에 비해 너무 큰 값을 할당 할 수 없습니다. 컴파일러 오류가 발생합니다. 대소 문자를 강제로 적용하면 값이 잘립니다.

유형이 8 비트 만 저장할 수 있고 경우에 따라 값을 강제로 입력하려고하면 비트 단위로 살펴보면 1010101010101맨 아래 8 비트 또는로 끝납니다 01010101.

당신의 예에서, 상관없이 당신이 무엇을하는 myArray[2], myArray[3]'4'가 포함됩니다. "유출"이 없습니다. 4 바이트 이상인 것을 넣으려고하면 하이 엔드의 모든 것을 벗어나고 아래쪽 4 바이트를 남겨 둡니다. 대부분의 시스템에서 결과는 -2147483648입니다.

실제적인 관점에서, 당신은 이것이 결코 일어나지 않도록하기를 원합니다. 이러한 종류의 오버플로는 종종 해결하기 어려운 결함을 초래합니다. 다시 말해, 당신의 가치가 수십억에 달할 가능성이 있다고 생각되면 사용하지 마십시오 int.


52
4 바이트 정수가있는 시스템에서 작업 중이고 int 변수를 2,147,483,647로 설정 한 다음 1을 추가하면 변수에 -2147483648이 포함됩니다. => 아니요 , 그것은 Undefined Behavior 이므로 루프되거나 다른 일을 할 수도 있습니다. 오버플로가없는 것을 기반으로 컴파일러가 검사를 최적화하고 예를 들어 무한 루프를 보았습니다 ...
Matthieu M.

죄송합니다. 맞습니다. 거기에 "보통"을 추가했을 것입니다.
로봇 고트

언어 관점 에서 @MatthieuM은 사실입니다. 주어진 시스템에서의 실행 측면에서, 우리가 여기서 말하는 것은 절대적으로 말도 안됩니다.
hobbs

@ hobbs : 문제는 정의되지 않은 동작으로 인해 컴파일러가 프로그램을 조작 할 때 실제로 프로그램을 실행하면 실제로 메모리를 덮어 쓰는 것과 비슷한 예기치 않은 동작이 발생한다는 것입니다.
Matthieu M.

24

부호있는 정수 오버플로는 정의되지 않은 동작입니다. 이 경우 프로그램이 유효하지 않습니다. 컴파일러는이를 확인하지 않아도되므로 합리적인 것으로 보이는 실행 파일을 생성 할 수 있지만 보장 할 수는 없습니다.

그러나 부호없는 정수 오버플로는 잘 정의되어 있습니다. 모듈로 UINT_MAX + 1을 래핑합니다. 변수가 차지하지 않는 메모리는 영향을받지 않습니다.

https://stackoverflow.com/q/18195715/951890참조하십시오.


부호있는 정수 오버플로는 부호없는 정수 오버플로와 마찬가지로 잘 정의되어 있습니다. 단어에 $ N $ 비트가 있으면 부호있는 정수 오버 플로우의 상위 경계는 $$ 2 ^ {N-1} -1 $$ ($-2 ^ {N-1} $로 줄 바꿈)에 있습니다. 부호없는 정수 오버플로의 상위 경계는 $$ 2 ^ N-1 $$입니다 (여기서 $ 0 $로 줄 바꿈). 덧셈과 뺄셈에 같은 메커니즘, 같은 크기의 숫자 범위 ($ 2 ^ N $)를 나타낼 수 있습니다. 오버 플로우의 다른 경계.
robert bristow-johnson

1
@ robertbristow-johnson : C 표준에 따르지 않습니다.
Vaughn Cato

글쎄, 표준은 종종 시대에 뒤 떨어진다 SO 참조를 살펴보면, 직접 언급 한 한 가지 의견이있다. "그러나 여기서 중요한 점은 2의 보수 부호 산술 이외의 다른 것을 사용하여 현대 세계에는 아키텍처가 남아 있지 않다는 점이다. 언어 표준은 여전히 ​​구현을 허용한다. 예를 들어 PDP-1은 순수한 역사적 유물입니다. – Andy Ross 8 월 12 일 '13시 20:12 "
robert bristow-johnson

내가 그것을 생각 하지 C 표준에서,하지만 난 일반 이진 연산이 사용되지 않는 구현이있을 수도있을 것 같군요 int. 회색 코드 또는 BCD 또는 EBCDIC을 사용할 수 있다고 가정합니다 . 왜 누군가가 회색 코드 또는 EBCDIC으로 산술을 수행하기 위해 하드웨어를 설계해야하는지 모르지만, 다시 말하지만, 왜 누군가가 unsigned바이너리로 작업하고 int2의 보수 이외의 것으로 서명 해야하는지 모르겠습니다 .
robert bristow-johnson 2012

14

여기에는 두 가지가 있습니다.

  • 언어 수준 : C의 의미는 무엇입니까
  • 머신 레벨 : 사용하는 어셈블리 / CPU의 의미는 무엇입니까

언어 수준에서 :

C에서 :

  • 오버플로 및 언더 플로는 부호없는 정수에 대한 모듈로 산술로 정의 되므로 해당 값 "루프"
  • 오버플로 및 언더 플로는 부호있는 정수에 대해 정의되지 않은 동작 이므로 모든 일이 발생할 수 있습니다

"무엇이든"예제를 원하는 사람들을 위해 다음과 같이 보았습니다.

for (int i = 0; i >= 0; i++) {
    ...
}

로 전환 :

for (int i = 0; true; i++) {
    ...
}

그렇습니다. 이것은 합법적 인 변화입니다.

이는 이상한 컴파일러 변환으로 인해 오버플로에서 메모리를 덮어 쓸 수있는 잠재적 인 위험이 있음을 의미합니다.

참고 : Clang 또는 gcc에서는 부호없는 정수의 언더 플로 / 오버플로를 중단 -fsanitize=undefined하는 정의되지 않은 동작 살균제 를 활성화하기 위해 디버그에서 사용합니다 .

또는 연산 결과를 사용하여 배열에 색인화 (체크 해제)하여 메모리를 겹쳐 쓸 수 있음을 의미합니다. 언더 플로 / 오버플로 감지가없는 경우 불행히도 훨씬 더 가능성이 높습니다.

참고 : Clang 또는 gcc -fsanitize=address에서 디버그에서 Address Sanitizer 를 활성화하면 범위를 벗어난 액세스가 중단됩니다.


기계 수준에서 :

실제로 사용하는 조립 지침 및 CPU에 따라 다릅니다.

  • x86에서 ADD 는 오버플로 / 언더 플로에서 2 보완을 사용하고 OF (오버플로 플래그)를 설정합니다
  • 향후 Mill CPU에는 다음과 같은 4 가지 오버플로 모드가 있습니다 Add.
    • 모듈로 : 2 보수 모듈로
    • 트랩 : 트랩이 생성되어 계산이 중단됩니다.
    • 포화 : 언더 플로에서 값이 최소로 고정되거나 오버플로에서 최대로 고정됨
    • 이중 너비 : 결과는 이중 너비 레지스터에서 생성됩니다.

레지스터 나 메모리에서 일이 발생하는지 여부에 관계없이 CPU는 오버플로시 메모리를 덮어 씁니다.


마지막 세 가지 모드가 서명 되었습니까? (두 번째 보수이므로 첫 번째 것은 중요하지 않습니다.)
중복 제거기

1
@ 중복 제거기 : Mill CPU 프로그래밍 모델 소개에 따르면 서명 된 추가 및 서명되지 않은 추가를위한 다른 opcode가 있습니다. 두 opcode 모두 4 가지 모드를 지원하고 다양한 비트 폭 및 스칼라 / 벡터에서 작동 할 수있을 것으로 기대합니다. 그리고 다시, 그것은 현재 증기 하드웨어입니다;)
Matthieu M.

4

@StevenBurnap의 답변을 더 나아 가기 위해서는 이것이 컴퓨터 수준에서 컴퓨터가 작동하는 방식 때문입니다.

어레이는 메모리에 저장됩니다 (예 : RAM). 산술 연산을 수행 할 때 메모리의 값이 산술을 수행하는 회로의 입력 레지스터 (ALU : 산술 논리 유닛 )에 복사 된 다음 입력 레지스터의 데이터에 대해 연산이 수행되어 결과가 생성됩니다. 출력 레지스터에. 이 결과는 메모리의 올바른 주소에서 메모리로 다시 복사되어 메모리의 다른 영역은 그대로 유지됩니다.


4

먼저 (C99 표준으로 가정) <stdint.h>표준 헤더 를 포함 하고 거기에 정의 된 일부 유형, 특히 int32_t정확히 32 비트 부호있는 정수 또는 uint64_t정확히 64 비트 부호없는 정수 등을 사용할 수 있습니다. int_fast16_t성능상의 이유로 같은 유형을 사용할 수 있습니다 .

부호없는 산술이 인접한 메모리 위치로 넘치거나 넘치지 않는다고 설명하는 다른 답변을 읽으십시오. 부호있는 오버플로 에서 정의되지 않은 동작 에 주의하십시오 .

그런 다음 정확히 큰 정수 를 계산해야하는 경우 (예 : 10 진수로 2568 자리를 모두 사용하여 1000의 계승을 계산하려는 경우) 임의의 정밀도 숫자 (또는 큰 숫자 )와 같은 bigint를 원합니다 . 효율적인 bigint 산술 알고리즘은 매우 영리하며 일반적으로 특수한 기계 명령어 (예 : 프로세서에 캐리가있는 단어 추가)를 사용해야합니다. 따라서이 경우 GMPlib 와 같은 기존 bigint 라이브러리 를 사용하는 것이 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.