이 문자열 반전 C 코드가 분할 오류를 일으키는 이유는 무엇입니까?[복제하다]
-
06-07-2019 - |
문제
이 질문에는 이미 답변이 있습니다.
문자열을 제자리에서 뒤집는 코드를 작성하려고 합니다(C 프로그래밍과 포인터 조작을 더 잘하려고 노력 중입니다). 그러나 오류가 발생하는 이유를 알 수 없습니다. 세그멘테이션 오류:
#include <string.h>
void reverse(char *s);
int main() {
char* s = "teststring";
reverse(s);
return 0;
}
void reverse(char *s) {
int i, j;
char temp;
for (i=0,j = (strlen(s)-1); i < j; i++, j--) {
temp = *(s+i); //line 1
*(s+i) = *(s+j); //line 2
*(s+j) = temp; //line 3
}
}
분할 오류를 일으키는 것은 라인 2와 3입니다.이 작업을 수행하는 더 좋은 방법이 있을 수 있다는 것을 이해하지만 구체적으로 무엇인지 알아보고 싶습니다. 내 코드에서 분할 오류가 발생하고 있습니다.
업데이트:요청하신 대로 호출 기능을 포함시켰습니다.
해결책
그 코드만으로는 말할 방법이 없습니다.아마도 유효하지 않은 메모리, 수정할 수 없는 메모리 또는 여기에서 처리하는 방식으로 처리할 수 없는 다른 종류의 메모리를 가리키는 포인터를 전달하고 있을 가능성이 높습니다.
함수를 어떻게 호출하나요?
추가됨:문자열 리터럴에 대한 포인터를 전달하고 있습니다.문자열 리터럴은 수정할 수 없습니다.문자열 리터럴은 되돌릴 수 없습니다.
대신 수정 가능한 문자열에 대한 포인터를 전달하세요.
char s[] = "teststring";
reverse(s);
이것은 이미 여기서 죽음에 대해 설명되었습니다. "teststring"
문자열 리터럴입니다.문자열 리터럴 자체는 수정할 수 없는 개체입니다.실제로 컴파일러는 이를 읽기 전용 메모리에 넣을 수 있습니다.그런 포인터를 초기화하면
char *s = "teststring";
포인터는 문자열 리터럴의 시작 부분을 직접 가리킵니다.무엇을 수정하려는 시도 s
일반적인 경우 실패로 간주된다는 점을 지적하고 있습니다.읽을 수는 있지만 쓸 수는 없습니다.이러한 이유로 포인터-상수 변수만 사용하여 문자열 리터럴을 가리키는 것이 좋습니다.
const char *s = "teststring";
하지만 당신이 선언할 때 s
~처럼
char s[] = "teststring";
완전히 독립적인 배열을 얻습니다. s
일반 수정 가능한 메모리에 위치합니다. 초기화됨 문자열 리터럴로.이는 독립적인 수정 가능한 배열을 의미합니다. s
초기값을 얻게 됩니다 복사됨 문자열 리터럴에서.그 후에는 당신의 s
배열과 문자열 리터럴은 완전히 독립적인 개체로 계속 존재합니다.리터럴은 여전히 수정할 수 없지만 s
배열은 수정 가능합니다.
기본적으로 후자의 선언은 기능적으로 다음과 동일합니다.
char s[11];
strcpy(s, "teststring");
다른 팁
여러 가지 이유로 코드가 분류 될 수 있습니다. 여기에 떠오르는 것들이 있습니다
- s는 null입니다
- s는 읽기 전용 메모리에서 고정 된 const 문자열을 가리 킵니다.
- s는 널 종료되지 않습니다
#2가 가장 가능성이 높다고 생각합니다. 반대의 통화 사이트를 보여줄 수 있습니까?
편집하다
샘플 #2를 기반으로하는 것이 확실히 답입니다. C/C ++의 문자열 문자는 수정할 수 없습니다. 적절한 유형은 실제로입니다 const char*
그리고 아닙니다 char*
. 당신이해야 할 일은 수정 가능한 문자열을 해당 버퍼에 전달하는 것입니다.
: 빠른 예 : 예 : : : : 예 : : 예 : : 예 : : 예 : : 예 : : 예 : 예 : : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : 예 : : 예 : : 예 : : 예 : 예)
char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
이런 식으로 테스트하고 있습니까?
int main() {
char * str = "foobar";
reverse(str);
printf("%s\n", str);
}
이것은 str을 문자 그대로 만들고 아마도 그것을 편집 할 수 없을 것입니다 (segfaults). 당신이 정의하는 경우 char * str = strdup(foobar)
잘 작동해야합니다 (나를 위해).
귀하의 선언은 완전히 잘못되었습니다.
char* s = "teststring";
"TestString"은 코드 세그먼트에 저장되며 코드와 같이 읽기 전용입니다. 그리고 S는 "TestString"에 대한 포인터이며 동시에 읽기 전용 메모리 범위의 값을 변경하려고합니다. 따라서 분할 결함.
하지만 함께:
char s[] = "teststring";
S는 물론 코드 세그먼트에있는 "TestString"으로 초기화되지만이 경우 스택에는 추가 사본 작업이 진행됩니다.
어떤 컴파일러 및 디버거를 사용하고 있습니까? GCC 및 GDB를 사용하여 -G 플래그로 코드를 컴파일 한 다음 GDB로 실행합니다. Segfaults에서, 나는 단지 뒤에서 (GDB의 BT 명령)를 수행하고 문제를 일으키는 문제를 해결하는 선을 보게 될 것입니다. 또한 GDB의 포인터 값을 "시청"하면서 코드를 단계별로 실행하고 문제가 정확히 어디에 있는지 알 수 있습니다.
행운을 빕니다.
위에 제공된 답변 중 일부와 같이 문자열 메모리는 읽기 전용입니다. 그러나 일부 컴파일러는 쓰기 가능한 문자열로 컴파일하는 옵션을 제공합니다. 예를 들어 gcc
, 3.x 버전이 지원됩니다 -fwritable-strings
그러나 최신 버전은 그렇지 않습니다.
보다 질문 1.32 C FAQ 목록에서:
이러한 초기화의 차이점은 무엇입니까?
char a[] = "string literal"; char *p = "string literal";
새 값을 할당하려고 하면 내 프로그램이 충돌합니다.
p[i]
.답변:
문자열 리터럴(C 소스에서 큰따옴표로 묶인 문자열에 대한 공식 용어)은 두 가지 약간 다른 방식으로 사용될 수 있습니다.
선언에서와 같이 char 배열의 초기화자로
char a[]
, 해당 배열에 있는 문자의 초기 값(및 필요한 경우 크기)을 지정합니다.다른 곳에서는 이름이 지정되지 않은 정적 문자 배열로 바뀌고 이 명명되지 않은 배열은 읽기 전용 메모리에 저장될 수 있으므로 반드시 수정할 수는 없습니다..표현식 컨텍스트에서 배열은 평소와 같이 한 번에 포인터로 변환되므로(섹션 6 참조) 두 번째 선언이 초기화됩니다.
p
이름 없는 배열의 첫 번째 요소를 가리킵니다.일부 컴파일러에는 문자열 리터럴의 쓰기 가능 여부(이전 코드 컴파일용)를 제어하는 스위치가 있고, 일부 컴파일러에는 문자열 리터럴이 공식적으로 배열로 처리되도록 하는 옵션이 있을 수 있습니다.
const char
(더 나은 오류 잡기를 위해).(내 강조)