const 에 대하여...

toy의 이미지

gcc에서 const 형에 대하여 이상한점이 있어서 질문합니다.
const char * c1 = "AAAA" ;
일때 c1 = "XXX" ; 는 허용되고 c1[0] = 'O' ; 와 같은건 안되는걸 확인했는데요.

char * const c2 = "BBB" ;
일경우!!!
c2 = "XXX" ; 하면 컴파일할때 워닝한번 나고 실행하면 잘실행됩니다.
c[0] = 'O' ; 는 컴파일할때 워닝안나는데 실행할때 세그폴트나는군요..ㅠㅜ
제가 알기로
c2 = "XXX" 는 허용이 안되고 c[0] = 'O'는 허용되어야하는거 아닌가요??
조언부탁드립니다...
아니면..gcc의 특성인지요...

jemiro의 이미지

세그폴트가 나는것이 정확한 동작입니다.

const char * c1 = "AAAA" ;
char * const c2 = "BBB" ;

위와 같이 정의를 한경우에는
c1과 c2의 const 특성과 상관없이
두 포인터가 가르키는 주소가 c1[0]='O'와 같이 쓰기가 가능한 메모리가 아니기 때문에 세그폴트가 나야지 정상입니다 :)

toy의 이미지

답변감사합니다..^^

몇가지 더 궁금한게 있는데요..
char * a = "Hello"
char a[] = "Hello"
이둘의 정확한 차이가 어떻게 되는지 갑자기 헛갈리네요..
예전엔 알았던거같은데...
아무튼 배열로 했을때는 값이 변경 가능하더군요.
그런데 .s파일을 보니 둘다 .rodata에 문자열을 넣고있던데요...

그리고요...
char * const a = "hello" 는 a라는 포인터변수값자체를 변경할수없다는것아닌가요...?
그런데 a = "xxx" 이와같은 표현식이 가능하다는게 이해가 잘 안갑니다.
(물론 컴파일할때 워닝은 냈지만 실행할때 잘실행이 되더군요. ) :oops:

akbar의 이미지

...

버려진의 이미지

char * const a = "hello";

char * const a;
a = "hello;

는 다릅니다. 제 능력으로 이거 설명하려면 한참 돌아가야 하겠는데요 :roll:

akbar의 이미지

...

버려진의 이미지

akbar wrote:
pyj200 wrote:
char * const a = "hello";

char * const a;
a = "hello;

는 다릅니다. 제 능력으로 이거 설명하려면 한참 돌아가야 하겠는데요 :roll:

char * const a;
a = "hello;

이란 코드도 역시 불가합니다.
허용된다면 어디까지나 컴파일러 차원이므로
이렇게 쓰지 마세요

저는 "그런데 a = "xxx" 이와같은 표현식이 가능하다는게 이해가 잘 안갑니다."
에 대한 답을 한거였습니다. 8) 위에것을 아래것처럼 착각해서 값을 바꿀 수 있는 것이라고 혼동하면 안된다는 뜻으로 쓴겁니다 :roll:

맹고이의 이미지

pyj200 wrote:
char * const a = "hello";

char * const a;
a = "hello;

는 다릅니다. 제 능력으로 이거 설명하려면 한참 돌아가야 하겠는데요 :roll:

그냥... '기본 값'과 '값의 변경'의 차이 아닌가요?

그리고 const는 값의 변경을 막는다... 정도로 알면 될 것 같은데... ;;

akbar의 이미지

pyj200 wrote:
akbar wrote:

char * const a;
a = "hello;

이란 코드도 역시 불가합니다.
허용된다면 어디까지나 컴파일러 차원이므로
이렇게 쓰지 마세요

저는 "그런데 a = "xxx" 이와같은 표현식이 가능하다는게 이해가 잘 안갑니다."
에 대한 답을 한거였습니다. 8) 위에것을 아래것처럼 착각해서 값을 바꿀 수 있는 것이라고 혼동하면 안된다는 뜻으로 쓴겁니다 :roll:

아 그러셨군요. 이런 민망할 때가 ... ^^

toy의 이미지

답변해주신 모든분들 감사드립니다. ^^

akbar wrote:
toy wrote:

1) 의 경우 문자열 "Hello" 을
.rodata 영역에서 메모리로 올리지 않습니다.

2) 의 경우 문자열 "Hello" 을
.rodata 영역에서 메모리로 올립니다.


1의 경우 메모리에 올리지 않는다는 말이 이해가 안갑니다.
메모리에 올라가지 않는다면 찍힐수조차 없지 않나요..?? :oops:
toy의 이미지

문자열의 갯수를 20개정도로 해보니 조금 이해가 가네요...
배열일경우는 rep movsb 를 통해서 스택으로 카피를 해주는데
포인터일 경우는 그런작업이 없군요...

아직 조금은 꺼림직하지만 약간은 이해되네요...^^
감사^^

lsj0713의 이미지

toy wrote:
gcc에서 const 형에 대하여 이상한점이 있어서 질문합니다.
const char * c1 = "AAAA" ;
일때 c1 = "XXX" ; 는 허용되고 c1[0] = 'O' ; 와 같은건 안되는걸 확인했는데요.

char * const c2 = "BBB" ;
일경우!!!
c2 = "XXX" ; 하면 컴파일할때 워닝한번 나고 실행하면 잘실행됩니다.
c[0] = 'O' ; 는 컴파일할때 워닝안나는데 실행할때 세그폴트나는군요..ㅠㅜ
제가 알기로
c2 = "XXX" 는 허용이 안되고 c[0] = 'O'는 허용되어야하는거 아닌가요??
조언부탁드립니다...
아니면..gcc의 특성인지요...

const char *c1은 c1이 가리키는 개체가 const char형이라는 뜻입니다. 즉 변경 불가능이죠. c1 자체는 변경 가능합니다.

Example 1:

const char *c1 = "AAA";
c1 = "BBB";    // 올바른 코드
c1 = "CCC";    // 올바른 코드
*c1 = 'b';    // 틀린 코드
c1[0] = 'B';    // 틀린 코드

반대로 char* const c2는 c2 자체가 const 라는 뜻입니다. c2 자체의 값은 변경 불가능이지만 c2가 가리키는 개체는 변경 가능합니다.

Example 2:

// 다음의 코드는 적법하다.
char str1[] = "ABC"; 
char* const p1 = str1;
p1[0] = 'a';
p1[1] = 'b';
printf("%s | %s\n", p1, str1);    // abC | abC 가 출력된다.

그럼 다음의 예제에서는 왜 상황이 약간 다른고 하니...

Example 3:

char* const c2 = "AAA";    // 올바른 코드
c2 = "BBB";    // 틀린 코드
*c2 = 'B';    // 틀린 코드
c2[0] = 'B';    // 틀린 코드

char s[] = "ABC"와 같이 char형 배열의 초기화에 쓰이는 것을 제외하고(이때는 정적 기억 수명을 가지는 개체가 따로 생성되지 않으며, s 안에 그냥 "ABC"가 저장됩니다), 모든 문자열 상수는 그 자신의 첫번째 원소를 가리키는 포인터 주소값과 동일하게 처리됩니다. 그리고 그 자신은 정적 기억 수명을 갖는 const char형 배열이 됩니다.

Example 4:

char *s = "ABC";

// 위의 코드는 아래의 코드와 의미적으로 동일하다 할 수 있다.

static const char _tmp[] = { 'A', 'B', 'C' }; //  정적 기억 수명을 갖는다!
char *s = _tmp;

예제 3으로 돌아가겠습니다. 2번째 코드는 그렇다고 쳐도 왜 3번째와 4번째 줄이 틀린가 하면 그것은 c2 자체는 char * const형이지만 c2가 가리키는 개체, 즉 문자열 상수의 타입이 const char[] 이기 때문입니다.

참고로 예제 3번의 첫번째 줄은 다음과 같이 쓰는 쪽이 더 바람직합니다.

const char * const c1 = "ABC";

이로써 c1 자체도 변경이 불가능하고, c1이 가리키는 개체도 변경이 불가능하다는 것이 명확해졌습니다.

kyong의 이미지

lsj0713 wrote:

char s[] = "ABC"와 같이 char형 배열의 초기화에 쓰이는 것을 제외하고(이때는 정적 기억 수명을 가지는 개체가 따로 생성되지 않으며, s 안에 그냥 "ABC"가 저장됩니다), 모든 문자열 상수는 그 자신의 첫번째 원소를 가리키는 포인터 주소값과 동일하게 처리됩니다. 그리고 그 자신은 정적 기억 수명을 갖는 const char형 배열이 됩니다.

Example 4:

char *s = "ABC";

// 위의 코드는 아래의 코드와 의미적으로 동일하다 할 수 있다.

static const char _tmp[] = { 'A', 'B', 'C' }; //  정적 기억 수명을 갖는다!
char *s = _tmp;


이것은 C++에 해당하는 것 같습니다.
C++에선 문자열 상수가 const로 묵인되죠.
lsj0713의 이미지

kyong wrote:
lsj0713 wrote:

char s[] = "ABC"와 같이 char형 배열의 초기화에 쓰이는 것을 제외하고(이때는 정적 기억 수명을 가지는 개체가 따로 생성되지 않으며, s 안에 그냥 "ABC"가 저장됩니다), 모든 문자열 상수는 그 자신의 첫번째 원소를 가리키는 포인터 주소값과 동일하게 처리됩니다. 그리고 그 자신은 정적 기억 수명을 갖는 const char형 배열이 됩니다.

이것은 C++에 해당하는 것 같습니다.
C++에선 문자열 상수가 const로 묵인되죠.

말씀하시고자 하는 바를 모르겠습니다. C에서는 문자열 상수가 수정될 수 있다는 뜻입니까? 아니면 다른 뜻이 있습니까? 자세한 설명 부탁드립니다.

ps. 다시 한번 문서를 찾아보니 문자열 상수는 char형 배열이고, 그것을 수정하려는 행동의 결과는 정의되어 있지 않다고 나와 있군요. 따라서 엄밀하게 따지자면 const char형 배열이라고 볼 수 없습니다. (문자열 상수의 원소를 변경하려는 행동의 결과는 정의되어 있지 않지만, const char형 배열의 원소를 변경하려는 시도는 금지되어 있습니다) 하지만 무슨 뜻으로 말씀하셨는지에 대해서는 확답을 듣고 싶습니다. 제가 뭔가 잘못 알고 있는 것이 있는지 확인하고 싶군요.

ps2. 좀 더 찾아보니 pointer to pointer to char와 pointer to pointer to const char 사이의 관계에서 문제가 생길 소지가 있군요. 지금까지 문자열상수를 가리킬 때 const char *를 즐겨 사용해 왔는데 char *로 바꿔야 할 것 같습니다.

ixevexi의 이미지

뱐 아자씨 말쌈이

Quote:

The type of a string literal is "array of the appropriate number of const characters," so "Bohr" is of type const char[5]
A string literal can be assinged to a char*. This is allowed because in previous definitions of C and C++, the type of a string literal was char*. Allowing the assingment of a string lietreal to a char* ensures that millions of lines of C and C++ remain valid. It is, however, an error to try to modify a string literal through such a pointer; 여기엔 간단한 코드가 :D This kind of error cannot in general be caught until run-time, and implementations differ in ther enforcement of this rule.

간략히 말하면 C++의 리터럴은 const char*지만 C와 옛 버전의 C++은
char*다. 그 옛버전과 수정없는 호환을 위해서 /const와 비 const는 캐스트를 해줘야 합니다 ^^/ char*로 어자인될 수 있게 해놨다...

그리고 뒤에는 char *라고 해서 고칠 수 없고 이때의 에러는 보통 런타임전까지 잡아낼 수 없고, 또한 이 룰에따른 구현은 다를 수 있다. 라고 이야기하네요

그런데 좋은 컴파일러들은 알려줄 수 있지 않을까요? 하다못해 워닝이라두 ^^

PS한번 고쳤습니다. 런타임시에 =>런타임 전까지 ^^
그리고 const 를 비 const포인터로 받으려면
비 const 포인터로 캐스트해야하는 것이 맞죠?
저도 역시 초보라서 잘 몰라서요 고수들의 정확한 답변 부탁드립니다.

C++, 그리고 C++....
죽어도 C++

kyong의 이미지

lsj0713 wrote:
kyong wrote:
lsj0713 wrote:

char s[] = "ABC"와 같이 char형 배열의 초기화에 쓰이는 것을 제외하고(이때는 정적 기억 수명을 가지는 개체가 따로 생성되지 않으며, s 안에 그냥 "ABC"가 저장됩니다), 모든 문자열 상수는 그 자신의 첫번째 원소를 가리키는 포인터 주소값과 동일하게 처리됩니다. 그리고 그 자신은 정적 기억 수명을 갖는 const char형 배열이 됩니다.

이것은 C++에 해당하는 것 같습니다.
C++에선 문자열 상수가 const로 묵인되죠.

말씀하시고자 하는 바를 모르겠습니다. C에서는 문자열 상수가 수정될 수 있다는 뜻입니까? 아니면 다른 뜻이 있습니까? 자세한 설명 부탁드립니다.


전 C에서 char *s = "ABC"와 같이 선언 됐을 때 문자열 상수가 변경될 수도
있다는 뜻이였습니다.
전 language lawyer는 아닙니다. 처음엔 implementation 상 점검이였다고
말하고 싶습니다.
제가 이렇게 생각한 것은
char *s = "ABC";
s[1] = 'D';

와 같은 코드가 전혀 문제가 없다는 것입니다.
ms compiler에서는 run time error도 나지 않더군요.
(Version 13.10.3077 for 80x86)

그리고 엄밀히 하면

char *s = "ABC";
----------------------------------------------------     
static const char _tmp[] = { 'A', 'B', 'C' , 0};
char *s = _tmp; 

과 같이 되어야 합니다.
결과적으로 implementation defined 된 부분이니 C에서 틀린 부분은
없지요.
lsj0713의 이미지

kyong wrote:

제가 이렇게 생각한 것은
char *s = "ABC";
s[1] = 'D';

와 같은 코드가 전혀 문제가 없다는 것입니다.
ms compiler에서는 run time error도 나지 않더군요.
(Version 13.10.3077 for 80x86)

반면에 gcc 3.2.3에서는 실행시간에 Segmentation fault 가 일어납니다. 지금 옆에 책이 없어서 확인이 불가능한데, 리눅스 쪽에서는 문자열 상수를 별도의 독립된 메모리 구역에 저장한다고 들었던 것 같습니다. 당연히 이 공간에는 수정이 금지되겠지요.

kyong wrote:

결과적으로 implementation defined 된 부분이니 C에서 틀린 부분은
없지요.

C99를 기준으로 말씀드리자면...

우선, 두개의 동일한 내용을 갖는 문자열 상수가 동일한 것인지 아닌지부터가 벌써 unspecified behavior 입니다.

char *str1 = "ABC";
char *str2 = "ABC";

// equal 이 출력될지 말지는 unspecified behavior에 의존
// 참고로 gcc 3.2.3 에서는 equal 이 출력된다.
if ( str1 == str2 ) { printf("equal!\n"); }

str1[0] = 'D';    // 일단, 여기서는 허용된다고 봤을 때

printf("%s\n", str2);    // DBC 가 출력될 것인가, ABC가 출력될 것인가?
// implementation defined behavior 인가, unspecified behavior 인가?

MSDN에서는 이에 대해 다음과 같이 말하고 있습니다.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/eleme_23.asp

"Note that the compiler may not store two identical strings at two different addresses."

그리고 문자열 상수의 내용을 수정하려는 행위에 대해서는 정의된 바 없습니다. (If the program attempts to modify such an array, the behavior is
undefined.) 따라서 그냥 실행하던 컴퓨터를 말아먹던 컴파일러 마음입니다.

MSDN에서도 이 사항에 대해서는 '그렇게 디자인하지 말라'고 말하고 있습니다.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/eleme_23.asp

In translation phase 6, the multibyte-character sequences specified by any sequence of adjacent string literals or adjacent wide-string literals are concatenated into a single multibyte-character sequence. Therefore, do not design programs to allow modification of string literals during execution. The ANSI C standard specifies that the result of modifying a string is undefined.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.