태그 보관물: strings

strings

C 문자열 리터럴이 읽기 전용 인 이유는 무엇입니까? 수있는 또 다른

읽기 전용 문자열 리터럴의 장점은 무엇입니까? (-ies / -ied)

  1. 발에 몸을 쏠 수있는 또 다른 방법

    char *foo = "bar";
    foo[0] = 'd'; /* SEGFAULT */
  2. 한 줄에 단어의 읽기-쓰기 배열을 우아하게 초기화 할 수 없음 :

    char *foo[] = { "bar", "baz", "running out of traditional placeholder names" };
    foo[1][2] = 'n'; /* SEGFAULT */ 
  3. 언어 자체를 복잡하게합니다.

    char *foo = "bar";
    char var[] = "baz";
    some_func(foo); /* VERY DANGEROUS! */
    some_func(var); /* LESS DANGEROUS! */

메모리를 절약 하시겠습니까?
RAM이 부족했을 때 컴파일러는 비슷한 문자열을 병합하여 메모리 사용을 최적화하려고 시도했던 그 어느 시점 (지금 소스를 찾을 수 없음)을 읽었습니다.

예를 들어 “more”및 “regex”는 “moregex”가됩니다. 오늘날 디지털 블루 레이 품질 영화 시대에도 이것이 사실입니까? 임베디드 시스템은 여전히 ​​제한된 리소스 환경에서 작동하지만 여전히 사용 가능한 메모리 양이 크게 증가했다는 것을 알고 있습니다.

호환성 문제?
읽기 전용 메모리에 액세스하려고하는 레거시 프로그램이 충돌하거나 발견되지 않은 버그로 계속 될 것이라고 가정합니다. 따라서 레거시 프로그램은 문자열 리터럴에 액세스하려고하지 않아야하므로 문자열 리터럴에 쓰도록 허용해도 유효하고 해킹되지 않은 휴대용 레거시 프로그램 에는 영향을 미치지 않습니다 .

다른 이유가 있습니까? 내 추론이 잘못 되었습니까? 새로운 C 표준에서 읽기 / 쓰기 문자열 리터럴을 변경하거나 최소한 컴파일러에 옵션을 추가하는 것이 합리적입니까? 이전에이 문제가 고려 되었습니까, 아니면 내 “문제”가 너무 작고 중요하지 않은 사람입니까?



답변

역사적으로 (아마도 일부를 다시 작성하여), 그 반대였습니다. 1970 년대 초반 (아마도 PDP-11 ) 프로토 타입 배아 C (아마도 BCPL )를 실행하는 최초의 컴퓨터 에는 MMU메모리 보호 기능 이 없었습니다 (대부분의 오래된 IBM / 360 메인 프레임 에 존재 함 ). (리터럴 스트링 또는 기계 코드를 처리하는 것을 포함) 메모리의 모든 바이트가 잘못된 프로그램이 덮어 쓰기 할 수 있도록 (일부 변경 프로그램 상상 %/A의 printf와 (3) 형식 문자열). 따라서 리터럴 문자열과 상수를 쓸 수있었습니다.

1975 년 십대, I 메모리 보호없이 오래된 1960 년대 시대의 컴퓨터에서 파리의 팔레 드 라 메디 테라 박물관 코딩 : / 1620 IBM은 여러 수십 입력했다, 그래서 키보드를 통해 초기화 할 수 있습니다 – 어떤 단지 코어 메모리를했다 펀치 테이프의 초기 프로그램을 읽을 수있는 자릿수; CAB / 500 에는 자기 드럼 메모리가있었습니다. 드럼 근처의 기계식 스위치를 통해 일부 트랙을 쓰지 못하게 할 수 있습니다.

나중에 컴퓨터에는 메모리 보호 기능이있는 메모리 관리 장치 (MMU)가 있습니다. CPU가 어떤 종류의 메모리를 덮어 쓰지 못하게하는 장치가있었습니다. 따라서 일부 메모리 세그먼트, 특히 코드 세그먼트 (일명 .text세그먼트)는 읽기 전용이되었습니다 (디스크에서로드 한 운영 체제 제외). 컴파일러와 링커에서 리터럴 문자열을 해당 코드 세그먼트에 넣는 것이 자연스럽고 리터럴 문자열은 읽기 전용이되었습니다. 당신의 프로그램이 그것들을 덮어 쓰려고 시도했을 때, 그것은 정의되지 않은 행동 이었습니다. 가상 메모리에 읽기 전용 코드 세그먼트가 있으면 동일한 장점을 제공합니다 . 동일한 프로그램을 실행하는 여러 프로세스 가 동일한 RAM ( 실제 메모리)을 공유합니다.해당 코드 세그먼트에 대한 페이지) ( Linux의 mmap (2)MAP_SHARED대한 플래그 참조 ).

오늘날 저렴한 마이크로 컨트롤러 에는 읽기 전용 메모리 (예 : 플래시 또는 ROM)가 있으며 코드 (및 리터럴 문자열 및 기타 상수)를 유지합니다. 또한 태블릿, 랩톱 또는 데스크탑과 같은 실제 마이크로 프로세서에는 정교한 메모리 관리 장치와 가상 메모리페이징에 사용되는 캐시 장치가 있습니다. 의 코드 세그먼트 그래서 실행 프로그램 (예에서 ELF가 ) 메모리 읽기 전용, 공유, 그리고에 의해 실행 가능한 세그먼트 (로 매핑됩니다 의 mmap (2) 또는 에서 execve (2) 리눅스, BTW 당신을 지시 줄 수있는 신분증을정말로 원한다면 쓰기 가능한 코드 세그먼트를 얻으려면). 쓰기 또는 악용은 일반적으로 세그먼테이션 오류 입니다.

따라서 C 표준은 바로크 식입니다. 법적으로 (역사적 이유로 만) 리터럴 문자열은 const char[]배열이 아니라 char[]덮어 쓸 수 없는 배열입니다.

BTW, 현재 사용 가능한 언어 중 문자열 리터럴을 덮어 쓸 수있는 언어는 거의 없습니다 (역사적으로 쓸 수있는 리터럴 문자열이 최근 4.02에서 동작이 변경되어 현재 읽기 전용 문자열을 갖는 Ocaml조차도).

현재 C 컴파일러는 마지막 5 바이트 (종료 널 바이트 포함) 를 최적화하고 보유 "ions"하고 "expressions"공유 할 수 있습니다.

파일에 C 코드를 컴파일하려고 foo.c으로 gcc -O -fverbose-asm -S foo.c생성 된 어셈블러 파일 내부 및 모양을 foo.s하여 GCC

마지막 으로 C 의 의미론 은 충분히 복잡 하며 (캡처하려는 CompCert & Frama-C 에 대해 자세히 읽어보십시오 ) 쓰기 가능한 상수 리터럴 문자열을 추가하면 프로그램이 더 약하고 덜 안전 해지며 더 약해집니다. 정의 된 동작)으로 인해 향후 C 표준이 쓰기 가능한 리터럴 문자열을 허용 할 가능성은 거의 없습니다. 반대로 const char[]그들은 도덕적으로해야 할 것처럼 배열을 만들 것 입니다.

또한 여러 가지 이유로, 가변 데이터는 상수 데이터보다 컴퓨터 (캐시 일관성), 개발자가 코딩, 이해하기가 어렵습니다. 따라서 대부분의 데이터 (특히 리터럴 문자열)를 변경하지 않는 것이 좋습니다. 함수형 프로그래밍 패러다임 에 대해 자세히 알아보십시오 .

IBM / 7094의 이전 Fortran77 일에서 버그는 상수 를 변경할 수도 있습니다 .2를 참조하여 전달 된 인수를 수정 한 CALL FOO(1)경우 FOO구현이 다른 발생을 1로 2로 변경했을 수 있습니다. 나쁜 버그, 찾기가 매우 어렵습니다.


답변

컴파일러는 결합 할 수 "more""regex"는 후에 전자는 널 바이트를 가지고 있기 때문에 e반면 후자는있다 x, 그러나 많은 컴파일러는 완벽하게 일치하는 문자열 리터럴을 결합 것이고, 일부는 공통의 꼬리를 공유 문자열 리터럴 일치합니다. 따라서 문자열 리터럴을 변경하는 코드는 완전히 다른 목적으로 사용되지만 동일한 문자를 포함하는 다른 문자열 리터럴을 변경할 수 있습니다.

C의 발명 이전에 FORTRAN에서도 비슷한 문제가 발생했다. 인수는 항상 값이 아닌 주소로 전달되었다. 따라서 두 개의 숫자를 추가하는 루틴은 다음과 같습니다.

float sum(float *f1, float *f2) { return *f1 + *f2; }

상수 값 (예 : 4.0)을로 전달하려는 sum경우 컴파일러는 익명 변수를 만들어로 초기화합니다 4.0. 동일한 값이 여러 함수에 전달되면 컴파일러는 동일한 주소를 모든 함수에 전달합니다. 결과적으로 매개 변수 중 하나를 수정 한 함수에 부동 소수점 상수가 전달되면 프로그램의 다른 곳에서 해당 상수의 값이 변경되어 “변수가 없습니다. 상수가 없습니다.” ‘티”.


답변