C 표준 보증 버퍼는 Null 터미네이터를 지나치지 않습니까?
표준 라이브러리의 많은 문자열 함수에 버퍼가 제공되는 다양한 경우에 버퍼가 null 터미네이터를 넘어 수정되지 않는다는 것이 보장됩니까?예:
char buffer[17] = "abcdefghijklmnop";
sscanf("123", "%16s", buffer);
아이즈buffer
이제 동등하게 요구되는"123\0efghijklmnop"
?
다른 예:
char buffer[10];
fgets(buffer, 10, fp);
읽기 줄이 3자만 길면 6번째 문자가 fgets가 호출되기 전과 동일하다고 확신할 수 있습니까?
C99 초안 표준은 이러한 경우에 어떤 일이 발생해야 하는지 명시적으로 명시하지 않지만, 여러 변형을 고려함으로써 모든 경우에 규격을 충족하도록 특정 방식으로 작동해야 한다는 것을 보여줄 수 있습니다.
기준은 다음과 같습니다.
%s - 공백이 아닌 일련의 문자와 일치합니다.252)
길이 한정자가 없는 경우 해당 인수는 시퀀스를 수용할 수 있을 정도로 큰 문자 배열의 초기 요소에 대한 포인터여야 하며 종료 null 문자는 자동으로 추가됩니다.
여기 표준을 충족하기 위해 제안하는 방식으로 작동해야 한다는 것을 보여주는 두 가지 예가 있습니다.
예 A:
char buffer[4] = "abcd";
char buffer2[10]; // Note the this could be placed at what would be buffer+4
sscanf("123 4", "%s %s", buffer, buffer2);
// Result is buffer = "123\0"
// buffer2 = "4\0"
예 B:
char buffer[17] = "abcdefghijklmnop";
char* buffer2 = &buffer[4];
sscanf("123 4", "%s %s", buffer, buffer2);
// Result is buffer = "123\04\0"
sscanf의 인터페이스는 이러한 인터페이스가 서로 다르다는 것을 실제로 알 수 있는 충분한 정보를 제공하지 않습니다.따라서 예제 B가 제대로 작동하려면 예제 A의 null 문자 뒤에 있는 바이트와 혼동하지 않아야 합니다.이것은 이 약간의 사양에 따라 두 경우 모두 작동해야 하기 때문입니다.
따라서 암묵적으로 사양상 말씀하신 대로 작동해야 합니다.
다른 함수에 대해서도 유사한 인수가 배치될 수 있지만, 이 예에서 아이디어를 볼 수 있다고 생각합니다.
참고: "%16s"와 같은 형식으로 크기 제한을 제공하면 동작이 변경될 수 있습니다.사양에 따르면 데이터를 버퍼에 쓰기 전에 sscanf가 버퍼를 한계치까지 제로로 만드는 것이 기능적으로 허용될 것입니다.실제로, 대부분의 구현은 성능을 선택합니다. 즉, 나머지는 의미입니다.
규격의 목적이 이러한 종류의 영점 조정을 수행하는 경우 일반적으로 명시적으로 지정됩니다.strncpy가 그 예입니다.문자열의 길이가 지정된 최대 버퍼 길이보다 작으면 나머지 공간을 null 문자로 채웁니다.동일한 "문자열" 함수가 종료되지 않은 문자열을 반환할 수 있다는 사실은 사람들이 자신의 버전을 롤링하는 가장 일반적인 함수 중 하나가 됩니다.
기우에 관한 한, 비슷한 상황이 발생할 수 있습니다.유일한 문제는 사양에 아무것도 읽지 않으면 버퍼가 그대로 유지된다고 명시되어 있다는 것입니다.허용 가능한 기능 구현은 버퍼를 0으로 만들기 전에 읽을 바이트가 하나 이상 있는지 확인하여 이 문제를 피할 수 있습니다.
버퍼의 각 개별 바이트는 개체입니다. 명의일제는고하외부의 ,sscanf
또는fgets
해당 바이트를 수정하는 것을 언급하거나, 심지어는 해당 바이트의 값이 변경될 수 있음을 암시합니다. 예를 들어 해당 바이트의 값이 지정되지 않음을 명시하면 일반 규칙이 적용됩니다. (내 것을 선택합니다.)
6.2.4 물품의 보관기간
2 [...] 개체가 존재하고 주소가 일정하며 수명 동안 마지막으로 저장된 값을 유지합니다. [...]
바로 이 원칙이 다음과 같은 것을 보장합니다.
#include <stdio.h>
int a = 1;
int main() {
printf ("%d\n", a);
printf ("%d\n", a);
}
1번 인쇄를 두 번 시도합니다.그럼에도 불구하고.a
글벌로,printf
수 , 에 대한 은 에 액세스할 수 있습니다.printf
수정하지 않는 것에 대해 언급하지 않음a
.
에 대한 설명도 없습니다.fgets
의그도아닌의 .sscanf
에서는 실제로 쓰기로 되어 있던 바이트(읽기 오류의 경우 제외)를 지나 버퍼를 수정하는 것에 대해 언급합니다. 따라서 해당 바이트는 수정되지 않습니다.
이에 대한 기준은 다소 모호하지만, 그에 대한 합리적인 해석은 다음과 같습니다: 예, 버퍼에 읽기+null보다 더 많은 바이트를 쓰는 것은 허용되지 않습니다.반면에, 본문에 대한 더 엄격한 읽기/해석은 대답이 "아니오", "보증이 없습니다"라는 결론을 내릴 수 있습니다.다음은 공개적으로 사용 가능한 초안입니다.fgets
.
char *fgets(char * restrict s, int n, FILE * restrict stream)
;그
fgets
로 지정된 .n
가 가리키는 시냇물에서.stream
가 가리키는 배열 안으로.s
새 줄 문자(유지됨) 또는 파일 끝 이후에는 추가 문자를 읽지 않습니다.null 문자는 배열에 마지막으로 읽은 문자 직후에 기록됩니다.그
fgets
는 함수반을 반환합니다.s
않은 됩니다.파일 끝이 발견되고 배열에 문자를 읽지 않은 경우 배열 내용은 변경되지 않고 null 포인터가 반환됩니다.작업 중에 읽기 오류가 발생하면 어레이 내용이 결정되지 않고 Null 포인터가 반환됩니다.
입력에서 읽어야 하는 양에 대한 보장이 있습니다. 즉, 새 줄이나 EOF에서 읽는 것을 멈추고 다음 이상 읽지 않습니다.n-1
버퍼에 쓸 수 있는 양에 대해 명시적으로 언급된 것은 없지만, 일반적으로 알고 있는 것입니다.fgets
의n
매개 변수는 버퍼 오버플로를 방지하는 데 사용됩니다.표준이 읽기라는 모호한 용어를 사용한다는 것이 약간 이상한데, 이는 반드시 다음을 의미하지는 않을 수 있습니다.gets
버퍼에 쓸 수 없는 시간:n
바이트(사용하는 용어를 트릭하고 싶은 경우). 두 "" 용어가 하세요: "읽기"는 "읽기"입니다.n
-limit 및 EOF/new line limit.그래서 만약 당신이 해석한다면.n
제한으로 "수 . 즉, 보다 짧을 때 - "read"는 "read", [read"는 "read"입니다.
반면에, 만약 당신이 "읽기"(="쓰기")와 단지 "읽기"라는 문구의 사용을 구별한다면, 당신은 위원회의 텍스트를 같은 방식으로 읽을 수 없습니다.""write to")보다 더 배열을 " to합니다.n
바이트입니다. 그러나 입력 문자열이 새 줄이나 EOF에 의해 더 빨리 종료되면 나머지(입력된 부분)만 "읽기"되지 않지만, 이 의미가 "읽기"(="쓰기")되지 않는지 여부는 이 엄격한 읽기에서 버퍼가 명확하지 않습니다.중요한 문제는 키워드가 "into"인데, 이는 생략되어 있기 때문에 다음 수정된 인용문에서 괄호 안에 제가 준 완성도가 의도된 해석인지 여부가 문제입니다.
새 줄 문자(유지됨) 뒤 또는 파일 끝 이후에는 추가 문자가 [어레이에] 읽히지 않습니다.
솔직히 공식으로 명시된 단일 사후 조건(이 경우 상당히 짧음)이 제가 인용한 요약보다 훨씬 더 도움이 되었을 것입니다.
나는 그들의 글을 분석하는 것을 귀찮아 할 수 없습니다.*scanf
그 다른 할 때, 저는 그것이 더입니다; 의 글은 가족, 면나일고다는어나모른때것려할든그을훨것기이씬더그록복것의들의때다이잡이할라고심하문기들서에들능기왜는그냐하,▁family;▁write▁their▁for'▁more▁i▁functions기.fscanf
5페이지 정도 됩니다.하지만 저는 비슷한 논리가 적용된다고 생각합니다.
버퍼가 null 터미네이터를 넘어 수정되지 않는다는 것이 보장됩니까?
아니요, 보장은 없습니다.
이제 버퍼가 "123\0efghijklmnop"과 동일해야 합니까?
변수를 입니다. 하지만 문자열 관련 함수에 올바른 매개 변수를 사용했기 때문입니다.길이를 를 버길이엉만경드수입우다력니합를식어는에 합니다.sscanf
그런 다음 프로그램을 컴파일합니다.그러나 런타임 중에는 실패할 가능성이 높습니다.
읽기 줄이 3자만 길면 6번째 문자가 fgets가 호출되기 전과 동일하다고 확신할 수 있습니까?
한 번. 한 번.fgets()
3자 입력 문자열을 가지고 있는 것으로 표시됩니다. 입력은 제공된 버퍼에 저장되며 제공된 공간의 재설정은 전혀 신경 쓰지 않습니다.
이제 버퍼가 "123\0efghijklmnop"과 동일해야 합니까?
여기서buffer
로구니다됩성으로 구성되어 .123
NUL로 끝나는 것이 보장되는 문자열입니다.
, 배열 입니다.buffer
는 되지 " " " " " " " " 을 .buffer
기껏해야 을 가질 수 있을 뿐16
언제든지 읽을 수 있는 char 요소.는 한 , 를 쓰느냐에 .buffer
가져갈 수 있습니다.
예:
char buffer[4096] = "abc";`
실제로 아래와 같은 일을 합니다.
memcpy(buffer, "abc", sizeof("abc"));
memset(&buffer[sizeof("abc")], 0, sizeof(buffer)-sizeof("abc"));
표준은 문자 배열의 일부가 초기화되면 메모리 경계를 준수할 때까지 언제든지 구성되는 것이 전부라고 주장합니다.
표준에서 보장하는 것이 없기 때문에 기능이sscanf
그리고.fgets
질문(및 의 사용)에 표시된 대로 (버퍼 크기와 관련하여) 사용할 것을 권장합니다.fgets
에 비해 바람직한 것으로 간주됩니다.gets
).
그러나 일부 표준 함수는 작업에서 null-terminator를 사용합니다.strlen
(하지만 문자열 수정에 대해 물어보시는 것 같습니다.)
편집:
예를 들어,
fgets(buffer, 10, fp);
10분의 1 이후의 문자(내용 및 길이)가 보장됩니다.buffer
에 의해 고려되지 않음fgets
)
EDIT2:
게다가, 사용할 수 있는 경우fgets
을 명심하시오'\n'
버퍼에 저장됩니다. 예:
"123\n\0fghijklmnop"
예상외로
"123\0efghijklmnop"
사용 중인 기능(그리고 그 구현 정도는 덜함)에 따라 다릅니다. sscanf
첫 번째 공백이 아닌 문자가 발견되면 쓰기를 시작하고 첫 번째 공백 문자가 나타날 때까지 쓰기를 계속합니다. 여기서 마무리를 추가합니다.0
그리고 돌아옵니다.하지만 같은 기능은strncpy
버퍼의 나머지 부분을 0으로 만듭니다.
그러나 C 표준에는 이러한 기능이 어떻게 작동하는지를 규정하는 것이 없습니다.
언급URL : https://stackoverflow.com/questions/28712548/does-the-c-standard-guarantee-buffers-are-not-touched-past-their-null-terminator
'IT' 카테고리의 다른 글
vuex 스토어의 라우터 ID에 액세스하고 Axios get 메서드에 추가하려면 어떻게 해야 합니까? (0) | 2023.07.02 |
---|---|
Hg: git's rebase와 같은 rebase를 하는 방법 (0) | 2023.07.02 |
Python panda: 데이터 프레임 행을 한 행씩 채우기 (0) | 2023.07.02 |
프로젝트에서 엔티티 프레임워크의 최신 버전을 참조하고 있습니다... - 오류 (0) | 2023.07.02 |
Heroku에서 Node.js 앱을 만들 때 Git에 "node_modules" 폴더를 체크인해야 합니까? (0) | 2023.07.02 |