C의 포인터 사용중에서 궁금한게 생겼는데요..

찬밥의 이미지

얼마전.. C++
시험공부를 하다..
책(The C++ Programming Language - Stroustrup)을 봤는데요..

char *str = "something";
str[3] = 'a';

이런식으로 하면 에러가 난다더라구요..
그래서.. 직접해봤는데 컴파일시 에러는 나지 않고..
실행했을 때 무슨 오류인진 모르겠으나..

Quote:

AppName: cpp1.exe AppVer: 0.0.0.0 ModName: cpp1.exe
ModVer: 0.0.0.0 Offset: 00001032

이런 내용과 함께 에러가 납니다..

그래서 이번엔

char str[] = "something";
str[3] = 'a';

위 처럼 고쳐서 해봤더니.. 아무문제가 없더군요..

이게 왜 그런건가요??

세벌의 이미지

char *str = "something" 에서 str의 내용을 바꾸면 안 됩니다.

컴파일은 잘 되도 실행하면 제대로 안 되는 경우는 아주 많습니다. 원칙에 어긋나는 것을 하면 그래도 될 때도 있기는 한데 항상 잘 된다는 보장은 없습니다.

익명 사용자의 이미지

두 코드가 어떤 방식으로 동작하는지 이해하고 있어야 합니다.

char *str = "something";
str[3] = 'a'; 

"something"이라는 내용의 문자열 상수를 만들고, str이 그것을 가리키게 합니다. "something"는 문자열 상수이므로 그 내용을 고칠 수 없습니다.

char str[] = "something";
str[3] = 'a'; 

char형 배열 str을 "something"이라는 내용으로 초기화합니다. str의 크기는 "something"을 저장할 수 있는 크기로 자동 결정됩니다. str은 char형 배열이므로 당연히 그 안의 내용을 고칠 수 있습니다.

ixevexi의 이미지

간단히 이야기하면 두개의 형이 다릅니다.

char str[] 와 char * str은 엄밀히 이야기하면 다른 타입입니다.

str[]는 포인터가 아니라 어레이죠.
그리고 포인터로 implicit conversion이 가능할 뿐입니다.

두 개는 또한 저장되는 위치또한 다릅니다.

보통 char str[] = "~~" 로 할당될때
~~는 스택에 저장이 됩니다. 스택은 쓰고 읽기가 가능합니다.

char * str = "~~" 에서 할당작업은 일어나지 않습니다.
위의 "~~"는 리터럴 상수로서 보통 데이터영역에 저장이 됩니다
이 데이터 영역은 보통 쓰기 금지가 되어있죠??
그래서 에러가 날겁니다.

잘 알지도 모르는 사람이 그냥 써봤습니다.
혹여 고수님들 틀린거 있으면 지적해주세요 ㅠ.ㅜ

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

happycat의 이미지

아래 두 분 말씀이 정확합니다.

하나는 데이터 영역이라 수정이 불가하고, 하나는 스택 영역이라 수정이 가능합니다.

dondek의 이미지

char *s = "hello, world!";

보통은 code 영역에 잡히는게 아닌가요? 물론 이것도 구현에 따라 다르겠지만요.

진리를 나의 수준으로 끌어내리지 마라.
나를 진리의 수준으로 끌어올려라. - 배꼽 중에서

익명 사용자의 이미지

char *str="something";
하면 "something"이라는 문자열은 컴파일하고 실행될때 메모리의
어딘가에 할당되어 있습니다..
만일 char str[2] 이렇게 하면 str이 잡힌메모리 번지+2 이곳을
참조하게 됩니다.. *(str+2)이렇게요..그럼 당연히 메모리
침범이 되어서 에러가 나겠죠...
변수 str에 할당된 메모리 번지에 +2번지가
더해져서 에러가 나는거겠죠...

ixevexi의 이미지

dondek wrote:
char *s = "hello, world!";

보통은 code 영역에 잡히는게 아닌가요? 물론 이것도 구현에 따라 다르겠지만요.

생각해보니 궁금해 져서 한번 해보았습니다.



int main(int argc, char **argv)
{

    char * str1 = " Hello, World!! Array1";
    char str2[] = " Hello, World!! Array2";
    return 0;
}
     

위 코드를 gcc -c test.c로 하여 컴파일 해보았습니다.
그리고 그 결과를 objdump로 한번 떠 보았습니다
다음과 같더군요

Quote:

t.o: file format elf32-i386

Disassembly of section .text:

00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 38 sub $0x38,%esp
6: 83 e4 f0 and $0xfffffff0,%esp
9: b8 00 00 00 00 mov $0x0,%eax
e: 29 c4 sub %eax,%esp
10: c7 45 f4 00 00 00 00 movl $0x0,0xfffffff4(%ebp)
17: a1 17 00 00 00 mov 0x17,%eax
1c: 89 45 c8 mov %eax,0xffffffc8(%ebp)
1f: a1 1b 00 00 00 mov 0x1b,%eax
24: 89 45 cc mov %eax,0xffffffcc(%ebp)
27: a1 1f 00 00 00 mov 0x1f,%eax
2c: 89 45 d0 mov %eax,0xffffffd0(%ebp)
2f: a1 23 00 00 00 mov 0x23,%eax
34: 89 45 d4 mov %eax,0xffffffd4(%ebp)
37: a1 27 00 00 00 mov 0x27,%eax
3c: 89 45 d8 mov %eax,0xffffffd8(%ebp)
3f: 0f b7 05 2b 00 00 00 movzwl 0x2b,%eax
46: 66 89 45 dc mov %ax,0xffffffdc(%ebp)
4a: 0f b6 05 2d 00 00 00 movzbl 0x2d,%eax
51: 88 45 de mov %al,0xffffffde(%ebp)
54: b8 00 00 00 00 mov $0x0,%eax
59: c9 leave
5a: c3 ret
Disassembly of section .rodata:

00000000 <.rodata>:
0: 20 48 65 and %cl,0x65(%eax)
3: 6c insb (%dx),%es:(%edi)
4: 6c insb (%dx),%es:(%edi)
5: 6f outsl %ds:(%esi),(%dx)
6: 2c 20 sub $0x20,%al
8: 57 push %edi
9: 6f outsl %ds:(%esi),(%dx)
a: 72 6c jb 78 <main+0x78>
c: 64 21 21 and %esp,%fs:(%ecx)
f: 20 41 72 and %al,0x72(%ecx)
12: 72 61 jb 75 <main+0x75>
14: 79 31 jns 47 <main+0x47>
16: 00 20 add %ah,(%eax)
18: 48 dec %eax
19: 65 gs
1a: 6c insb (%dx),%es:(%edi)
1b: 6c insb (%dx),%es:(%edi)
1c: 6f outsl %ds:(%esi),(%dx)
1d: 2c 20 sub $0x20,%al
1f: 57 push %edi
20: 6f outsl %ds:(%esi),(%dx)
21: 72 6c jb 8f <main+0x8f>
23: 64 21 21 and %esp,%fs:(%ecx)
26: 20 41 72 and %al,0x72(%ecx)
29: 72 61 jb 8c <main+0x8c>
2b: 79 32 jns 5f <main+0x5f>
...
Disassembly of section .comment:

00000000 <.comment>:
0: 00 47 43 add %al,0x43(%edi)
3: 43 inc %ebx
4: 3a 20 cmp (%eax),%ah
6: 28 47 4e sub %al,0x4e(%edi)
9: 55 push %ebp
a: 29 20 sub %esp,(%eax)
c: 33 2e xor (%esi),%ebp
e: 33 2e xor (%esi),%ebp
10: 35 20 28 44 65 xor $0x65442820,%eax
15: 62 69 61 bound %ebp,0x61(%ecx)
18: 6e outsb %ds:(%esi),(%dx)
19: 20 31 and %dh,(%ecx)
1b: 3a 33 cmp (%ebx),%dh
1d: 2e 33 2e xor %cs:(%esi),%ebp
20: 35 2d 38 29 00 xor $0x29382d,%eax

이렇게 보면 잘 모를 것입니다.
그러나 Hello, World의 아스키 값을 적어보자면
48(H), 65(e), 6c(l), 6c(l)... 로 되고
저기 .rodata 세그먼트에 있군요 앞의 20은 스페이스가 되겠습니다. 결론적으로 이 Hello 패턴을 두개 찾을 수 있습니다
.rodata세그먼트에서요 :D

이 짓을 다 하고 보니 생각해보니까
strings라는 간편한 유틸리티가 있었는데 -_-;;
내가 멀 했나 하기도 싶고..

물론 이건 순전히 gcc 구현에 따른 것이기 때문에 절대적인
자료는 될 수 없겠지만, 요새 컴파일러들이
데이터 상수들과 코드를 섞어서 컴파일할것이라고 생각 되지는
않네요 ^^

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

advanced의 이미지

소스코드내에 초기화 된 변수들의 내용은
data 영역에 저장 됩니다. 이영역은 read-only 영역입니다
그래서 고칠 수 가 없습니다

지역변수인 char *string 자체는 포인터 이지만
그것이 가르키고 있는 영역이 data 영역이라는 의미입니다

Hyun의 이미지

#include <stdio.h>

int abcd=1234;

main()
{
    printf( "%d\n", abcd );
    abcd = 2345;
    printf( "%d\n", abcd );
}

에서 abcd변수가 초기화 된 변수로서 data 영역에 저장된다고 알고 있는데... read only가 아니지 않습니까???

그리고... 스택에서 char *str = "string"; 라고 하는건... 정확히 표현해서 const char *str = "string";이지 않습니까? 즉... 고칠 수 없는 문자열 포인터죠...

ixevexi의 이미지

박현우 wrote:
#include <stdio.h>

int abcd=1234;

main()
{
    printf( "%d\n", abcd );
    abcd = 2345;
    printf( "%d\n", abcd );
}

에서 abcd변수가 초기화 된 변수로서 data 영역에 저장된다고 알고 있는데... read only가 아니지 않습니까???

그리고... 스택에서 char *str = "string"; 라고 하는건... 정확히 표현해서 const char *str = "string";이지 않습니까? 즉... 고칠 수 없는 문자열 포인터죠...

^^ 잠시 착각하신거 같습니다.
advanced님께서 설명해 주셨지만
다시 설명하자면 abcd와 1234가 저장되는 위치가 틀립니다.
1234는 데이터 영역에 있고 abcd는 코드 영역에 있습니다.
1234는 readonly입니다.

두번째로 "string"은 const char * 형이 아닙니다.
제가 알기로는 C++에서는 const char[] 이고
C에서는 단순히 char * 라고 알고 있습니다 <- 확실치 않네요 C는 잘 몰라서 ^^ 혹시 틀렸다면 고수분들 지적을;;

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

zeon의 이미지

ixevexi wrote:

이렇게 보면 잘 모를 것입니다.
그러나 Hello, World의 아스키 값을 적어보자면
48(H), 65(e), 6c(l), 6c(l)... 로 되고
저기 .rodata 세그먼트에 있군요 앞의 20은 스페이스가 되겠습니다. 결론적으로 이 Hello 패턴을 두개 찾을 수 있습니다
.rodata세그먼트에서요 :D

이 짓을 다 하고 보니 생각해보니까
strings라는 간편한 유틸리티가 있었는데 -_-;;
내가 멀 했나 하기도 싶고..

objdump -s

==333

여친이 길르는 용..

익명 사용자의 이미지

Quote:

에서 abcd변수가 초기화 된 변수로서 data 영역에 저장된다고 알고 있는데... read only가 아니지 않습니까???

전 그래서 스택이니 힙이니 데이터영역이니 하는 설명방식을 싫어합니다. 단지 문자열 상수가 실은 수정불가능한 char형 배열이라는 점만 알면 되는데 굳이 컴파일러 내부 구현까지 들먹일 필요는 없지요.

char c;
c = "abcde"[2];

문자열 상수는 배열입니다. char str[] = "str"과 같이 char형 배열을 초기화할 때 빼고는.

Quote:

그리고... 스택에서
코드:
char *str = "string";
라고 하는건... 정확히 표현해서
코드:
const char *str = "string";
이지 않습니까? 즉... 고칠 수 없는 문자열 포인터죠...

좀 더 정확히 얘기하자면

const char _t[] = "string";
char *str = _t;

가 되겠죠.

fromdj의 이미지

Processor가 메모리 보호를 지원하기때문에.. 그렇겠죠
제 개발환경에서는 다됩니다. ㅠㅠ
arm without memory protection..

^^ be cool ~
http://fromdj.pe.kr

gimmesilver의 이미지

Anonymous wrote:
Quote:

에서 abcd변수가 초기화 된 변수로서 data 영역에 저장된다고 알고 있는데... read only가 아니지 않습니까???

전 그래서 스택이니 힙이니 데이터영역이니 하는 설명방식을 싫어합니다. 단지 문자열 상수가 실은 수정불가능한 char형 배열이라는 점만 알면 되는데 굳이 컴파일러 내부 구현까지 들먹일 필요는 없지요.

char c;
c = "abcde"[2];

문자열 상수는 배열입니다. char str[] = "str"과 같이 char형 배열을 초기화할 때 빼고는.

Quote:

그리고... 스택에서
코드:
char *str = "string";
라고 하는건... 정확히 표현해서
코드:
const char *str = "string";
이지 않습니까? 즉... 고칠 수 없는 문자열 포인터죠...

좀 더 정확히 얘기하자면

const char _t[] = "string";
char *str = _t;

가 되겠죠.

질문하신 분의 문제는 컴파일러 문제가 아닙니다...플랫폼의 문제입니다. 즉, 실행 환경에서 상수 문자열이 저장되는 메모리 공간이 readonly이기 때문에 발생한 에러입니다. 말씀하신 것과 같이 const type에 의한 문제하고는 약간의 차이가 있습니다. 제시하신 예처럼 const char [] 의 경우라면 단순히 type casting을 하게 되면 얼마든지 문자열의 내용을 바꿀 수 있지만 질문하신 분의 경우는 type casting으로 해결할 수 없습니다. 왜냐하면 문자열이 위치한 메모리 자체가 쓰기 금지 영역이기 때문입니다...상수 문자열의 포인터를 const char* 로 하는 이유는 바로 이러한 런타임 오류를 막기 위한 문법적인 조치라고 이해하는 것이 더 좋다고 생각합니다...

------------------------
http://agbird.egloos.com

doldori의 이미지

Agbird wrote:
질문하신 분의 문제는 컴파일러 문제가 아닙니다...플랫폼의 문제입니다. 즉, 실행 환경에서 상수 문자열이 저장되는 메모리 공간이 readonly이기 때문에 발생한 에러입니다. 말씀하신 것과 같이 const type에 의한 문제하고는 약간의 차이가 있습니다.

누구(또는 어느 것)의 문제냐고 굳이 따진다면 저는 컴파일러도 아니고 플랫폼도
아니고 원래 코드 자체라고 하겠습니다. 추상적인 언어 정의가 어떻게 구현되는지
아는 것도 좋겠지만, 그 이전에 언어의 정의를 올바르게 이해하는 것이 우선이라고
생각합니다. C 또는 C++에서 문자열 상수를 변경하는 결과는 정의되지 않는다
(undefined)라고 규정하고 있습니다. 이 경우 구현체는 나름대로 합당하다고
생각하는 행동을 선택할 수 있습니다. 바로 seg fault를 낼 수도 있고, 변경하려는
행위를 무시할 수도 있고, 실제로 문자열을 변경할 수도 있습니다. 물론 이렇게
정의되지 않는 결과를 유발하는 코드는 잘못된 것입니다.

Agbird wrote:
제시하신 예처럼 const char [] 의 경우라면 단순히 type casting을 하게 되면 얼마든지 문자열의 내용을 바꿀 수 있지만 질문하신 분의 경우는 type casting으로 해결할 수 없습니다.

const char[]를 캐스팅을 통해 변경하는 결과 역시 정의되지 않습니다.
const char s[] = "C++";
char* p = const_cast<char*>(s);
*p = '\0';  // undefined!

즉 const로 정의된 개체를 변경하는 *모든* 행위는 잘못입니다.

Agbird wrote:
왜냐하면 문자열이 위치한 메모리 자체가 쓰기 금지 영역이기 때문입니다...상수 문자열의 포인터를 const char* 로 하는 이유는 바로 이러한 런타임 오류를 막기 위한 문법적인 조치라고 이해하는 것이 더 좋다고 생각합니다...

저는 const char*로 하는 이유는 정의되지 않은 결과를 유발하지 않도록 하기 위한
조치라고 이해하는 것이 더 좋다고 생각합니다.
죠커의 이미지

Agbird wrote:
질문하신 분의 문제는 컴파일러 문제가 아닙니다...플랫폼의 문제입니다.

doldori 말씀 처럼 코드가 문제인 것입니다.

많은 사람들이 구현 자체에 집중하고 있어서 문법적으로 틀린 코드에 대해서 관심을 가지고 있지 않군요.

안되어야 하는 이유는 논리적으로 잘못되었기 때문이고 그게 강제성이 없기 때문에 일부 컴파일러/인터프리터에서 제대로 작동이 되는 것입니다.

coathanger의 이미지

잘 배워 갑니다.

댓글 달기

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