C에서 문자열 리터럴의 "수명"


84

다음 함수에서 반환 된 포인터에 액세스 할 수 없습니까?

char *foo(int rc)
{
    switch (rc)
    {
        case 1:

            return("one");

        case 2:

            return("two");

        default:

            return("whatever");
    }
}

따라서 C / C ++에서 지역 변수의 수명은 실제로 함수 내에서만 가능합니다. 즉, char* foo(int)종료 후 반환되는 포인터는 더 이상 아무것도 의미하지 않습니다.

지역 변수의 수명에 대해 약간 혼란 스럽습니다. 좋은 설명이란 무엇입니까?


10
함수에있는 유일한 "var"는 매개 변수 int rc입니다. 그 수명은 return-s 각각에서 끝납니다 . 반환하는 포인터는 문자열 리터럴에 대한 것입니다. 문자열 리터럴에는 정적 저장 기간이 있습니다. 수명은 적어도 프로그램의 수명과 같습니다.
Kaz

14
@PedroAlves 왜 안돼? 메서드는 추상화를 허용합니다. 앞으로 번역 리소스에서 문자열을 읽을 수 있지만 제품의 V1 (또는 V0.5)에 대한 국제화 지원이 필요하지 않으면 어떻게됩니까?
2013 년

1
@PedroAlves는 "코드는 작품 있는지 것이다 (그리고 시도가 컴파일 할 경우 당신은 그것을 볼 수 있습니다)," 즉 따르지 않습니다. 많은 (대부분? 본질적으로 모든?) c 컴파일러는 불법 코드를 사용하고 종종 작동하는 것처럼 보이는 코드를 내 보냅니다. 그러나 다른 컴파일러 (또는 동일한 컴파일러의 다른 버전) 에서 시도하면 넘어 질 수 있습니다.
dmckee --- 전 중재자 새끼 고양이

6
@PedroAlves, 단일 상수 문자열을 반환하는 함수는 사용이 제한 될 수 있지만 입력 또는 개체 상태에 따라 여러 상수 문자열 중 하나를 반환하는 함수는 어떻습니까? 간단한 예는 열거 형을 문자열 표현으로 변환하는 함수입니다.
Mark Ransom 2013 년

4
당신은 strerror분명히 그 기능을 본 적이 없습니다 .
Kaz 2013 년

답변:


86

예, 지역 변수의 수명 은 생성 된 범위 ( {, }) 내에 있습니다.

로컬 변수에는 자동 또는 로컬 저장소가 있습니다. 자동은 그들이 자동으로이 끝을 생성하는 내 범위를 한 번 파괴하기 때문이다.

그러나 여기에있는 것은 구현 정의 읽기 전용 메모리에 할당 된 문자열 리터럴입니다. 문자열 리터럴은 지역 변수와 다르며 프로그램 수명 내내 살아 있습니다. 그들은 정적 기간 [참조 1] 수명을 갖습니다 .

주의 사항!

그러나 문자열 리터럴의 내용을 수정하려는 시도는 정의되지 않은 동작 (UB)입니다. 사용자 프로그램은 문자열 리터럴의 내용을 수정할 수 없습니다.
따라서 항상 const문자열 리터럴을 선언하는 동안 사용하는 것이 좋습니다 .

const char*p = "string"; 

대신에,

char*p = "string";    

사실, C ++에서는 C에서는 const아니지만 문자열 리터럴을 선언하는 것은 더 이상 사용되지 않습니다 . 그러나 a를 사용하여 문자열 리터럴을 선언하면 const컴파일러가 일반적으로 문자열 리터럴을 수정하려고 할 때 경고를 표시하는 이점이 있습니다. 두 번째 경우.

샘플 프로그램 :

#include<string.h> 
int main() 
{ 
    char *str1 = "string Literal"; 
    const char *str2 = "string Literal"; 
    char source[]="Sample string"; 
 
    strcpy(str1,source);    // No warning or error just Uundefined Behavior 
    strcpy(str2,source);    // Compiler issues a warning 
 
    return 0; 
} 

산출:

cc1 : 경고가 오류로 처리됨
prog.c : 함수 'main'에서 :
prog.c : 9 : 오류 : 'strcpy'의 인수 1을 전달하면 포인터 대상 유형에서 한정자가 삭제됩니다.

컴파일러는 두 번째 경우에 대해 경고하지만 첫 번째 경우에는 경고하지 않습니다.


여기에서 몇 명의 사용자가 묻는 질문에 답하려면 :

정수 리터럴은 무엇입니까?

즉, 다음 코드가 유효합니까?

int *foo()
{
    return &(2);
} 

대답은이 코드가 유효하지 않다는 것입니다. 형식이 잘못되어 컴파일러 오류가 발생합니다.

다음과 같은 것 :

prog.c:3: error: lvalue required as unary ‘&’ operand
     

문자열 리터럴은 l- 값입니다. 즉 : 문자열 리터럴의 주소를 사용할 수 있지만 내용을 변경할 수는 없습니다.
그러나, 다른 리터럴 ( int, float, char등) (R) - 값 (C 표준 용어 사용되어 식의 값 과 자신의 주소를 전혀 수행 할 수없는 이러한을위한).


[참조 1] C99 표준 6.4.5 / 5 "문자열 리터럴-의미 체계":

변환 단계 7에서는 문자열 리터럴 또는 리터럴의 결과 인 각 멀티 바이트 문자 시퀀스에 값이 0 인 바이트 또는 코드가 추가됩니다. 그런 다음 멀티 바이트 문자 시퀀스를 사용하여 시퀀스를 포함하기에 충분한 정적 저장 기간 및 길이 배열을 초기화합니다 . 문자열 리터럴의 경우 배열 요소는 char 유형을 가지며 멀티 바이트 문자 시퀀스의 개별 바이트로 초기화됩니다. 와이드 문자열 리터럴의 경우 배열 요소는 wchar_t 유형을 가지며 와이드 문자 시퀀스로 초기화됩니다.

요소에 적절한 값이있는 경우 이러한 배열이 구별되는지 여부는 지정되지 않습니다. 프로그램이 이러한 배열을 수정하려고하면 동작이 정의되지 않습니다 .


사용자가 이와 같은 것을 반환하면 어떻게 될까요? char * a = & "abc"; 반환 a; 이것은 유효하지 않습니까?
Ashwin

@Ashwin : 문자열 리터럴의 유형은입니다 char (*)[4]. 유형 때문이다 "ABC"는 이다 char[4]과 같은 4 개 문자의 배열에 대한 포인터를 선언 char (*)[4]당신이 그것의 주소를 가지고해야하는 경우 그래서, 당신은 그것을 할 필요가, char (*a)[4] = &"abc";그리고 예, 그것은 유효합니다.
Alok Save

@Als "abc"는 char[4]입니다. (왜냐하면의 '\0')
asaelr

1
어쩌면 또한 그에게 경고를하는 것이 좋습니다 것입니다 char const s[] = "text";않습니다 하지 수 있도록 s문자 리터럴을, 따라서 s 그 어떤 생존 포인터가 매달려 있도록, 범위의 끝에서 파괴 될 수있다.
celtschk

1
@celtschk :하고 싶지만 Q는 특히 문자열 리터럴에 관한 것이므로 당면한 주제를 고수 할 것입니다. 그러나 여기 내 대답에 관심이있는 사람들을 위해 char a [] = "string"과 char의 차이점은 무엇입니까? * p = "문자열"? 오히려 도움이 될 것입니다.
Alok Save

74

유효합니다. 문자열 리터럴에는 정적 저장 기간이 있으므로 포인터가 매달려 있지 않습니다.

C의 경우 섹션 6.4.5, 단락 6에 명시되어 있습니다.

변환 단계 7에서는 문자열 리터럴 또는 리터럴의 결과 인 각 멀티 바이트 문자 시퀀스에 값이 0 인 바이트 또는 코드가 추가됩니다. 그런 다음 멀티 바이트 문자 시퀀스를 사용 하여 시퀀스를 포함하기에 충분한 정적 저장 기간 및 길이 배열을 초기화합니다 .

섹션 2.14.5, 단락 8-11의 C ++의 경우 :

8 일반 문자열 리터럴 및 UTF-8 문자열 리터럴은 좁은 문자열 리터럴이라고도합니다. 좁은 문자열 리터럴에는 "n 배열"유형이 있습니다 const char. 여기서 n은 아래 정의 된 문자열의 크기이며 정적 저장 기간 (3.7)을 갖습니다.

9 u로 시작 u"asdf"하는 char16_t문자열 리터럴 (예 :) 은 문자열 리터럴입니다. char16_t문자열 리터럴 유형 '(n)의 배열을 갖는다 const char16_t", 여기서 n은 아래에서 정의 된 스트링의 크기; 정적 저장 기간이 있으며 주어진 문자로 초기화됩니다. 단일 c-char는 char16_t서로 게이트 쌍의 형태로 둘 이상의 문자를 생성 할 수 있습니다 .

10 U로 시작 U"asdf"하는 char32_t문자열 리터럴 (예 :) 은 문자열 리터럴입니다. char32_t문자열 리터럴 유형 '(n)의 배열을 갖는다 const char32_t", 여기서 n은 아래에서 정의 된 스트링의 크기; 정적 저장 기간이 있으며 주어진 문자로 초기화됩니다.

11 L로 시작하는 문자열 리터럴 (예 L"asdf":)은 와이드 문자열 리터럴입니다. 와이드 문자열 리터럴은 "n 배열"유형을 가지며 const wchar_t, 여기서 n은 아래 정의 된 문자열의 크기입니다. 정적 저장 기간이 있으며 주어진 문자로 초기화됩니다.



14

문자열 리터럴은 전체 프로그램에 유효하므로 (스택이 아닌 할당되지 않음) 유효합니다.

또한, 문자열 리터럴은 읽기 전용이므로 (좋은 스타일) 어쩌면 당신은 변경해야합니다 fooconst char *foo(int)


사용자가 이와 같은 것을 반환하면 어떻게 될까요? char * a = & "abc"; 반환 a; 이것은 유효하지 않습니까?
Ashwin

&"abc"이 아닙니다 char*. 배열의 주소이고 유형은 char(*)[4]입니다. 그러나, 중 return &"abc";char *a="abc";return a;유효합니다.
asaelr

@asaelr : 사실, 좋은 스타일위한 것 이상 입니다. 자세한 내용은 내 대답을 확인하십시오.
Alok Save

@Als 글쎄, 그가 전체 프로그램을 작성한다면, 그는 작성하지 않고 문자열을 변경하는 것을 피할 수 const있으며 완전히 합법적이지만 여전히 나쁜 스타일입니다.
asaelr

전체 프로그램에 유효하다면 왜 malloc을해야합니까?
TomSawyer

7

예, 유효한 코드입니다. 아래 사례 1을 참조하십시오. 최소한 다음과 같은 방법으로 함수에서 C 문자열을 안전하게 반환 할 수 있습니다.

  • const char*문자열 리터럴에. 수정할 수 없으며 호출자가 해제해서는 안됩니다. 아래에 설명 된 해제 문제로 인해 기본값을 반환하는 용도로는 거의 유용하지 않습니다. 실제로 어딘가에 함수 포인터를 전달해야하므로 문자열을 반환하는 함수가 필요합니다.

  • char*또는 const char*정적 char 버퍼에. 호출자가 해제해서는 안됩니다. 수정 될 수 있지만 (const가 아닌 경우 호출자 또는이를 반환하는 함수에 의해)이를 반환하는 함수는 (쉽게) 여러 버퍼를 가질 수 없으므로 (쉽게) 스레드 안전하지 않으며 호출자에게 필요할 수 있습니다. 함수를 다시 호출하기 전에 반환 된 값을 복사합니다.

  • char*로 할당 된 버퍼에 malloc. 수정할 수 있지만 일반적으로 호출자에 의해 명시 적으로 해제되어야하며 힙 할당 오버 헤드가 있습니다. strdup이 유형입니다.

  • const char*또는 char*함수에 인수로 전달 된 버퍼로 전달됩니다 (반환 된 포인터는 인수 버퍼의 첫 번째 요소를 가리킬 필요가 없습니다). 버퍼 / 메모리 관리 책임은 호출자에게 맡깁니다. 많은 표준 문자열 함수가이 유형입니다.

한 가지 문제는 이들을 하나의 함수로 혼합하면 복잡해질 수 있다는 것입니다. 호출자는 반환 된 포인터를 처리하는 방법, 유효 기간 및 호출자가 해제해야하는지 여부를 알아야하며 런타임에이를 결정할 수있는 (좋은) 방법이 없습니다. 따라서 예를 들어 호출자가 필요로하는 힙 할당 버퍼에 대한 포인터를 반환하는 함수를 가질 수 없으며, free호출자가해서는 안되는 문자열 리터럴에서 기본값에 대한 포인터를 반환하는 경우도 free있습니다.



6

좋은 질문. 일반적으로 귀하가 옳지 만 귀하의 예는 예외입니다. 컴파일러는 문자열 리터럴에 대해 전역 메모리를 정적으로 할당합니다. 따라서 함수에서 반환 된 주소가 유효합니다.

이것이 C의 다소 편리한 기능이 아닌가? 이것은 프로그래머가 메시지가 저장되는 메모리에 대해 걱정할 필요없이 미리 작성된 메시지를 반환 할 수 있도록합니다.

@asaelr의 올바른 관찰 re를 참조하십시오 const.


: 사용자가 이와 같은 것을 반환하면 어떨까요? char * a = & "abc"; 반환 a; 이것은 유효하지 않습니까?
Ashwin

권리. 사실, 하나는 쓸 수 const char *a = "abc";를 생략 &. 그 이유는 큰 따옴표로 묶인 문자열이 초기 문자의 주소로 확인되기 때문입니다.
thb

3

지역 변수는 선언 된 범위 내에서만 유효하지만 해당 함수에서 지역 변수를 선언하지 않습니다.

함수에서 문자열 리터럴에 대한 포인터를 반환하는 것은 완벽하게 유효합니다. 문자열 리터럴은 static또는 전역 변수 처럼 프로그램의 전체 실행에 걸쳐 존재하기 때문 입니다.

당신이하고있는 일이 정의되지 않은 유효하지 않을지 걱정된다면, 당신이 잘못하고있는 것이 실제로 있는지 확인하기 위해 컴파일러 경고를 올려야합니다.


사용자가 이와 같은 것을 반환하면 어떻게 될까요? char * a = & "abc"; 반환 a; 이것은 유효하지 않습니까?
Ashwin 2012

@Ashwin : &"abc"유형이 아닌 char*그러나 모두, "abc"그리고 &"abc"프로그램의 전체 실행에 걸쳐 유효합니다.
AusCBloke 2012

2

str고정 주소를 가리 키기 때문에 매달린 포인터가 될 수 없습니다.문자열 리터럴이 .

로드 될 때 대부분 읽기 전용 이며 프로그램에 전역 적 입니다.

해제 또는 수정을 시도하더라도 메모리 보호 기능이있는 플랫폼 에서 세분화 오류가 발생 합니다 .



매달리지 않을 경우 malloc을해야합니까? 아니?
TomSawyer

0

스택에 지역 변수가 할당됩니다. 함수가 완료된 후 변수는 범위를 벗어나 더 이상 코드에서 액세스 할 수 없습니다. 그러나 해당 변수를 가리 키도록 할당 한 전역 (또는 단순히-아직 범위를 벗어나지 않은) 포인터가있는 경우 해당 변수가 있던 스택의 위치를 ​​가리 킵니다. 다른 함수에서 사용하는 값이거나 의미없는 값일 수 있습니다.


사용자가 이와 같은 것을 반환하면 어떻게 될까요? char * a = & "abc"; 반환 a; 이것은 유효하지 않습니까?
Ashwin 2012

0

위의 예에서 실제로 위를 호출하는 함수에 할당 된 포인터를 반환합니다. 따라서 로컬 포인터가되지 않습니다. 또한 반환해야하는 포인터의 경우 전역 세그먼트에 메모리가 할당됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.