정적 로컬 변수로 포인터를 반환하는 것은 안전합니까?
정적 로컬 변수에 포인터를 반환하는 관용어를 널리 사용하는 코드를 사용하고 있습니다.예:
char* const GetString()
{
static char sTest[5];
strcpy(sTest, "Test");
return sTest;
}
이게 안전하다고 생각하는 게 맞나요?
PS, 이 방법이 같은 작업을 수행하는 더 나은 방법이라는 것을 알고 있습니다.
char* const GetString()
{
return "Test";
}
편집: 죄송합니다. 함수 서명은 당연히 다음과 같습니다.
const char* GetString();
첫 번째 예: 다소 안전함
char* const GetString()
{
static char sTest[5];
strcpy(sTest, "Test");
return sTest;
}
이는 권장되지 않지만 안전합니다. 정적 변수의 범위는 함수의 범위가 종료되어도 활성 상태로 유지됩니다.이 기능은 스레드 세이프가 전혀 아닙니다.이 있다면, ''을 할 수 것입니다.char* buffer
a. a. a.maxsize
★★★★★★★★★★★★★★★★의 경우GetString()
함수가 채워집니다.
특히, 재진입 함수는 무엇보다도 정적(글로벌) 비정수 데이터에 주소를 반환해서는 안 되기 때문에 이 함수는 재진입 함수로 간주되지 않습니다.재진입 기능을 참조해 주세요.
두 번째 예: 전혀 안전하지 않음
char* const GetString()
{
return "Test";
}
방법은, 「이렇게 하면 안전합니다」라고 하는 합니다.const char *
할 수 수정의되지 않은 결과가 발생하기 입니다.그 이유는 문자열 리터럴을 읽기 전용 메모리 세그먼트에 저장할 수 있으며 이를 수정하면 정의되지 않은 결과가 발생하기 때문입니다.
char* const
pointer수 const char *
const는 이 수
결론:
다음 중 하나를 고려해야 합니다.
할 수 는, 1) 「코드의 을 실시합니다.GetString
변수를 char* buffer
채우다maxsize
사용할 수 있습니다.
2) 코드에 액세스 할 수 없지만, 호출할 필요가 있는 경우는, 이 메서드를 뮤텍스로 보호되고 있는 다른 함수로 랩 합니다.새로운 방법은 1과 같습니다.
static
변수(함수 내)는 범위 전역 변수와 같습니다.일반적으로 이러한 기능은 피해야 하지만(글로벌 변수와 마찬가지로 재진입 문제가 발생하지만), 경우에 따라서는 도움이 됩니다(일부 표준 라이브러리 기능에서 사용됩니다).할 수 할 수 .static
변수도 마찬가지입니다.
안전하다는 게 무슨 의미냐에 따라 다르죠.즉시 알 수 있는 문제가 몇 가지 있습니다.
char * const
발신자가 이 로케이션에서 문자열을 변경할 수 있습니다., 그게 아니라...const char *
?- 재진입 또는 동시성에 문제가 있을 수 있습니다.
두 번째를 설명하기 위해 다음 사항을 고려합니다.
const char * const format_error_message(int err)
{
static char error_message[MAXLEN_ERROR_MESSAGE];
sprintf(error_message, "Error %#x occurred", err);
return error_message;
}
이렇게 부르면:
int a = do_something();
int b = do_something_else();
if (a != 0 && b != 0)
{
fprintf(stderr,
"do_something failed (%s) AND do_something_else failed (%s)\n",
format_error_message(a), format_error_message(b));
}
...무엇이 인쇄될 예정입니까?
스레드도 마찬가지입니다.
기본적으로는 정적이기 때문에 값이 무기한 유지된다는 점에서 안전합니다.
상수 데이터에 대한 변수 포인터가 아니라 변수 데이터에 대한 상수 포인터를 반환했다는 점에서 안전하지 않습니다.호출 함수에 의한 데이터 변경이 허가되지 않은 경우는, 다음과 같이 하는 것이 좋습니다.
const char *GetString(void)
{
static char sTest[5];
strncpy(sTest, "Test", sizeof(sTest)-1);
sTest[sizeof(sTest)-1] = '\0';
return sTest;
}
표시된 간단한 경우에서는 버퍼 오버플로우를 걱정할 필요가 거의 없지만 내 버전의 코드는 걱정하며 늘 종료를 보장합니다.TR24731 기능을 사용할 수도 있습니다.strcpy_s
대신:
const char *GetString(void)
{
static char sTest[5];
strcpy_s(sTest, sizeof(sTest), "Test");
return sTest;
}
더 중요한 것은 두 변형 모두 일정한 데이터에 대한 (변수) 포인터를 반환하기 때문에 사용자는 문자열을 수정하거나 어레이의 범위 밖에서 (아마도) 짓밟지 않아야 합니다.(@strager가 코멘트에서 지적했듯이const char *
사용자가 반환된 데이터를 수정하려고 하지 않는다는 보장은 없습니다.단, 반환된 포인터를 일정하지 않게 캐스팅한 후 데이터를 수정해야 합니다.이는 정의되지 않은 동작을 호출하고 그 시점에서 모든 것이 가능합니다.)
리터럴 리턴의 장점 중 하나는 보통 쓰기 금지 약속을 컴파일러와 운영체제에 의해 강제할 수 있다는 것입니다.문자열은 프로그램의 텍스트(코드) 세그먼트에 배치되며 사용자가 반환값에 의해 지적된 데이터를 수정하려고 하면 운영체제는 장애(Unix에서의 세그먼트 위반)를 생성합니다.
[다른 답변 중 적어도 하나는 코드가 다시 입력되지 않았음을 나타냅니다. 정답입니다.리터럴을 반환하는 버전이 다시 입력됩니다.재진입이 중요한 경우 발신자가 데이터를 저장하는 공간을 제공하도록 인터페이스를 수정해야 합니다.]
네, 완벽하게 안전합니다.로컬 스태틱의 수명은 C의 전체 프로그램 실행 수명입니다.즉, 함수가 반환된 후에도 어레이가 활성화되고 반환된 포인터가 효과적으로 참조 해제될 수 있으므로 포인터를 반환할 수 있습니다.
이 기능은 printf 파라미터로 직접 사용할 수 있기 때문에 매우 편리합니다.단, 전술한 바와 같이 1개의 콜 내의 함수에 대해 여러 개의 콜을 하면 문제가 발생합니다.이는 함수가 같은 스토리지를 사용하고 두 번 호출하면 반환된 문자열이 덮어쓰기되기 때문입니다.단, 이 코드를 테스트해 본 결과, 동작하고 있는 것 같습니다.Givemestring이 MAX_CALLs의 최대 횟수에 사용되고 있는 함수를 안전하게 호출할 수 있습니다.
#define MAX_CALLS 3
#define MAX_LEN 30
char *givemestring(int num)
{
static char buf[MAX_CALLS][MAX_LEN];
static int rotate=0;
rotate++;
rotate%=sizeof(buf)/sizeof(buf[0]);
sprintf(buf[rotate],"%d",num);
return buf[rotate];
}
유일한 문제는 스레드 안전이지만 이는 스레드 로컬 변수(gcc의 __thread 키워드)로 해결할 수 있습니다.
예, 이것은 일부 검색의 텍스트 부분을 반환하기 위해 자주 사용됩니다. 즉, 일부 오류 번호를 인간 친화적인 문자열로 변환하기 위해 사용됩니다.
다음과 같은 경우에 이 작업을 수행하는 것이 좋습니다.
fprintf(stderr, "Error was %s\n", my_string_to_error(error_code));
ifmy_string_to_error()
는 할당된 문자열을 반환했습니다.이러한 함수의 일반적인 사용법을 생각하면, 프로그램은 누설됩니다.
char const *foo_error(...)
{
return "Mary Poppins";
}
...도 괜찮습니다.뇌사 컴파일러 중에는 캐스팅을 원하는 사람도 있습니다.
이런 식으로 줄을 보고 책을 반납하지 마세요:)
언급URL : https://stackoverflow.com/questions/453696/is-returning-a-pointer-to-a-static-local-variable-safe
'programing' 카테고리의 다른 글
Eclipse가 "Java was started but exit code = 1" 오류 메시지를 반환합니다. (0) | 2022.08.10 |
---|---|
vue typescript 클래스 구성 요소와 vueraggable (0) | 2022.08.10 |
글로벌 변수는 더 빠른 코드를 의미합니까? (0) | 2022.08.10 |
같은 패키지에 roxygen2와 doxygen을 사용하고 있습니까? (0) | 2022.08.09 |
VueJS 2 + TypeScript: 계산된 값이 데이터에 의해 정의된 속성을 감지하지 않음 (0) | 2022.08.09 |