문자열 리터럴로 char []를 초기화하는 것이 좋지 않습니까?

응답 중 하나 에 따르면char배열을 초기화하는 것은 “어쨌든 [원문] 나쁜 습관입니다. with a string literal. “

이것이 사실입니까, 아니면 (“엘리트 회원 “이긴하지만) 그의 의견 일 뿐입니 까?


원래 질문은 다음과 같습니다.

p>

#include <stdio.h> #include<string.h> main() { char string[] = "october"; strcpy(string, "september"); printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string)); return 0; } 

맞습니다. 크기는 길이에 1을 더한 값이어야합니까?

이것은 출력

the size of september is 8 and the length is 9

크기는 반드시 10이어야합니다. strcpy에 의해 변경되기 전에 문자열의 크기를 계산하는 것과 비슷하지만 길이는

내 구문에 문제가 있습니까?


여기 답장 :

어쨌든 문자열 리터럴로 문자 배열을 초기화하는 것은 나쁜 습관입니다. 따라서 항상 다음 중 하나를 수행하십시오.

const char string1[] = "october"; char string2[20]; strcpy(string2, "september"); 

댓글

  • 첫 번째 줄의 ” const “에 유의하세요. 저자가 c 대신 C ++를 가정했을 수 있습니까? C ++에서는 ” 나쁜 습관 “입니다. 리터럴은 const 여야하고 최신 C ++ 컴파일러는 경고 (또는 오류)를 제공하기 때문입니다. const 리터럴을 상수가 아닌 배열에 할당하는 방법에 대해 설명합니다.
  • @Andr é C ++는 문자열 리터럴을 const 배열로 정의합니다. 이것이 유일한 안전한 처리 방법이기 때문입니다. 그들과 함께. C가 ‘ 안 하는 것이 문제이므로 안전한 것을 시행하는 사회적 규칙이 있습니다
  • @Caleth. 저는 답장의 작성자가 C ++ 관점에서 ” 나쁜 관행 “에 접근하고 있다고 더 많이 주장했습니다.
  • @Andr é ‘ C ++에서는 나쁜 습관이 아닙니다. isn ‘ 연습 , 그것은 ‘ 정확한 유형 오류입니다. C에서는 유형 오류가 되어야하지만 ‘ t가 아니므로 ‘ 금지됨 ”

답변

어쨌든 문자열 리터럴로 문자 배열을 초기화하는 것은 나쁜 습관입니다.

해당 의견의 작성자는 결코 그것을 정당화하지 않으며, 저는 그 진술이 당혹 스럽습니다.

C에서 (그리고 당신은 이것을 C로 태그했습니다), 그 ” 문자열 값으로 char 배열을 초기화 하는 유일한 방법입니다 (초기화는 할당과 다름). 둘 중 하나를 작성할 수 있습니다.

char string[] = "october"; 

또는

char string[8] = "october"; 

또는

char string[MAX_MONTH_LENGTH] = "october"; 

첫 번째 경우 배열의 크기는 초기화 프로그램의 크기에서 가져옵니다. 문자열 리터럴은 char의 배열로 저장됩니다. 0 바이트로 끝나므로 배열의 크기는 8 ( “o”, “c”, “t”, “o”, “b”, “e”, “r”, 0)입니다. 두 번째 두 경우에서 배열의 크기는 선언의 일부로 지정됩니다 (8 및 MAX_MONTH_LENGTH, 어떤 일이 발생하든).

할 수 수없는 일은 다음과 같이 작성하는 것입니다.

char string[]; string = "october"; 

또는

char string[8]; string = "october"; 

등 첫 번째 경우 string의 선언은 불완전 입니다. 배열 크기가 지정되지 않았고 크기를 가져올 이니셜 라이저도 없기 때문입니다. 경우, a) string와 같은 배열 표현식이 할당의 대상이 아닐 수 있고 b) 때문에 =가 작동하지 않습니다. = 연산자는 어쨌든 한 배열의 내용을 다른 배열로 복사하도록 정의되어 있지 않습니다.

동일한 토큰으로는 쓸 수 없습니다

char string[] = foo; 

여기서 foochar의 또 다른 배열입니다. 이 형식의 초기화는 문자열 리터럴에서만 작동합니다.

편집

초기화 할 수도 있다고 수정해야합니다.

char string[] = {"o", "c", "t", "o", "b", "e", "r", 0}; 

또는

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII 

그러나 문자열 리터럴을 사용하는 것이 눈에 더 쉽습니다.

편집 2

선언 외부에서 배열의 contents 를 할당하려면 strcpy/strncpy (0으로 끝나는 문자열의 경우) 또는 (다른 유형의 배열) :

if (sizeof string > strlen("october")) strcpy(string, "october"); 

또는

strncpy(string, "october", sizeof string); // only copies as many characters as will // fit in the target buffer; 0 terminator // may not be copied, but the buffer is // uselessly completely zeroed if the // string is shorter! 

댓글

  • strncpy는 거의 정답이 아닙니다.
  • @KeithThompson : 동의하지 않습니다. 단지 완전성을 위해 추가했습니다. ‘.
  • char[8] str = "october";는 나쁜 습관입니다. 나는 그것이 오버플로가 아닌지 확인하기 위해 문자 그대로 문자 수를 계산해야했고 ‘ 유지 보수 중에 중단됩니다. 맞춤법 오류를 seprate에서 separate로 수정하면 크기가 업데이트되지 않으면 깨집니다.
  • djechlin에 동의합니다. 주어진 이유 때문에 나쁜 습관입니다. JohnBode ‘의 답변은 ‘ ” 나쁜 관행에 대해 전혀 언급하지 않습니다. ” aspect (질문의 주요 부분입니다 !!), 배열을 초기화하기 위해 할 수있는 것과 할 수없는 것을 설명합니다.
  • 사소한 : iv로 strlen()에서 반환 된 id = “6c384c7da2”>

length ” 값에MAX_MONTH_LENGTHchar string[]에 필요한 최대 크기를 유지하는 데 종종 잘 보이지 않습니다 . IMO,MAX_MONTH_SIZE가 여기에 더 좋습니다.

답변

제가 기억하는 유일한 문제는 문자열 리터럴을 char *에 할당하는 것입니다.

char var1[] = "september"; var1[0] = "S"; // Ok - 10 element char array allocated on stack char const *var2 = "september"; var2[0] = "S"; // Compile time error - pointer to constant string char *var3 = "september"; var3[0] = "S"; // Modifying some memory - which may result in modifying... something or crash 

예를 들어 다음 프로그램을 사용하세요.

#include <stdio.h> int main() { char *var1 = "september"; char *var2 = "september"; var1[0] = "S"; printf("%s\n", var2); } 

내 플랫폼 (Linux)에서 읽기 전용으로 표시된 페이지에 쓰려고 할 때 충돌합니다. 다른 플랫폼에서는 “September”등을 인쇄 할 수 있습니다.

즉, 리터럴로 초기화하면 특정 예약 금액이 생성되므로 작동하지 않습니다.

char buf[] = "May"; strncpy(buf, "September", sizeof(buf)); // Result "Sep" 

하지만 이것은

char buf[32] = "May"; strncpy(buf, "September", sizeof(buf)); 

마지막으로 말하지만 strcpy 전혀 :

char buf[8]; strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory 

일부 컴파일러는이를 안전한 호출로 변경할 수 있지만 strncpy가 훨씬 안전합니다.

char buf[1024]; strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers. buf[sizeof(buf) - 1] = "\0"; 

댓글

  • 아직 ‘ 해당 strncpy에서 버퍼 오버런 위험은 ‘ something_elsesizeof(buf)보다 큽니다. 일반적으로 마지막 문자 buf[sizeof(buf)-1] = 0를 설정하여 보호하거나 buf가 0으로 초기화 된 경우 를 사본 길이로 사용합니다.
  • strlcpy 또는 strcpy_s 또는 필요한 경우
  • 수정되었습니다. 안타깝게도 최신 컴파일러 (strlcpysnprintf는 직접 액세스 할 수 없음)를 사용하지 않는 한 쉽게 이식 할 수있는 방법이 없습니다. MSVC에서는 최소한 주문과 strcpy_s는 * nix에 없습니다).
  • @MaciejPiechotka : 음, Unix가 Microsoft가 후원하는 부록 k를 거부했습니다. / li>

답변

주로 char[] 프로그램 내에서 쉽게 사용할 수있는 변수 / 구문입니다.

링크의 코드 샘플 :

 char string[] = "october"; strcpy(string, "september"); 

string는 스택에 7 자 또는 8 자 길이로 할당되어 있습니다.이 방법으로 null로 끝나는 지 여부를 기억할 수 없습니다. 링크 한 스레드에서 .

해당 문자열에 “9 월”을 복사하는 것은 명백한 메모리 오버런입니다.

string를 다른 함수에 전달하면 또 다른 문제가 발생합니다.그래서 다른 함수는 배열에 쓸 수 있습니다. 배열이 오버런을 일으키지 않도록 그것 이 얼마나 긴지 다른 함수에 알려야합니다. iv의 결과와 함께 string를 전달할 수 있습니다. id = “3296e2f67c”>

그러나 스레드는string가 null로 끝나지 않는 경우 이것이 어떻게 폭발 할 수 있는지 설명합니다.

당신은 더 나은 선택입니다. 고정 된 크기 (가급적이면 상수로 정의 됨)로 문자열을 할당 한 다음 배열과 고정 된 크기를 다른 함수에 전달합니다. @John Bode “의 의견은 정확하며 이러한 위험을 완화 할 수있는 방법이 있습니다. 또한 위험을 사용하려면 더 많은 노력이 필요합니다.

내 경험상, 내가 초기화 한 값은 char[] to는 일반적으로 거기에 배치해야하는 다른 값에 비해 너무 작습니다. 정의 된 상수를 사용하면이 문제를 방지하는 데 도움이됩니다.


sizeof string는 버퍼의 크기 (8 바이트)를 제공합니다. 메모리가 걱정되면 strlen 대신 해당 표현식의 결과를 사용하세요.
마찬가지로 strcpy 대상 버퍼가 소스 문자열 if (sizeof target > strlen(src)) { strcpy (target, src); }에 충분히 큰지 확인합니다.
예, 배열을 함수에 전달해야하는 경우 ” 물리적 크기도 전달해야합니다 : foo (array, sizeof array / sizeof *array);. – John Bode

댓글

  • sizeof string 버퍼 (8 바이트)의 크기를 제공합니다. 메모리가 걱정되는 경우 strlen 대신 해당 표현식의 결과를 사용하세요. ‘ 마찬가지로 strcpy를 호출하기 전에 대상 버퍼가 소스 문자열 if (sizeof target > strlen(src)) { strcpy (target, src); }에 비해 충분히 큰지 확인할 수 있습니다. 예, 배열을 함수에 전달해야하는 경우 ‘ 물리적 크기도 전달해야합니다 (foo (array, sizeof array / sizeof *array);).
  • @JohnBode-감사합니다. 좋은 점입니다. 귀하의 의견을 제 답변에 통합했습니다.
  • 더 정확하게는 배열 이름 string에 대한 대부분의 참조가 , 배열의 첫 번째 요소를 가리 킵니다. 이것은 배열 경계 정보를 잃습니다. 함수 호출은 이러한 상황이 발생하는 여러 컨텍스트 중 하나 일뿐입니다. char *ptr = string;는 또 다른 것입니다. string[0]도 이에 대한 예입니다. [] 연산자는 배열이 아닌 포인터에서 작동합니다. 추천 자료 : comp.lang.c FAQ 의 섹션 6
  • 마지막으로 실제로 질문과 관련된 답변입니다!

답변

두 스레드 모두 표시되지 않는 한 가지는 다음과 같습니다.

char whopping_great[8192] = "foo"; 

vs.

char whopping_great[8192]; memcpy(whopping_great, "foo", sizeof("foo")); 

전자는 다음과 같은 작업을 수행합니다.

memcpy(whopping_great, "foo", sizeof("foo")); memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo")); 

후자는 memcpy 만 수행합니다. C 표준은 배열의 일부가 초기화되면 모든 것이 초기화된다고 주장합니다. 따라서이 경우에는 스스로하는 것이 더 낫습니다. treuss가 그렇게했을 수도 있습니다.

확실히

char whopping_big[8192]; whopping_big[0] = 0; 

다음 중 하나보다 낫습니다.

char whopping_big[8192] = {0}; 

또는

char whopping_big[8192] = ""; 

ps 보너스 포인트는 다음과 같습니다.

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo")); 

배열을 오버플로하려는 경우 컴파일 시간을 0으로 나누기 오류를 발생시킵니다.

답변

“나쁜 관행”아이디어는 다음과 같은 형식이라는 사실에서 비롯된 것 같습니다.

char string[] = "october is a nice month"; 

소스 기계 코드에서 스택까지 암시 적으로 strcpy를 만듭니다.

해당 문자열에 대한 링크 만 처리하는 것이 더 효율적입니다. 다음과 같이 :

char *string = "october is a nice month"; 

또는 직접 :

strcpy(output, "october is a nice month"); 

(물론 대부분 코드는 아마도 중요하지 않습니다.)

댓글

  • ‘ 복사 만 만들지 않습니다. 수정하려고한다면 컴파일러가 그보다 더 똑똑 할 것이라고 생각합니다.
  • char time_buf[] = "00:00";와 같은 경우는 어떻습니까? ‘ 버퍼를 수정할 예정입니까? 문자열 리터럴로 초기화 된 char *는 첫 번째 바이트의 주소로 설정되므로 수정하려고하면 정의되지 않은 동작이 발생합니다. 문자열 리터럴 ‘의 저장 방법은 알 수 없지만 (구현 정의 됨) char[]의 바이트를 수정하는 것은 완벽하게 합법적입니다. 초기화는 스택에 할당 된 쓰기 가능한 공간에 바이트를 복사합니다. 즉, ‘의 ” 효율성이 낮습니다. 또는 ” 나쁜 습관 ” char* vs char[]는 오해의 소지가 있습니다.

Answer

정말 긴 시간은 아니지만 초기화 char [] “string”은 const char *이고 char *에 할당하기 때문입니다. 따라서이 char []를 데이터를 변경하는 메서드에 전달하면 흥미로운 동작을 할 수 있습니다.

내가 언급했듯이 약간 char []와 char *를 혼합했습니다. 약간 다르기 때문에 좋지 않습니다.

데이터를 char 배열에 할당하는 데 아무런 문제가 없습니다. 그러나이 배열을 사용하려는 의도는 “문자열”(char *)로 사용하는 것이므로 수정하지 말아야한다는 사실을 잊기 쉽습니다. array.

주석

  • 잘못되었습니다. 초기화는 문자열 리터럴의 내용을 배열로 복사합니다. 배열 객체는 t const.(그리고 C의 문자열 리터럴은 const가 아니지만 문자열 리터럴을 수정하려는 시도는 정의되지 않은 동작을 갖습니다.) char *s = "literal";에는 ‘ 당신이 말하는 행동의 종류; ‘는 const char *s = "literal";
  • ” 일반적으로 ” asdf “는 상수이므로 const로 선언해야합니다. ” -iv id = “811864f2cd”때문에 int n = 42;const가 필요한 이유는 동일합니다. >

는 상수입니다.

  • 어떤 시스템을 사용하는지 ‘ 중요하지 않습니다. ‘ 언어 표준은 c가 수정 가능함을 보장합니다. ‘는 1 + 12로 평가하는 것과 똑같이 강력한 보증입니다. 위에 링크 한 프로그램 EFGH 인쇄 이외의 작업을 수행하는 경우 C 구현을 준수하지 않음을 나타냅니다.
  • @Dainus : MSVC 컴파일러에는 ‘ 문자열 풀링 ‘이라는 최적화 기능이 있습니다. 동일한 문자열을 읽기 전용 세그먼트로 사용하는 것이 읽기 전용임을 보장 할 수있는 경우. ‘ 정상적인 ‘ 동작을 확인하려면 최적화를 해제하세요. 참고로 ” 수정하고 계속하기 “를 사용하려면이 옵션이 켜져 있어야합니다. 자세한 정보 : msdn.microsoft.com/en-us/library/s0s0asdt.aspx
  • Dainius가 많은 오류는 바이트 나 포인터 자체의 수정을 방지하기 위해 변수 자체를 const char *const로 표시해야한다는 것입니다. 그러나 대부분의 경우 프로그래머는 일부 런타임 코드를 허용하도록 하나 또는 둘 모두를 변경 가능하게 둡니다. 입력 된 상수로 보이는 것을 수정합니다 (하지만 상수는 아님).
  • 답글 남기기

    이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다