왜 strcpy 대신 strncpy를 사용해야합니까?


84

편집 : 예제에 대한 소스를 추가했습니다.

내가 건너 온 이 예 :

이 출력은 다음과 같습니다.

목적지는 원래 = 'abcdefg'
strcpy 이후 목적지는 '123456789'가됩니다.

destination1은 원래 = 'abcdefg'입니다.
strncpy 이후 destination1은 '12345fg'가됩니다.

왜 누군가가이 효과를 원하는지 궁금합니다. 혼란 스러울 것 같습니다. 이 프로그램은 기본적으로 Tom Bro763을 사용하여 누군가의 이름 (예 : Tom Brokaw)을 복사 할 수 있다고 생각하게합니다.

strncpy() over 사용의 장점은 무엇입니까strcpy() ?


81
나는 당신이 " strcpy대신 왜 도대체 누가 사용 strncpy하겠습니까?"
Sam Harwell

5
내가 C에서 첫 학기 프로그래밍 과정의 조교 였을 때, 나는 학생들에게 getline신중하게 만들어진 입력에 대해 등급을 매길 때 같은 방법을 사용 하면 잘못된 결과가 발생할 것이라고 확신 했습니다. :)
Sam Harwell

4
코드가 실제로 무엇을하는지 오해했다고 생각합니다. 자세히 살펴보십시오.
Emil H

6
C가 문자열을위한 반쯤 괜찮은 표준 라이브러리를 얻지 못한 것은 정말 유감입니다.
starblue 2009-08-11

7
그다지 유감이 아닙니다. 내 말은, 그것은 완전히 나를 파산, 높은 수준의 언어 훨씬 더 재미 :)했다
카슨 마이어스

답변:


98

strncpy길이를 입력하도록 요구하여 버퍼 오버 플로우를 방지합니다. strcpy후행에 의존하며 \0항상 발생하지는 않습니다.

둘째, 7 자 문자열에 5 자만 복사하도록 선택한 이유는 저를 넘어서지 만 예상되는 동작을 생성합니다. 세 번째 인수가 있는 첫 번째 n문자 만 복사합니다 n.

n함수는 모두 버퍼 오버플로에 대한 방어 코딩으로 사용됩니다. 와 같은 이전 기능 대신 사용하십시오 strcpy.


47
lysator.liu.se/c/rat/d11.html 참조 : strncpy처음에는 디렉토리 항목과 같은 구조의 고정 길이 이름 필드를 처리하기 위해 C 라이브러리에 도입되었습니다. 이러한 필드는 문자열과 같은 방식으로 사용되지 않습니다. 최대 길이 필드에는 후행 널이 필요하지 않으며, 짧은 이름에 대한 후행 바이트를 널로 설정하면 효율적인 필드 별 비교가 보장됩니다. strncpy원래는 "제한된 strcpy"가 아니며,위원회는 그러한 사용에 더 적합하도록 기능을 변경하기보다는 기존 관행을 인식하는 것을 선호했습니다.
Sinan Ünür

35
왜 이것이 많은 투표를 받고 있는지 잘 모르겠습니다. strncpy는 strcpy에 대한 안전한 대안으로 의도되지 않았으며 실제로 문자열을 0으로 종료하지 않기 때문에 더 안전하지 않습니다. 또한 제공된 길이를 NUL 문자로 채우는 점에서 다른 기능이 있습니다. caf가 답장에서 말했듯이-고정 크기 배열의 문자열을 덮어 쓰기위한 것입니다.
딥 스틱

26
사실 남아 strncpy있다 하지 의 안전한 버전 strcpy.
Sinan Ünür

7
@Sinan : 나는 그것이 더 안전하다고 결코 말하지 않았습니다. 방어 적입니다. 그것은 당신이하고있는 일에 대해 생각하게 만드는 길이를 넣어야합니다. 더 나은 해결책이 있지만, 사람들이 훨씬 더 방어적인 기능이기 때문에 strncpy대신 사용할 것이라는 사실은 여전히 ​​남아 있습니다 strcpy.
Eric

10
n 함수는 모두 버퍼 오버플로에 대한 방어 코딩으로 사용됩니다. strcpy와 같은 이전 기능 대신 사용하십시오. 이것은 사실 snprintf이지만에는 관련이 strncat없고 완전히 사실이 아닙니다 strncpy. 이 답변이 어떻게 그렇게 많은 찬성표를 얻을 수 있습니까? 이 가짜 기능과 관련하여 상황이 얼마나 나쁜지 보여줍니다. 이를 사용하는 것은 방어 적이 지 않습니다. 대부분의 상황에서 프로그래머는 의미를 이해하지 못하고 잠재적으로 0이 아닌 종료 문자열을 생성합니다.
chqrlie

180

strncpy()함수는 매우 특별한 문제를 염두에두고 설계되었습니다. 원래 UNIX 디렉토리 항목의 방식으로 저장된 문자열을 조작하는 것입니다. 이것들은 고정 된 크기의 배열을 사용했고, nul- 종결자는 파일 이름이 배열보다 짧은 경우에만 사용되었습니다.

이것이 두 가지 이상한 이유 뒤에 있습니다 strncpy().

  • 완전히 채워지면 대상에 Nul- 종결자를 넣지 않습니다. 과
  • 항상 목적지를 완전히 채우고 필요한 경우 nuls로 채 웁니다.

"안전한 strcpy()"경우 다음 strncat()과 같이 사용하는 것이 좋습니다 .

그것은 항상 결과를 무효화하고 필요 이상으로 복사하지 않을 것입니다.


은 strncpy하는 최대 문자 수를 허용합니다하지만, 물론, strncpy에서 당신이 중 원하는 것을 항상 아닙니다 추가되지 대상 버퍼 크기 ...하지만 그렇게 아마하지 않는 한 문제가되지 않을 것입니다 만 사소한 일이다 한 문자열을 다른 문자열에 연결하려고합니다.
David Wolever

나는 그 이유를 몰랐고 내가 atm에서 작업하는 것과 매우 관련이 있습니다.
Matt Joiner

strncpy () 함수는 고정 길이 널 패딩 형식으로 문자열을 저장하도록 설계되었습니다. 이러한 형식은 원래 Unix 디렉토리 항목에 사용되었지만 0-N 바이트의 문자열을 N 바이트의 저장소에 저장할 수 있기 때문에 다른 곳에서도 사용됩니다. 오늘날에도 많은 데이터베이스는 고정 길이 문자열 필드에 널로 채워진 문자열을 사용합니다. strncpy ()와의 혼동은 문자열을 FLNP 형식으로 변환한다는 사실에서 기인합니다. 필요한 것이 FLNP 문자열이라면 훌륭합니다. null로 끝나는 문자열이 필요하면 스스로 종료를 제공해야합니다.
supercat 2011

1
dest[0] = '\0';strncat 호출 전에 왜 작성해야 합니까? 설명해 주시겠습니까?
snr

4
@snr : strncat()소스 문자열을 대상 문자열의 끝에 연결합니다. 소스 문자열을 대상에 복사하고 싶기 때문에 먼저 대상을 빈 문자열로 설정합니다 dest[0] = '\0';.
caf

34

의 의도를 알고 있지만 strncpy실제로 좋은 기능은 아닙니다. 둘 다 피하십시오. Raymond Chen이 설명합니다 .

개인적으로 내 결론은 단순히 strncpynull로 끝나는 문자열을 다루는 경우 모든 친구들 을 피하는 것 입니다. 이름에 "str"이 있음에도 불구하고 이러한 함수는 null로 끝나는 문자열을 생성하지 않습니다. 널로 끝나는 문자열을 원시 문자 버퍼로 변환합니다. 두 번째 버퍼가 잘못되어 널로 끝나는 문자열이 예상되는 곳에서 사용합니다. 소스가 너무 길면 적절한 null 종료를 얻지 못할뿐만 아니라 소스가 짧으면 불필요한 null 패딩이 발생합니다.

strncpy가 안전하지 않은 이유 도 참조하십시오 .


27

strncpy는 strcpy보다 안전하지 않으며 한 유형의 버그를 다른 유형과 교환 할뿐입니다. C에서 C 문자열을 처리 할 때 버퍼의 크기를 알아야합니다. strncpy는 다른 사람들이 언급 한 디렉토리 항목에 대해 정당화되었지만 그렇지 않으면 절대 사용해서는 안됩니다.

  • 문자열과 버퍼의 길이를 알고 있다면 왜 strncpy를 사용합니까? 기껏해야 컴퓨팅 능력의 낭비입니다 (쓸모없는 0 추가).
  • 길이를 모르면 버퍼 오버플로보다 낫지 않은 문자열을 조용히 잘라낼 위험이 있습니다.

나는 이것이 strncpy에 대한 좋은 설명이라고 생각하므로 투표했습니다. strncpy에는 자체 문제가 있습니다. 예를 들어 glib에 자체 확장 기능이있는 이유라고 생각합니다. 그리고 그렇습니다. 프로그래머로서 모든 배열의 크기를 알아야한다는 것은 불행한 일입니다. 문자열로 0 종료 문자 배열을 갖는 decison은 ... 모든 사랑스러운 비용을 우리가
프리드리히

1
0으로 채워진 문자열은 고정 형식 파일에 데이터를 저장할 때 매우 일반적입니다. 확실히 데이터베이스 엔진 및 XML과 같은 것의 인기와 진화하는 사용자 기대로 인해 고정 형식 파일이 20 년 전보다 덜 일반적이게되었습니다. 그럼에도 불구하고 이러한 파일은 종종 데이터를 저장하는 가장 시간 효율적인 수단입니다. 레코드에있는 데이터의 예상 길이와 최대 길이 사이에 큰 차이가있는 경우를 제외하고는 레코드를 여러 청크로 분할 된 레코드를 읽는 것보다 사용되지 않은 데이터가 포함 된 단일 청크로 읽는 것이 훨씬 빠릅니다.
supercat 2011

g_strlcpy ()를 사용하는 레거시 코드의 유지 관리를 인수했기 때문에 패딩 비 효율성을 겪지 않지만 전송 된 바이트 수가 유지되지 않았으므로 코드가 조용히 결과를 자릅니다.
user2548100

21

당신이 찾고 strlcpy()있는 것은 항상 0으로 문자열을 종료하고 버퍼를 초기화하는 함수 입니다. 또한 오버플로를 감지 할 수 있습니다. 유일한 문제는 (정말) 이식성이없고 일부 시스템 (BSD, Solaris)에만 존재한다는 것입니다. 이 기능의 문제점은 http://en.wikipedia.org/wiki/Strlcpy 의 토론에서 볼 수 있듯이 또 다른 웜 캔을 열 수 있다는 것입니다 .

제 개인적인 의견은 strncpy()및 보다 훨씬 더 유용하다는 것 strcpy()입니다. 더 나은 성능을 가지고 있으며 snprintf(). 이 기능이없는 플랫폼의 경우 구현하기가 비교적 쉽습니다. (응용 프로그램의 개발 단계에서는이 두 함수 ( snprintf()strlcpy())를 버퍼 오버플로 또는 잘림시 프로그램을 잔인하게 중단하는 트 랩핑 버전으로 대체합니다 . 이렇게하면 최악의 공격자를 신속하게 잡을 수 있습니다. 특히 다른 사람의 코드베이스에서 작업하는 경우 .

편집 : strlcpy()쉽게 구현할 수 있습니다.


3
strlcpy는 Linux와 Windows를 제외한 거의 모든 곳에서 사용 가능하다고 작성할 수 있습니다! 그러나 BSD 라이센스이므로 라이브러리 중 하나에 드롭하고 거기에서 사용할 수 있습니다.
Michael van der Westhuizen

테스트를 추가 dstsize > 0하고 그렇지 않은 경우 아무것도 수행하지 않을 수 있습니다.
chqrlie

네 말이 맞아. 그것 없이는 목적지 버퍼 의 길이 를 dstsize트리거하고 그것을 넘치게 할 것입니다. memcpylen
Patrick Schlüter 2016

또한 좋은 솔루션을 홍보하기위한 것입니다. 더 많은 사람들이 strlcpy에 대해 알 필요가 있습니다.
rsp

@MichaelvanderWesthuizen Linux에서 사용할 수 있지만 glibc에서는 사용할 수 없습니다. 자세한 정보는 내 답변을 참조하십시오 (1) (2) (3)
rsp

3

strncpy()함수는 더 안전한 함수입니다. 대상 버퍼가 허용 할 수있는 최대 길이를 전달해야합니다. 그렇지 않으면 소스 문자열이 올바르게 0으로 종료되지 않을 strcpy()수 있습니다. 이 경우 함수는 대상에 더 많은 문자를 기록하여 대상 버퍼 이후의 메모리에있는 모든 것을 손상시킬 수 있습니다. 이것은 많은 익스플로잇에서 사용되는 버퍼 오버런 문제입니다.

또한 read()버퍼에 종료 0을 넣지 않고 읽은 바이트 수를 반환하는 POSIX API 함수의 경우 수동으로 0을 넣거나 strncpy().

예제 코드에서는 index실제로 인덱스가 아니지만 counta- 소스에서 대상으로 복사 할 문자 수 를 나타 냅니다. 소스의 처음 n 바이트 중 널 바이트가없는 경우 대상에 배치 된 문자열은 널로 종료되지 않습니다.


1

strncpy는 목적지의 크기가 더 작더라도 소스의 크기에 대해 '\ 0'으로 목적지를 채 웁니다 ....

맨 페이지 :

src의 길이가 n보다 작 으면 strncpy ()는 dest의 나머지 부분을 널 바이트로 채 웁니다.

나머지뿐만 아니라 ...이 후 n 문자에 도달 할 때까지. 따라서 오버플로가 발생합니다 ... (맨 페이지 구현 참조)


3
strncpy는 대상의 크기가 더 작더라도strncpy 대상을 '\ 0'으로 채 웁니다 . 그러나이 문장은 오류가 있고 혼란 스럽 습니다. 소스의 길이가 더 작은 경우 크기 인수. size 인수는 소스의 크기가 아니라 소스에서 복사 할 최대 문자 수가 아닙니다.에서 strncat와 같이 대상의 크기입니다.
chqrlie

@chqrlie : 맞습니다. strncpy다른 복사 작업 에 비해 장점은 전체 대상이 기록된다는 것입니다. 컴파일러는 일부 불확실한 값을 포함하는 구조를 복사 할 때 "창의적인"것을 시도 할 수 있으므로 구조 내의 모든 문자 배열이 완전히 작성되도록하는 것이 "놀람"을 방지하는 가장 간단한 방법 일 수 있습니다.
supercat apr

@supercat :이 특정 경우에 대한 매우 작은 이점 ... 그러나 strncpynull 종료를 보장하기 위해 호출 후에 대상을 패치해야합니다 . strncpy(dest, src, dest_size)[dest_size - 1] = '\0';
chqrlie

@chqrlie : 후행 널 바이트가 필요한지 여부는 데이터가 나타내는 내용에 따라 다릅니다. 구조 내에서 0으로 끝나는 데이터가 아닌 0으로 채워진 데이터를 사용하는 것은 예전처럼 일반적이지 않지만 예를 들어 객체 파일 형식이 8 바이트 섹션 이름을 사용하는 char[8]경우 구조 내에서 처리 할 수 있습니다. 8 자까지는 a를 사용하는 것보다 더 좋을 char[8]수 있지만 7 자만 처리 할 수 ​​있거나 문자열을 char[9]버퍼 에 복사 한 다음 memcpy대상 으로 복사 해야합니다.
supercat 2017-04-16

@chqrlie : 문자열로 작업을 수행하는 대부분의 코드는 길이를 알고 있어야 char하며 0에 도달 할 때까지 포인터로 맹목적으로 실행해서는 안됩니다 . 유일한 것은 제로 종료 문자열은 문자열 리터럴입니다 정말 좋은, 심지어 가변 길이 - 인코딩 된 접두사는 아마 더있을 것입니다. 그 밖의 거의 모든 경우에는 문자열에 길이를 접두사로 붙이 거나char* 가 실제로 라는 것을 나타내는 특수 접두사를 사용하는 것이 struct stringInfo {char header[4]; char *realData; size_t length; size_t size;}좋습니다.
supercat 2017-04-16

-1

원본 문자열의 일부만 대상에 복사해야하는 다른 많은 시나리오에서 사용할 수 있습니다. strncpy ()를 사용하면 strcpy ()와 반대로 원래 문자열의 제한된 부분을 복사 할 수 있습니다. 당신이 올린 코드는작성한 publib.boulder.ibm.com 입니다.


-1

그것은 우리의 요구 사항에 달려 있습니다. Windows 사용자의 경우

전체 문자열을 복사하지 않거나 n 개의 문자 만 복사하고 싶을 때마다 strncpy를 사용합니다. 그러나 strcpy는 종료 널 문자를 포함하여 전체 문자열을 복사합니다.

이 링크는 strcpy 및 strncpy 및 사용할 수있는 위치에 대해 더 많이 알 수 있도록 도와줍니다.

strcpy에 대해

strncpy에 대해


-8

strncpy는 모든 종류의 공격에 시스템을 취약하게 만드는 잠재적 인 버퍼 오버플로 취약점 때문에 strcpy를 사용해서는 안되므로 strcpy의 안전한 버전입니다.


6
lysator.liu.se/c/rat/d11.html 참조 : strncpy 함수 strncpy는 처음에 디렉토리 항목과 같은 구조의 고정 길이 이름 필드를 처리하기 위해 C 라이브러리에 도입되었습니다. 이러한 필드는 문자열과 같은 방식으로 사용되지 않습니다. 최대 길이 필드에는 후행 널이 필요하지 않으며, 짧은 이름에 대한 후행 바이트를 널로 설정하면 효율적인 필드 별 비교가 보장됩니다. strncpy는 원래``제한된 strcpy ''가 아니며위원회는 이러한 사용에 더 적합하도록 기능을 변경하는 대신 기존 관행을 인식하는 것을 선호했습니다.
Sinan Ünür
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.