char *로 할당된 스트링값을 변경하는 방법에 대해서

neon20의 이미지

아래와 같은 형태로 이미 배정된 스트링값을 변경하는건
불가능 한가요? 스트링값을 변경하기 위해선 배열만 사용해야
하는건가요? 다른 방법이 있다면 알려주세요.

#include"stdio.h"

void main()
{
	char *a;
	a = "girl";
	*(a+0) = 'o';	
}
익명 사용자의 이미지

모든 문자열 상수(string literal)는 변경 불가능합니다.

char *a;
a = "girl";
*(a+0) = 'o';    

"girl"은 문자열 상수이며 변경 불가능합니다.

char a[] = "girl";
a[0] = 'o';

a는 char형 배열이므로 변경 가능합니다.

정태영의 이미지

구버젼의 gcc 의 경우엔... 옵션으로 가능하게 할 수도 있었지만... 이젠 그 옵션이 아예 없어졌나보군요 ;)

mac gcc wrote:
-fwritable-strings
Store string constants in the writable data segment and don't uniquize them. This is for
compatibility with old programs which assume they can write into string constants.

Writing into string constants is a very bad idea; ``constants'' should be constant.

This option is deprecated.

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

hanzo69의 이미지

C언어 문법상으로 불가능한건 아닙니다. 예전 도스라면 가능했던 일이죠.
문제는 그 문자열 상수가 어디에 있냐죠.

실행파일이 매번 실행될 때 실행파일 전체를 메모리에 별도로 복사한다면 가능한 일이지만, 리눅스나 윈도우 둘 다 한번만 메모리에 로딩하고 그 다음엔 이미 로딩한 실행파일 메모리를 공유해서 사용합니다.
문자열 상수는 이 공유되는 메모리에 속해있으며, 이 이미지 영역은 read-only 속성을 가지게 됩니다.
대신 전역변수는 읽기-쓰기 가능한 메모리를 할당해 배치시키죠.
char a[] = "girl";
위와 같이 초기값이 있는 전역 변수는 실행시에 그러한 읽기-쓰기 가능한 메모리 영역으로 이미지 영역의 초기값을 복사하는 방식이구요.
char *a = "girl";
이런 경우엔 a가 읽기만 가능한 이미지 영역을 직접 가리키므로 쓰기를 하게 되면 세그-폴트가 발생하게 됩니다.

글쎄.. 윈도우라면 문자열 영역에 VirtualProtect()를 사용해서 PAGE_WRITECOPY 속성을 지정하면 쓰기 시도를 할 때 별도의 물리적 메모리에 자동적으로 복사하게 만들어 쓸 수 있긴 한데..
그러느니 차라리 직접 배열을 잡아 복사한 후 쓰는게 편하죠.

님ㅎ 즐~

neon20의 이미지

음..c의 포인터 스트링은 변경 불가능하군요..ㅜㅜ
너무 감사합니다

익명 사용자의 이미지

Quote:
C언어 문법상으로 불가능한건 아닙니다. 예전 도스라면 가능했던 일이죠.

문자열 상수를 수정하는 행동은 정의되지 않은 동작을 일으킵니다. 문법만 따지자면 불가능한 게 맞고, 예전 도스시절의 C(아마도 Turbo C인거 같은데)가 특별한 경우일 뿐입니다.

이와 관련하여 한가지 더 고려할 사항이 있는데

char *a = "str";
char *b = "str";

같은 내용을 가지는 문자열 상수는 하나만 만들어질 수도 있습니다(단, 항상 그래야 되는 것은 아닙니다.). 어차피 문자열 상수는 수정이 불가능하므로, 같은 내용이라면 하나만 만드는 것이 메모리 공간을 적게 차지합니다. 문자열 상수를 변경할 수 있다 해도, a가 가르키는 문자열 상수를 고치면 b가 가르치는 문자열 상수의 내용도 바뀔 수 있습니다.

Quote:
대신 전역변수는 읽기-쓰기 가능한 메모리를 할당해 배치시키죠.

전역변수와는 전혀 상관이 없습니다. 전역변수는 보통 file scope를 가지는 변수를 뜻하는 거라 알고 있습니다만... a가 전역변수이던 아니던, "str"은 수정 불가능합니다. 이것은 a가 가르키는 "str"이 문자열 상수이기 때문입니다.

char a[] = "str";

이 경우에는 a가 배열로 생성이 되고, "str"이란 내용으로 초기화됩니다. a는 char형 배열이므로 그 안의 내용을 변경할 수 있는 것이 당연합니다.

hanzo69의 이미지

c언어에 문자열형이 있던가요?

c언어에선 문자열형이란 없을텐데요. 오직 포인터형, 정수형, 실수형 세가지뿐이죠. 정수형은 크기별로 몇가지가 있긴 있죠.
"abcd"는 문자열형이 아니고 메모리 공간일 뿐이며, 평가형은 (const char *)형입니다. 이걸 건드리든 말던 그건 프로그래머 자유죠.

"abcd"[0] = 0;
이 코드는 "abcd" 자체가 상수가 아니라 (const char *) 형이 가리키는 데이터이므로 문법적으로 오류가 있을 뿐입니다.
((char *)"abcd")[0] = 0;
char *p = "abcd";
p[0] = 0;
은 실제론 먼저의 코드와 같은 일을 하지만 문법적으론 아무 문제가 없는 코드죠. 단지 실행시에 오류가 날 뿐이죠.
정의되지 않은 동작이 아니고 a라는 문자의 위치에 0을 쓰려 시도를 합니다. 그리고 그 위치가 read-only라면 예외가 발생할 뿐이죠. 만약 롬과 램이 주소공간에 물린 아케이드 보드라면... 뭐 쓰기 시도가 무시되겠죠.

그리고 c언어 문법상으론 const 형은 자료형만 같다면 non-const 형에 대입할땐 자동 변환이 가능하도록 되어 있습니다.
char *p = (char *)"abcd"; // const char * 형을 char *형으로 변환
int i = (int)1; // const int형을 int형으로 변환
이렇게 명시적으로 변환하며 코딩하는 사람은 없죠.
그리고 일단 변환된 이상은 건드리던 말던 시스템이 받아주는 한계 내에선 c의 문법 내에선 자유랍니다. 실행시 오류가 나면 나는거죠.

세상의 모든 시스템이 "abcd"를 건드려선 안되는건 아닙니다. 건드려도 무방한 시스템에선 얼마든지 건드려도 된답니다.
오히려 리눅스가 특별한 경우라면 특별한 경우겠죠. 원래 읽기 쓰기가 가능한 메모리를 읽기만 가능하게 만들었으니까요.
윈도우에선 "abcd" 영역을 copy on write 속성을 지정하면 쓰기가 가능하니 뭐... 좀 다르다 해야 할라나?

그리고 정적 변수와 전역변수의 의미에 대한 차이는 사실 c언어에선 무의미하지 않던가요?
어차피 함수 밖에서 변수를 선언하면 static 키워드를 따로 붙이지 않는 한 모두 전역변수랍니다.
그리고 static를 붙여도 외부에서 포인터를 얻는다면 얼마든지 접근이 가능하죠.

그리고 전역변수던 상수든지간에 c언어 문법상으론 모든 상수를 얼마든지 수정할 수 있답니다. 포인터를 얻어 쓰기를 하면 그만이니까요. 단 운영체제가 그 상수가 기억된 메모리를 읽기 전용으로 설정했는가가 문제일뿐이죠. 아니면 롬이라던가..

c언어는 다른 언어와 달리 형식에 대하여 자유로운 언어입니다... 흠.. 문자열형 등등을 따지며 엄격하게 다루려면.. 차라리 델파이나 자바가 어울리지 않을까 하네요. 그런식이라면 힘들게 c언어를 공부할 필요가 없겠죠. 좀 빠른 자바쯤이 되려나?

님ㅎ 즐~

doldori의 이미지

hanzo69 wrote:
c언어에 문자열형이 있던가요?
c언어에선 문자열형이란 없을텐데요.

hanzo69님 외에는 문자열형이라는 말을 쓴 사람이 없는데요?
말씀대로 C에는 문자열형이 없습니다. 지금 논의되는 대상을 가리켜 문자열 상수
(string literal)라는 용어를 쓰지요. 형을 따진다면 배열형입니다.

hanzo69 wrote:
"abcd"는 문자열형이 아니고 메모리 공간일 뿐이며, 평가형은 (const char *)형입니다. 이걸 건드리든 말던 그건 프로그래머 자유죠.

글쎄요. 컴파일이 된다는 이유로 이런 것을 자유라고 부를 수 있는지는 의문입니다.
비슷한 얘기로 널 포인터를 역참조하는 것도 자유라고 하시겠습니까?
그리고 평가형이라는 말도 처음 들어보는군요. "abcd"는 char[5]의 배열이며
char*로 변환될 수 있을 뿐입니다.

hanzo69 wrote:
"abcd"[0] = 0;
이 코드는 "abcd" 자체가 상수가 아니라 (const char *) 형이 가리키는 데이터이므로 문법적으로 오류가 있을 뿐입니다.
((char *)"abcd")[0] = 0;
char *p = "abcd";
p[0] = 0;
은 실제론 먼저의 코드와 같은 일을 하지만 문법적으론 아무 문제가 없는 코드죠. 단지 실행시에 오류가 날 뿐이죠.
정의되지 않은 동작이 아니고 a라는 문자의 위치에 0을 쓰려 시도를 합니다. 그리고 그 위치가 read-only라면 예외가 발생할 뿐이죠. 만약 롬과 램이 주소공간에 물린 아케이드 보드라면... 뭐 쓰기 시도가 무시되겠죠.

길게 설명하신 것을 C에서는 정의되지 않은 동작(undefined behavior)이라고 부릅니다.
참고로 C99 표준에서 관련된 내용을 인용합니다.
Quote:
6.4.5/6
[...] If the program attempts to modify such an array, the behavior is undefined.

hanzo69 wrote:
그리고 c언어 문법상으론 const 형은 자료형만 같다면 non-const 형에 대입할땐 자동 변환이 가능하도록 되어 있습니다.
char *p = (char *)"abcd"; // const char * 형을 char *형으로 변환
int i = (int)1; // const int형을 int형으로 변환
이렇게 명시적으로 변환하며 코딩하는 사람은 없죠.
그리고 일단 변환된 이상은 건드리던 말던 시스템이 받아주는 한계 내에선 c의 문법 내에선 자유랍니다. 실행시 오류가 나면 나는거죠.

C가 프로그래머의 자유를 상당히 허용하는 편이긴 합니다만, 그렇다고 모든 자유를
허용하는 것은 아닙니다. 컴파일이 된다고 해서, 정의되지 않은 동작을 유발하는
코드가 우연히 의도대로 실행된다고 해서 이것을 자유라고 받아들이는 것은 곤란합니다.
정의되지 않은 동작을 유발하는 코드는 분명 잘못된 것입니다.

hanzo69 wrote:
세상의 모든 시스템이 "abcd"를 건드려선 안되는건 아닙니다. 건드려도 무방한 시스템에선 얼마든지 건드려도 된답니다.
오히려 리눅스가 특별한 경우라면 특별한 경우겠죠. 원래 읽기 쓰기가 가능한 메모리를 읽기만 가능하게 만들었으니까요.
윈도우에선 "abcd" 영역을 copy on write 속성을 지정하면 쓰기가 가능하니 뭐... 좀 다르다 해야 할라나?

어떤 코드가 특정 플랫폼에서 어떻게 구현되는지 아는 것도 도움이 되겠지만, 그 전에
언어의 정의를 바르게 이해하는 것이 우선이라고 봅니다. 이식성을 중요하게 생각한다면
당연히 그래야 하지 않겠습니까?

hanzo69 wrote:
그리고 전역변수던 상수든지간에 c언어 문법상으론 모든 상수를 얼마든지 수정할 수 있답니다. 포인터를 얻어 쓰기를 하면 그만이니까요. 단 운영체제가 그 상수가 기억된 메모리를 읽기 전용으로 설정했는가가 문제일뿐이죠. 아니면 롬이라던가..

상수는 l-value가 아니므로 그에 대한 주소를 취할 수 없습니다.
int* p = &0;
*p = 1;
이것은 운영 체제가 메모리를 읽기 전용으로 설정하건 말건 언어의 정의에 의해
잘못된 코드입니다.

hanzo69 wrote:
c언어는 다른 언어와 달리 형식에 대하여 자유로운 언어입니다... 흠.. 문자열형 등등을 따지며 엄격하게 다루려면.. 차라리 델파이나 자바가 어울리지 않을까 하네요. 그런식이라면 힘들게 c언어를 공부할 필요가 없겠죠. 좀 빠른 자바쯤이 되려나?

자유롭다고 해서 힘들게 C를 공부할 필요가 없을까요? 비교적 간단한 언어이긴 해도
제대로 이해하려면 꽤나 시간이 걸립니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.