어레이 인덱스가 바인딩되지 않은 동작
어레이 인덱스가 경계를 벗어나는 경우 C/C++가 구별되는 이유
#include <stdio.h>
int main()
{
int a[10];
a[3]=4;
a[11]=3;//does not give segmentation fault
a[25]=4;//does not give segmentation fault
a[20000]=3; //gives segmentation fault
return 0;
}
프로세스 또는 스레드에 할당된 메모리에 액세스하려고 하는 것을 알고 있습니다.a[11]
또는a[25]
스택 경계에서 벗어나고 있습니다.a[20000]
.
컴파일러 또는 링커가 어레이 크기를 인식하지 못하는 이유는 무엇입니까?그렇지 않으면 어떻게 해야 합니까?sizeof(a)
올바르게 동작합니까?
문제는 C/C++가 어레이에 관한 경계체크를 실제로 하지 않는다는 것입니다.유효한 메모리에 액세스 하고 있는 것을 확인하는 것은, OS 에 의해서 다릅니다.
이 경우 스택 기반 어레이를 선언합니다.특정 구현에 따라 어레이의 경계 밖에 액세스하면 이미 할당된 스택스페이스의 다른 부분에 액세스 할 수 있습니다(대부분의 OS와 스레드는 스택용으로 메모리의 특정 부분을 예약합니다).사전에 할당된 스택 공간에서 놀고 있는 한 모든 것이 크래시되지 않습니다(작업이라고 말한 것은 아닙니다).
마지막 줄에서는 스택에 할당된 메모리 부분을 넘어서 액세스합니다.그 결과 프로세스에 할당되지 않았거나 읽기 전용으로 할당되어 있는 메모리의 일부로 인덱싱됩니다.OS는 이를 인식하고 seg 장애를 프로세스로 전송합니다.
이것이 경계 체크에 있어서 C/C++가 매우 위험한 이유 중 하나입니다.
segfault는 인덱스가 경계를 벗어났음을 알려주는 C 프로그램의 의도된 액션이 아닙니다.오히려, 그것은 정의되지 않은 행동의 의도하지 않은 결과입니다.
C 및 C++에서는 다음과 같은 어레이를 선언하면
type name[size];
인덱스가 있는 요소에만 액세스할 수 있습니다.0
까지size-1
이 범위를 벗어나면 정의되지 않은 동작이 발생합니다.인덱스가 범위에 가까웠다면 프로그램의 메모리를 읽을 수 있을 것입니다.인덱스가 대부분 범위를 벗어나면 운영 체제에 의해 프로그램이 중지될 수 있습니다.하지만, 무슨 일이든 일어날 수 있다는 건들면 안 돼
왜 C는 그것을 허락합니까?음, C와 C++의 기본적인 요점은 퍼포먼스가 비싸면 기능을 제공하지 않는 것입니다.C와 C++는 고성능 크리티컬 시스템에 오랫동안 사용되어 왔습니다.C는 커널 및 프로그램의 구현 언어로 사용되어 왔습니다.여기서 어레이 경계를 벗어난 액세스는 메모리 내의 인접한 오브젝트에 빠르게 액세스 할 수 있습니다.컴파일러에게 이것을 금지시켜도 소용없다.
왜 그것에 대해 경고하지 않는 거죠?경고 수준을 높게 설정하고 컴파일러의 자비를 바랄 수 있습니다.이를 구현품질(QoI)이라고 합니다.일부 컴파일러가 오픈 동작(예: 정의되지 않은 동작)을 사용하여 좋은 일을 하는 경우, 그 점에서 구현 품질이 우수합니다.
[js@HOST2 cpp]$ gcc -Wall -O2 main.c
main.c: In function 'main':
main.c:3: warning: array subscript is above array bounds
[js@HOST2 cpp]$
어레이에 액세스 할 수 있는 범위를 넘은 시점에서 하드 디스크를 포맷하는 경우는(합법적으로는), 실장의 품질이 저하됩니다.나는 ANSI C Regonics 문서에서 그것에 대해 읽는 것을 즐겼다.
C 철학은 항상 프로그래머를 신뢰하는 것이다.또, 경계를 체크하지 않는 것으로써, 프로그램을 고속으로 실행할 수 있습니다.
litb에서 설명한 바와 같이 컴파일러에 따라서는 컴파일 시에 Out-Bounds 어레이 접근을 검출할 수 있습니다.그러나 컴파일 시 경계 확인으로 모든 것을 파악할 수 있는 것은 아닙니다.
int a[10];
int i = some_complicated_function();
printf("%d\n", a[i]);
이를 검출하려면 런타임체크를 사용해야 하며 퍼포먼스에 영향을 미치기 때문에 C에서는 사용하지 않도록 합니다.컴파일 시 어레이 크기(size of (a))를 알고 있어도 런타임 체크를 삽입하지 않고는 보호할 수 없습니다.
질문이나 코멘트를 이해하고 있기 때문에, 메모리 액세스의 범위외에서 나쁜 일이 발생하는 것은 이해할 수 있지만, 왜 특정 컴파일러가 경고를 하지 않았는지 궁금할 것입니다.
컴파일러는 경고할 수 있으며, 많은 컴파일러는 경고 수준이 가장 높습니다.그러나 이 표준은 사용자가 모든 종류의 장치에 대해 컴파일러를 실행할 수 있도록 하고 모든 종류의 기능을 갖춘 컴파일러를 실행할 수 있도록 하기 위해 작성되었습니다.따라서 이 표준에는 필요한 최소한의 컴파일러가 필요하지만 사람들이 유용한 작업을 수행할 수 있도록 보장합니다.
표준에서는 특정 코딩 스타일이 진단을 생성하도록 요구하는 경우가 몇 번 있습니다.표준이 진단을 필요로 하지 않는 경우가 몇 가지 있습니다.진단이 필요한 경우에도 표준에서 정확한 문구가 무엇인지 알 수 없습니다.
하지만 당신은 완전히 추운 곳에 있지 않아요.컴파일러가 경고하지 않으면 Lint가 경고할 수 있습니다.또, 힙상의 어레이에 대해서, 그러한 문제를(실행시에) 검출하기 위한 툴이 다수 있습니다.그 중 가장 유명한 것은 Electric Fence(DUMA)입니다.하지만 전기 펜스조차도 모든 오버런 오류를 잡을 수 있다고 보장하지는 않습니다.
일반적으로 프로세스가 소유하고 있지 않은 메모리에 액세스하려고 하는 경우에만 분할 오류가 발생합니다.
이 경우 보고 있는 것은a[11]
(그리고a[10]
그건 그렇고)는 프로세스가 소유하고 있지만 해당 프로세스에 속하지 않는 메모리입니다.a[]
어레이를 설정합니다.a[25000]
너무 멀다a[]
아마 전혀 기억이 나지 않을 거예요.
변화하는a[11]
는 다른 변수(또는 함수가 돌아왔을 때 다른 세그멘테이션 장애를 일으킬 수 있는 스택프레임)에 사일런트하게 영향을 미치기 때문에 훨씬 더 교묘합니다.
C는 안 해OS의 가상 메모리 서브시스템은 다음과 같습니다.
경계를 약간 벗어난 경우 프로그램(이 경우 스택콜 스택)에 할당되어 있는 메모리의 수신처를 지정합니다.사용하시는 프로그램에 할당되지 않은 메모리를 사용하고 있는 경우 OS에 의해 세그멘테이션 장애가 발생하고 있습니다.
시스템에 따라서는 OS가 강제하는 「쓰기 가능한」메모리의 개념도 있습니다.또, 자신이 소유하고 있지만, 기입할 수 없는 것이 마크 되어 있는 메모리에 쓰려고 하고 있는 경우도 있습니다.
다른 사람의 말을 덧붙이자면, 이러한 경우 단순히 프로그램이 크래쉬 하는 것만으로는 신뢰할 수 없습니다.메모리 장소에 액세스하려고 하면, 「어레이의 번즈」를 넘어서는 일이 일어날지는 알 수 없습니다.마치 다음과 같은 작업을 수행하는 것과 같습니다.
int *p;
p = 135;
*p = 14;
그건 그냥 무작위일 뿐이야. 이게 먹힐지도 몰라.아닐 수도 있어요.하지 마세요.이러한 문제를 방지하기 위한 코드입니다.
이것은 C의 문제가 아니라 OS의 문제입니다.프로그램에는 특정 메모리 공간이 부여되어 있으며, 그 안에서 수행하는 모든 작업은 문제 없습니다.분할 장애는 프로세스 공간 밖에서 메모리에 액세스하는 경우에만 발생합니다.
모든 운영체제가 각 프로세서에 별도의 주소 공간을 가지고 있는 것은 아닙니다.이 경우 경고 없이 다른 프로세스 또는 운영체제 상태를 손상시킬 수 있습니다.
JaredPar가 말했듯이 C/C++가 항상 범위 체크를 실행하는 것은 아닙니다.프로그램이 할당된 배열 이외의 메모리 위치에 액세스하면 프로그램이 크래쉬하거나 스택 상의 다른 변수에 액세스하기 때문에 크래쉬하지 않을 수 있습니다.
C의 연산자 크기에 대한 질문: size of(array)/size(array[0])를 사용하여 어레이 크기를 확실하게 결정할 수 있지만 컴파일러가 범위 체크를 실행하는 것은 아닙니다.
제 연구에 따르면 C/C++ 개발자들은 사용하지 않는 것에 대해 돈을 지불해서는 안 된다고 믿고 있으며 프로그래머들이 자신들이 무엇을 하고 있는지 알고 있다고 믿고 있습니다.(이것에 대해서는, 다음의 회답변으로 해 주세요.어레이에 액세스해도 에러는 발생하지 않습니다.이유는 무엇입니까?
C 대신 C++를 사용할 수 있다면 벡터를 사용할 수 있을까요?퍼포먼스가 필요한 경우(범위 체크 없음), 또는 보다 바람직한 경우 vector.at()를 사용할 수 있습니다(퍼포먼스를 희생하여 범위 체크 기능을 갖추고 있습니다).벡터가 가득 차도 자동으로 용량이 증가하지는 않습니다.안전하려면 push_back()을 사용합니다.이것에 의해, 필요에 따라서 용량이 자동적으로 증가합니다.
벡터에 대한 자세한 내용은http://http://www.cplusplus.com/reference/vector/vector/
언급URL : https://stackoverflow.com/questions/671703/array-index-out-of-bound-behavior
'source' 카테고리의 다른 글
Google Chrome에서 인라인 Javascript로 중단점을 설정하는 방법은 무엇입니까? (0) | 2022.10.30 |
---|---|
Vuetify.js에서 추가 아이콘을 클릭하여 함수를 호출하는 방법 (0) | 2022.10.30 |
간단한 JSON 라이브러리를 사용하여 json 파일을 Java로 읽는 방법 (0) | 2022.10.30 |
maven의 유통관리 조직 전체를 지정하는 방법 (0) | 2022.10.29 |
응답 코드 220이 필요하지만 Larabel에서 메시지 ""과 함께 코드 ""가 수신되었습니다. (0) | 2022.10.29 |