CodeGuru에서 “strlen vs sizeof”라는 제목의 스레드를 읽고있었습니다. div id = “22f0f376d3”>
응답 중 하나 에 따르면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;
여기서 foo
는 char
의 또 다른 배열입니다. 이 형식의 초기화는 문자열 리터럴에서만 작동합니다.
편집
초기화 할 수도 있다고 수정해야합니다.
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_LENGTH
는char 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_else
가sizeof(buf)
보다 큽니다. 일반적으로 마지막 문자buf[sizeof(buf)-1] = 0
를 설정하여 보호하거나buf
가 0으로 초기화 된 경우 를 사본 길이로 사용합니다. -
strlcpy
또는strcpy_s
또는 필요한 경우 - 수정되었습니다. 안타깝게도 최신 컴파일러 (
strlcpy
및snprintf
는 직접 액세스 할 수 없음)를 사용하지 않는 한 쉽게 이식 할 수있는 방법이 없습니다. 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 + 1
이 2
로 평가하는 것과 똑같이 강력한 보증입니다. 위에 링크 한 프로그램 이 EFGH
인쇄 이외의 작업을 수행하는 경우 C 구현을 준수하지 않음을 나타냅니다. const char *const
로 표시해야한다는 것입니다. 그러나 대부분의 경우 프로그래머는 일부 런타임 코드를 허용하도록 하나 또는 둘 모두를 변경 가능하게 둡니다. 입력 된 상수로 보이는 것을 수정합니다 (하지만 상수는 아님).