char* str 과 char str[] 과의 차이점이 무엇입니까?

ultraman의 이미지

char* str = "abcd";
str[1] = 'x';

위와 같은 경우 Violation 에러가 나며, - 이게 에러나는 줄 처음(?)알았습니다. :roll:

char str[] = "abcd";
str[1] = 'x';

는 당연하게도 성공합니다.

단순히 문자배열과 char* 가 동일하다고 생각하고 있었는데...

위의 결과를 보면 완전히 다르군요.

이에 대한 차이점을 알고 싶습니다.

미리 답변에 감사드립니다. :D

cinsk의 이미지

C FAQ 6.2를 참고하기 바랍니다. 영문판은 여기에 있습니다.

doldori의 이미지

첨언하면 "abcd"는 변경이 불가능한 문자열 상수(string literal)입니다. 그런데
char*가 이 문자열을 가리키게 하면 그 포인터를 통해 문자열을 바꾸는 실수를
해도 컴파일러는 아무런 에러 메시지를 내지 않지요. 따라서

const char* str = "abcd";
str[1] = 'x';   // compilation error

와 같이 const로 한정하여 쓰는 것이 안전합니다.
ultraman의 이미지

두분 답변에 감사드립니다.

덕분에 좋은 사이트도 알게되고, 통찰도 얻었습니다. :D

두분 답변을 토대로 정리하자면,

Quote:
"abcd"가 상수이며, str은 이 상수의 주소를 가르키는 포인터일 뿐이다.
따라서, str[1]은 str + 1 주소를 지칭하며 이는 '상수' 값을 가르키는 포인터 이므로 (변수가 아니다) 값을 새로 할당할 수 없다.
반대로, 배열인 경우는 변수할당에 해당하므로 새로운 값을 대입할 수 있다.

가 되지 않을까 합니다.

IsExist의 이미지

개발 시스템마다 틀립니다. 정확히는 컴파일러(링커)마다 다르다고
해야 합니다.

"abcd" 문자열이 .text 영역에 지정될 수 있고. .rodata 혹은 .data
영역에 지정될 수도 있습니다. objdump 나 elfdump 등으로
볼 수 있습니다.

#include <stdio.h>

int
main(void)
{
        char *a = "abcd";

        printf ("%s,%p,%x\n", a, a, a);
        a[0] = 'b';
        printf ("%s,%p,%x\n", a, a, a);
        return 0;
}

위 코드를 SunOS 5.8, Sun Workshop 6 Update 1 C 5.2 로
컴파일 했을때와(이 경우에는 .data 영역에 지정), gcc 3.3.3 으로
했을때(.rodata 에 지정) 다릅니다.

---------
간디가 말한 우리를 파괴시키는 7가지 요소

첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스

이익추구를 위해서라면..

다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치

doldori의 이미지

IsExist wrote:
개발 시스템마다 틀립니다. 정확히는 컴파일러(링커)마다 틀리다고
해야 합니다.

그런 것을 C에서는 "결과는 정의되지 않는다"고 말합니다.
char* str = "abcd"; 
str[1] = 'x'; 

이 코드를 실행한 결과가 어떻게 될 것인가에 대해서 표준은 어떤 보장도 하지 않고,
각 구현체에서 알아서 하라는 것이죠. 그 결과가 구현체마다 다른 것은 당연합니다.
C가 이식성을 추구하는 언어이고 의도한 결과를 얻을 수 있는 잘 정의된 방법을
제공하고 있기 때문에 이 코드는 잘못 되었다고 말하는 것입니다.
익명 사용자의 이미지

Quote:
두분 답변을 토대로 정리하자면,

인용:
"abcd"가 상수이며, str은 이 상수의 주소를 가르키는 포인터일 뿐이다.
따라서, str[1]은 str + 1 주소를 지칭하며 이는 '상수' 값을 가르키는 포인터 이므로 (변수가 아니다) 값을 새로 할당할 수 없다.
반대로, 배열인 경우는 변수할당에 해당하므로 새로운 값을 대입할 수 있다.

가 되지 않을까 합니다.

틀렸습니다. str[1] = 'x'; 라는 수식 자체의 의미는, 위의 코드나 아래의 코드나 서로 다르지 않습니다.

// 코드1:
char *str = "abcd";
str[1] = 'b';

//코드 2:
char str[] = "abcd";
str[1] = 'b';

일단 이런 코드가 있다고 칩시다.

Quote:
str[1]은 str + 1 주소를 지칭하며

str[1]은 코드 1이건 코드2이건 간에 *(str +1)이며 이것은 str +1 주소에 저장된 char형 그 자체를 뜻합니다. 코드 1과 코드 2의 차이는, str + 1이 가리키는 주소의 공간이 수정될 수 있는가 없는가 하는 점이며, 이때문에 코드 1에서는 문제가 발생하는 것입니다.

Quote:
이는 '상수' 값을 가르키는 포인터 이므로 (변수가 아니다) 값을 새로 할당할 수 없다.

표준에서는 단지 문자열 상수(영어로 쓰면 string literal 이므로 '상수'라는 표현은 부정확하다고 볼 수도 있습니다. 토큰 상의 구분으로 볼때 constant와 string literal은 서로 별개입니다)를 수정할 때의 결과는 정의되지 않았다고 표현합니다. 새로 할당한다는 것도 오해의 여지가 있는 표현인데, 새로운 메모리가 할당되는 것이 아니라 기존의 이미 존재하는 공간의 데이터를 수정하는 행동이기 때문입니다.

코드 1과 코드 2의 차이점을 알기 위해서는 다음 사항들을 이해하고 있어야 합니다. 편의상 규칙 1,2,3,4라 이름을 붙이겠습니다.

1. 문자열 상수는 수정불가능한 char형 배열이다.
2. 배열은 그 자신의 첫번째 원소를 가리키는 포인터 주소값으로 자동 변환된다.
3. 2번에는 3가지 예외가 있는데, &연산자의 피연산자, sizeof 연산자의 피연산자, 그리고 char형 배열을 초기화할 때 쓰였을 경우이다.
4. array[2] 는 *(array+2)와 완전히 동일한 표현이다.

한가지 설명을 돕기 위한 예를 들어보이겠습니다.

char *str;
str = "abcd";

위의 코드는 정상일까요? 정상입니다. str은 char형 배열 "abcd"의 첫번째 원소를 가리키는 주소값을 저장하게 됩니다.

char c;
c = "abcd"[2]; // 변수 c에는 'c'가 대입된다.

위의 코드에서 "abcd"[2]는 *("abcd" + 2) 와 같은 뜻입니다. 그런데 "abcd"는 배열이며(규칙1), 그 자신의 첫번째 원소를 가리키는 포인터 주소값으로 자동 변환됩니다. 따라서 "abcd"[2]의 최종 결과값은 "abcd"란 배열의 세번째 원소(인덱스는 0이 첫번째이므로)인 'c'가 됩니다.

그럼 이제 코드 1과 2의 차이점에 대해 알아보겠습니다.

// 코드1:
char *str = "abcd";
str[1] = 'b';

코드 1에서 char *str="abcd"; 의 의미는 char*형 변수 str에 문자열 상수(즉 배열) "abcd"의 첫번째 원소의 주소값을 기억하라는 뜻입니다(규칙2).

//코드 2:
char str[] = "abcd";
str[1] = 'b';

코드 2에서의 "abcd"는 코드 1에서의 "abcd"하고는 의미가 다르며, 일반적인 규칙과는 다른 규칙을 따릅니다(규칙 3을 상기하십시오). 문자열 상수가 char형 배열의 초기화에 쓰일 경우에는, 문자열 상수는 배열로 작용하는 것이 아니라 char형 배열을 초기화 하는데 쓰입니다. 따라서 코드 2에서는 크기 5의 char형 배열 str을 만들어서, 그 안에 'a', 'b', 'c', 'd', '\0' 이라는 내용을 저장하게 됩니다.

코드 1에서 문제가 생기는 이유는, C에서는 문자열 상수가 수정이 불가능(실제로는, 수정을 시도했을 시 그 결과는 정의되어 있지 않음)하기 때문이고, 코드 2에서 문제가 생기지 않는 이유는, char형 배열은 수정이 가능하기 때문입니다. str[1]이 가리키는 공간의 성격에 대해 유의할 필요가 있습니다.

참고로, 문자열 상수에는 한가지 재미있는 특성이 있습니다.

char *s1 = "abc"; // (1)
char *s2 = "abc"; // (2)
char *s3 = "abc"; // (2)
printf("s1=%p, s2=%p, s3=%p", s1, s2, s3);

똑같은 내용의 문자열 상수라면, 컴파일러가 굳이 여러개의 배열을 만들지 않아도 됩니다. 즉, 위의 코드에서 출력되는 s1, s2, s3의 주소값이 동일할 수도 있습니다. 물론 아닐 수도 있고요.

댓글 달기

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