범위를 벗어난 어레이에 액세스해도 오류가 발생하지 않습니다. 이유는 무엇입니까?
C++ 프로그램에서 다음과 같은 범위를 벗어나는 값을 할당합니다.
#include <iostream>
using namespace std;
int main()
{
int array[2];
array[0] = 1;
array[1] = 2;
array[3] = 3;
array[4] = 4;
cout << array[3] << endl;
cout << array[4] << endl;
return 0;
}
은 프그램인다니를 인쇄합니다.3
그리고.4
그것은 가능하지 않아야 합니다.나는 g++ 4.3.3을 사용하고 있습니다.
컴파일 및 실행 명령입니다.
$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4
만을 할당할 array[3000]=3000
분할 오류가 발생합니까?
gcc가 배열 경계를 확인하지 않으면 나중에 심각한 문제가 발생할 수 있으므로 프로그램이 올바른지 어떻게 확인할 수 있습니까?
위 코드를 다음으로 대체했습니다.
vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout << vint[5] << endl;
그리고 이것 또한 오류를 발생시키지 않습니다.
모든 C/C++ 프로그래머의 가장 친한 친구에 오신 것을 환영합니다.정의되지 않은 동작입니다.
다양한 이유로 언어 표준에 명시되지 않은 것이 많습니다.이것이 그들 중 하나입니다.
일반적으로 정의되지 않은 동작이 발생할 때마다 어떤 일이 발생할 수 있습니다.응용 프로그램이 충돌하거나 중지되거나 CD-ROM 드라이브를 꺼내거나 코에서 악마가 나올 수 있습니다.하드 드라이브를 포맷하거나 모든 포르노를 할머니에게 이메일로 보낼 수 있습니다.
만약 여러분이 정말 운이 없다면, 올바르게 작동하는 것처럼 보일 수도 있습니다.
이 언어는 배열의 범위 내에 있는 요소에 액세스할 경우 발생해야 하는 작업을 간단히 말합니다.범위를 벗어나면 어떤 일이 일어나는지는 정의되지 않은 채로 남아 있습니다.컴파일러에서 현재 작동하는 것처럼 보일 수 있지만 합법적인 C나 C++은 아니며 다음에 프로그램을 실행할 때에도 작동한다는 보장은 없습니다.또는 지금도 중요한 데이터를 덮어쓰지 않고, 앞으로 발생할 문제에 직면하지 않았을 뿐입니다.
경계 검사가 없는 이유에 대해서는 다음과 같은 몇 가지 측면이 있습니다.
- 배열은 C에서 남은 것입니다.C 배열은 여러분이 얻을 수 있는 것처럼 원시적입니다.연속된 주소를 가진 일련의 요소일 뿐입니다.단순히 원시 메모리를 노출하는 것이기 때문에 경계 검사는 없습니다.강력한 경계 확인 메커니즘을 구현하는 것은 C에서 거의 불가능했을 것입니다.
- C++에서는 클래스 유형에 대해 경계 검사가 가능합니다.하지만 배열은 여전히 일반적인 오래된 C 호환 배열입니다.그것은 수업이 아닙니다.또한 C++는 경계 검사를 이상적이지 않게 만드는 다른 규칙을 기반으로 합니다.C++ 지도 원칙은 "사용하지 않는 것은 지불하지 않는다"입니다.코드가 올바르면 경계 검사가 필요하지 않으며 런타임 경계 검사의 오버헤드에 대해 강제로 비용을 지불하지 않아야 합니다.
- C++는 다음과 같은 기능을 제공합니다.
std::vector
둘 다 허용하는 클래스 템플릿입니다.operator[]
효율적으로 설계되었습니다.언어 표준은 경계 검사를 수행할 필요가 없습니다(금지하지도 않음).벡터는 또한 다음을 갖습니다.at()
경계 검사를 수행하도록 보장되는 멤버 함수입니다.따라서 C++에서는 벡터를 사용하면 두 가지 장점을 모두 얻을 수 있습니다.경계 검사 없이 어레이와 같은 성능을 얻을 수 있으며, 필요할 때 경계 검사된 액세스를 사용할 수 있습니다.
옵션을 할 수 . g++ 명령어는 다음과 같습니다.-fstack-protector-all
.
예를 들어 다음과 같은 결과를 얻었습니다.
> g++ -o t -fstack-protector-all t.cc
> ./t
3
4
/bin/bash: line 1: 15450 Segmentation fault ./t
문제를 찾거나 해결하는 데 도움이 되지는 않지만, 적어도 세그먼트 결함은 무언가 잘못되었다는 것을 알려줄 것입니다.
g++는 배열 경계를 확인하지 않으며, 3,4로 무언가를 덮어쓸 수 있지만, 큰 숫자로 시도하면 충돌이 발생합니다.
사용되지 않는 스택의 일부를 덮어쓰는 것입니다. 스택에 할당된 공간의 끝에 도달할 때까지 계속할 수 있습니다. 그러면 스택이 충돌합니다.
편집: 이 문제를 해결할 방법이 없습니다. 정적 코드 분석기가 이러한 오류를 발견할 수도 있지만, 너무 단순합니다. 정적 분석기에서도 유사한(그러나 더 복잡한) 오류가 감지되지 않을 수 있습니다.
제가 아는 한 그것은 정의되지 않은 행동입니다.그것으로 더 큰 프로그램을 실행하면 도중에 어딘가에서 충돌할 것입니다.경계 검사는 원시 배열(또는 짝수 std::vector)의 일부가 아닙니다.
를 std:와 함께 합니다.std::vector::iterator
대신 걱정하지 않으셔도 됩니다.
편집:
그냥 재미로 이것을 실행하고 충돌이 발생할 때까지 얼마나 걸리는지 확인해 보십시오.
int main()
{
int arr[1];
for (int i = 0; i != 100000; i++)
{
arr[i] = i;
}
return 0; //will be lucky to ever reach this
}
편집 2:
그거 뛰지 마.
편집 3:
다음은 어레이와 포인터와의 관계에 대한 간단한 설명입니다.
배열 인덱싱을 사용할 때는 자동으로 참조되지 않는 위장된 포인터("참조"라고 함)를 사용하게 됩니다.따라서 *(array+1) 대신 Array[1]가 해당 인덱스의 값을 자동으로 반환합니다.
배열에 대한 포인터가 있는 경우 다음과 같습니다.
int arr[5];
int *ptr = arr;
그러면 두 번째 선언의 "배열"은 실제로 첫 번째 배열에 대한 포인터로 쇠퇴합니다.이 동작은 다음과 같습니다.
int *ptr = &arr[0];
당신이 할당한 것 이상으로 접근하려고 할 때, 당신은 정말로 다른 메모리에 대한 포인터를 사용하고 있습니다(C++는 불평하지 않을 것입니다).위의 프로그램 예를 들어보면, 이는 다음과 같습니다.
int main()
{
int arr[1];
int *ptr = arr;
for (int i = 0; i != 100000; i++, ptr++)
{
*ptr++ = i;
}
return 0; //will be lucky to ever reach this
}
프로그래밍에서는 다른 프로그램, 특히 운영 체제와 통신해야 하는 경우가 많기 때문에 컴파일러는 불평하지 않을 것입니다.이것은 포인터로 꽤 많이 수행됩니다.
Valgrind를 통해 실행하면 오류가 나타날 수 있습니다.
Falaina가 지적했듯이, Valgrind는 스택 손상의 많은 인스턴스를 감지하지 못합니다.방금 valgrind 아래의 샘플을 시도해 봤는데, 실제로 오류가 전혀 발생하지 않았습니다.그러나 Valgrind는 많은 다른 유형의 메모리 문제를 찾는 데 도움이 될 수 있습니다. --stack-check 옵션을 포함하도록 bulid를 수정하지 않는 한 이 경우에는 특별히 유용하지 않습니다.샘플을 다음과 같이 빌드하고 실행하는 경우
g++ --stack-check -W -Wall errorRange.cpp -o errorRange
valgrind ./errorRange
valgrind가 오류를 보고합니다.
힌트
범위 오류 검사가 포함된 빠른 제약 조건 크기 배열을 사용하려면 boost::array를 사용해 보십시오. (std::tr1:array from<tr1/array>
다음 C++ 사양에서는 표준 컨테이너가 될 것입니다.std::vector보다 int 어레이[]와 마찬가지로 힙 또는 내부 클래스 인스턴스에 메모리를 예약합니다.
다음은 간단한 샘플 코드입니다.
#include <iostream>
#include <boost/array.hpp>
int main()
{
boost::array<int,2> array;
array.at(0) = 1; // checking index is inside range
array[1] = 2; // no error check, as fast as int array[2];
try
{
// index is inside range
std::cout << "array.at(0) = " << array.at(0) << std::endl;
// index is outside range, throwing exception
std::cout << "array.at(2) = " << array.at(2) << std::endl;
// never comes here
std::cout << "array.at(1) = " << array.at(1) << std::endl;
}
catch(const std::out_of_range& r)
{
std::cout << "Something goes wrong: " << r.what() << std::endl;
}
return 0;
}
이 프로그램은 다음을 인쇄합니다.
array.at(0) = 1
Something goes wrong: array<>: index out of range
C 또는 C++는 배열 액세스의 경계를 확인하지 않습니다.
스택에 어레이를 할당하고 있습니다.다을통배인을 통해 배열 array[3]
합니다.(array + 3)
여기서 array는 &array[0]에 대한 포인터입니다.이로 인해 정의되지 않은 동작이 발생합니다.
C에서 때때로 이것을 잡는 한 가지 방법은 부목과 같은 정적 검사기를 사용하는 것입니다.실행하는 경우:
splint +bounds array.c
위에,
int main(void)
{
int array[1];
array[1] = 1;
return 0;
}
그러면 다음과 같은 경고가 표시됩니다.
array.c: (in function main) array.c:5:9: 가능성이 높은 저장소: array[1] 제약 조건을 확인할 수 없음: 0 > = 1 전제 조건을 충족하는 데 필요함: maxSet(array @ array.c:5:9) > = 1 메모리 쓰기가 할당된 버퍼를 초과하는 주소에 쓸 수 있습니다.
스택을 덮어쓰는 것은 확실하지만 프로그램은 이 효과가 눈에 띄지 않을 정도로 간단합니다.
gcc의 일부인 libstdc++에는 오류 확인을 위한 특별한 디버그 모드가 있습니다.컴파일러 플래그에 의해 활성화됩니다.-D_GLIBCXX_DEBUG
다른 것들 중에서 그것은 경계 확인을 합니다.std::vector
수행을 희생하여이것은 최근 버전의 gcc를 사용한 온라인 데모입니다.
따라서 실제로 libstdc++ 디버그 모드로 경계 검사를 수행할 수 있지만 일반 libstdc++ 모드에 비해 현저한 성능이 들기 때문에 테스트 시에만 수행해야 합니다.
정의되지 않은 동작이 사용자에게 유리하게 작동합니다.당신이 기억하고 있는 것이 무엇이든 간에 분명히 중요한 것은 아무것도 가지고 있지 않습니다.C와 C++는 배열에 대한 경계 검사를 수행하지 않으므로 컴파일이나 실행 시간에 이러한 것이 잡히지 않습니다.
C에 'array[index]'를 쓰면 기계 명령어로 번역됩니다.
번역은 다음과 같습니다.
- 'array의 주소를 가져옵니다.
- '구성된 개체 배열 형식의 크기 가져오기'
- '색인 기준으로 형식 크기 표시'
- '어레이 주소에 결과 추가'
- '결과 주소에 있는 내용 읽기'
결과는 어레이의 일부일 수도 있고 아닐 수도 있는 것을 해결합니다.기계 명령의 맹렬한 속도 대신 컴퓨터의 안전망을 잃어버리는 것입니다.당신이 꼼꼼하고 신중하다면 문제가 되지 않습니다.만약 당신이 엉성하거나 실수를 하면 당신은 화상을 입을 것입니다.잘못된 명령을 생성하여 예외가 발생할 수도 있고 그렇지 않을 수도 있습니다.
할 때int array[2]
에 대한 할당됩니다. 식별자 2는 2를 의미합니다.array
단순히 그 공간의 시작을 가리킵니다. 액스할때에 액세스할 수 .array[3]
그리고.array[4]
는 배열이 위해 시킵니다. 그런 다 컴 음 러 일 는 증 충 켜 시 배 가 길 어 있 에 디 이 가 니 값 보 다 오 는 킵 시 십 접 해 같 근 것 다 음 과 에 리 은 지 들 그 를 열 면 소 주 이 파히분 그 단 히 순 ▁something ▁like ▁accessing ▁the ▁simplyarray[42]
먼저 초기화하지 않으면 해당 위치에서 이미 메모리에 있는 값을 얻을 수 있습니다.
편집:
포인터/포인터에 대한 자세한 정보: http://home.netcom.com/ ~tjensen/ptr/px.htm
로컬 변수는 스택에 할당되므로 자신의 스택에서 경계를 벗어나는 것은 다른 로컬 변수를 덮어쓸 수 있습니다. 너무 많이 사용하여 스택 크기를 초과하지 않는 한 말입니다.함수에 선언된 다른 변수가 없으므로 부작용이 발생하지 않습니다.첫 번째 변수/어레이 직후에 다른 변수/어레이를 선언하고 이 변수/어레이에서 어떤 일이 발생하는지 확인합니다.
를 들어 내자주보또를실아사형유좋접 NULL은은요근소던법었용되다것요가니소생주입된성는는하입왔고제로를또▁a▁that)를 주입하는 입니다.uint THIS_IS_INFINITY = 82862863263;
배열의 끝에 있습니다.
그런 다음 루프 상태 점검에서TYPE *pagesWords
포인터 배열의 일종입니다.
int pagesWordsLength = sizeof(pagesWords) / sizeof(pagesWords[0]);
realloc (pagesWords, sizeof(pagesWords[0]) * (pagesWordsLength + 1);
pagesWords[pagesWordsLength] = MY_NULL;
for (uint i = 0; i < 1000; i++)
{
if (pagesWords[i] == MY_NULL)
{
break;
}
}
어레가다채워이면솔말루을하않지습으로 채워지면 이 솔루션은 단어가 되지 않습니다.struct
types.
지금 질문에서 언급한 것처럼 std:::vector::at를 사용하면 문제가 해결되고 액세스하기 전에 바인딩을 확인할 수 있습니다.
첫 번째 코드로 스택에 위치한 고정 크기 배열이 필요한 경우 C++11 새 컨테이너 std::array를 사용합니다. 벡터에는 std::array:at 함수가 있습니다.실제로 함수는 모든 표준 컨테이너에 존재하며 여기서 연산자 []는 std::bitset::test라고 하는 std::bitset을 제외하고 :(deque, map, unordered_map) 정의됩니다.
프로그램을 약간 변경하는 경우:
#include <iostream>
using namespace std;
int main()
{
int array[2];
INT NOTHING;
CHAR FOO[4];
STRCPY(FOO, "BAR");
array[0] = 1;
array[1] = 2;
array[3] = 3;
array[4] = 4;
cout << array[3] << endl;
cout << array[4] << endl;
COUT << FOO << ENDL;
return 0;
}
(자본금의 변화 -- 만약 당신이 이것을 시도할 것이라면 그것들을 소문자로 넣으십시오.)
변수 foo가 폐기된 것을 볼 수 있습니다.코드는 존재하지 않는 배열[3] 및 배열[4]에 값을 저장하고 이 값을 올바르게 검색할 수 있지만 실제 사용되는 저장소는 foo입니다.
따라서 원래의 예에서 배열의 경계를 초과하여 "탈락"할 수 있지만, 다른 곳에서 손상을 입히는 대가를 치러야 합니다. 이 손상은 진단하기 매우 어려울 수 있습니다.
자동 경계 검사가 없는 이유에 대해서는 올바르게 작성된 프로그램에는 필요하지 않습니다.이 작업이 완료되면 런타임 경계 검사를 수행할 이유가 없으며 이렇게 하면 프로그램 속도가 느려집니다.설계 및 코딩 중에 이 모든 것을 파악하는 것이 가장 좋습니다.
C++는 C를 기반으로 하며, C는 가능한 어셈블리 언어에 가깝게 설계되었습니다.
int 어레이[2]를 선언할 때, 각각 4바이트(32비트 프로그램)로 구성된 2개의 메모리 공간을 예약합니다.코드에 array[4]를 입력하면 여전히 유효한 호출에 해당하지만 실행 시에만 처리되지 않은 예외가 발생합니다.C++는 수동 메모리 관리를 사용합니다.이것은 실제로 해킹 프로그램에 사용된 보안 결함입니다.
이해하는 데 도움이 될 수 있습니다.
int * 어떤 지적;
일부 지점[0]=일부 지점[5];
동작은 시스템에 따라 다를 수 있습니다.일반적으로 값이 0이거나 가비지 값인 경우도 있으며 범위를 벗어날 수 있는 여유가 있습니다.자세한 내용은 OS에 사용되는 메모리 할당 메커니즘을 통해 확인할 수 있습니다.또한 c/c++와 같은 프로그래밍 언어를 사용하는 경우 배열과 같은 일부 컨테이너를 사용할 때 경계를 확인하지 않습니다.따라서 OS가 표면 아래에서 무엇을 했는지 모르기 때문에 "정의되지 않은 이벤트"를 만나게 됩니다.그러나 프로그래밍 언어 자바처럼 경계를 확인할 것입니다.만약 당신이 경계를 벗어나면, 당신은 예외를 얻게 될 것입니다.
언급URL : https://stackoverflow.com/questions/1239938/accessing-an-array-out-of-bounds-gives-no-error-why
'programing' 카테고리의 다른 글
iPhone: 마지막 화면 터치 이후 사용자 비활성/유휴시간 (0) | 2023.05.26 |
---|---|
시스템 추가 방법.창문들.프로젝트에 대한 상호 작용? (0) | 2023.05.26 |
셀 변경 시 Excel 매크로 자동 실행 (0) | 2023.05.26 |
컴파일된 실행 파일에 DLL 포함 (0) | 2023.05.26 |
어떤 리눅스 셸 명령이 문자열의 일부를 반환합니까? (0) | 2023.05.26 |