strcpy() 반환 값
표준 C 라이브러리의 많은 함수들, 특히 문자열 조작을 위한 함수들, 특히 strcpy()는 다음과 같은 프로토타입을 공유합니다.
char *the_function (char *destination, ...)
은 제공된 의 반환 값과 .destination불필요한 것을 위해 반품 가치를 낭비하는 이유는 무엇입니까?그러한 기능은 무효이거나 유용한 것을 반환하는 것이 더 이치에 맞습니다.
함수 호출을 다른 식에 내포하는 것이 더 쉽고 편리하다는 것이 그 이유입니다. 예를 들어 다음과 같습니다.
printf("%s\n", strcpy(dst, src));
이 관용구를 정당화할 다른 합리적인 이유가 있습니까?
에반이 지적했듯이, 비슷한 것을 하는 것은 가능합니다.
char* s = strcpy(malloc(10), "test");
assign 예 : 정배malloc()ed도우미 변수를 사용하지 않고 값을 기억합니다.
(이 예제는 최상의 예가 아닙니다. 메모리 부족 상태에서 충돌하지만 아이디어는 분명합니다.)
char *stpcpy(char *dest, const char *src); 포인터를 문자열 끝으로 반환하며 POSIX.1-2008의 일부입니다.그 이전에는 1992년부터 GNU libc 확장이었습니다.그것은 래티스 C 아미가에서 처음 등장했습니다.1986년 DOS.
gcc -O3됩니다.strcpy+strcat용할을 stpcpy또는strlen인라인 복사(아래 참조).
는 매우의 표 라 이 브 설 일 었 되 으 쉽 주 장 게 수 있 할 습 니 다 매 우 준 며 계 러 리 찍 는 우 매 ▁that ▁the ▁c ▁was 있 니 습 다 ▁to ▁easy ▁very s ▁argue ▁very ▁it ▁library ' ▁designed ▁c 의 ▁and s ' , 매 수 ▁earlystr*함수가 최적으로 설계되지 않았습니다.I/O 기능은 확실히 C가 프리프로세서를 가지기 전인 1972년에 매우 일찍 설계되었습니다. 이것이 Unix와 같은 플래그 비트맵 대신 모드 문자열을 사용하는 이유입니다.
Mike I package 수 Mike Lesk의 "portable I/O package"에 포함된 기능 목록이 알 수 .strcpy현재의 형태로 날짜는 거기까지 거슬러 올라가거나 나중에 이러한 기능이 추가된 경우입니다.(내가 유일하게 찾은 진짜 출처는 Dennis Ritchie의 널리 알려진 C History 기사인데, 이 기사는 훌륭하지만 깊이는 그리 깊지 않습니다.실제 I/O 패키지 자체에 대한 문서나 소스 코드를 찾지 못했습니다.)
그들은 1978년 K&R 초판에 현재 형태로 등장합니다.
함수는 호출자에게 잠재적으로 유용한 계산 결과를 버리는 대신 반환해야 합니다.문자열 끝에 대한 포인터로 사용하거나 정수 길이로 사용할 수 있습니다. (포인터는 자연스러운 것입니다.)
@R이 말하는 것처럼:
는 이 되는 null. ( 우리모이러함이수종이들널료는되포바반를인바다터환를다기랍하니것에이니입감트킬양시소을많는은두는한이▁(▁a▁byte▁we▁returned▁reduce▁to▁wouldwhich▁these▁lot▁of다것▁pointer▁a▁functions▁the우▁wish리니입)
O(n)로의작업의O(1))
예를 들어 전화하기strcat(bigstr, newstr[i])많은 짧은 (O(1) 길이의) 문자열로부터 긴 문자열을 빌드하기 위한 루프에서 대략적으로O(n^2) 복성잡, 그나러strlen/memcpy각 문자를 두 번만 봅니다(스트렐렌에 한 번, memcpy에 한 번).
ANSIC 표준 라이브러리만 사용하면 모든 문자를 한 번만 효율적으로 볼 수 없습니다.한 번에 한 바이트씩 루프를 수동으로 작성할 수 있지만, 몇 바이트보다 긴 문자열의 경우 효율적인 libc 제공 SIMD 스트렐렌과 메모리를 고려할 때 현대 HW에서 현재 컴파일러(검색 루프를 자동 벡터화하지 않음)로 각 문자를 두 번 보는 것보다 더 어렵습니다.사용할 수 있습니다.length = sprintf(bigstr, "%s", newstr[i]); bigstr+=length;,그렇지만sprintf()형식 문자열을 구문 분석해야 하며 속도가 빠르지 않습니다.
심지어 차이의 위치를 반환하는 버전도 없습니다.만약 그것이 당신이 원하는 것이라면, 당신은 왜 문자열 비교가 파이썬에서 그렇게 빠릅니까?와 같은 문제를 가지고 있습니다.컴파일된 루프를 사용하여 수행할 수 있는 어떤 작업보다 빠르게 실행되는 최적화된 라이브러리 함수(관심 있는 모든 대상 플랫폼에 대해 손으로 최적화된 ASM이 없는 경우)를 사용할 수 있습니다. 이 기능을 사용하면 한 번 더 일반 루프로 이동하기 전에 다른 바이트에 접근할 수 있습니다.
길이의 것뿐만 의 O) 하지 않고 으로 보이며, C의 문자열 라이브러리는 O(n)라는 이름으로 표시됩니다.strcpy의 행동만이 유일한 예는 아닙니다.
기본적으로 암묵적인 길이의 문자열을 전체 불투명 객체로 취급하며, 항상 포인터를 시작으로 반환하며, 검색 또는 추가 후에는 끝으로 또는 하나 안의 위치로 반환하지 않습니다.
역사 추측
초기 C on a PDP-11에서, 나는 의심합니다.strcpy보다 효율적이지 않았습니다.while(*dst++ = *src++) {}(아마도 그런 방식으로 구현되었을 것입니다.)
사실, K&R 초판(101페이지)은 다음과 같은 구현을 보여줍니다.strcpy다음과 같이 말합니다.
이것이 언뜻 보기에는 암호화된 것처럼 보일 수 있지만, 표기상의 편의성이 상당하며, C 프로그램에서 자주 볼 수 있는 다른 이유가 없다면 관용구를 숙달해야 합니다.
이것은 프로그래머들이 당신이 마지막 값인 또는 를 원하는 경우에 그들 자신의 루프를 작성할 것을 충분히 예상했다는 것을 의미합니다. 따라서 그들은 손에 최적화된 asm 라이브러리 기능을 위한 더 유용한 API를 노출하기에 너무 늦기 전까지 표준 라이브러리 API를 재설계할 필요성을 느끼지 못했을 수도 있습니다.
하지만 원래의 가치를 돌려주는 것이dst말이 됩니까?
strcpy(dst, src) 반환은 에 대한 평가와 유사합니다. 따라서 strcpy를 문자열 할당 연산자처럼 작동시킵니다.
다른 대답들이 지적했듯이, 이것은 둥지를 틀 수 있습니다.foo( strcpy(buf,input) );초기의 컴퓨터들은 메모리에 매우 제약이 있었습니다.소스 코드를 콤팩트하게 유지하는 것이 일반적이었습니다.펀치 카드와 느린 단말기가 아마도 이것의 한 요인이었을 것입니다.저는 역사적인 코딩 표준이나 스타일 가이드, 또는 어떤 것이 한 줄에 올려놓기에는 너무 많다고 여겨졌는지 모릅니다.
구식 컴파일러들도 한 요인일 수 있습니다.인 최적화 최신최적화컴를사용면하러일파,▁with면▁optim,char *tmp = foo();/bar(tmp);가 보다느않음보다 느리지 .bar(foo()); 그것은 그은나것러와 함께 .gcc -O0 초기의 최적화할 수 ), 할 수 있기를 매우초컴의스변가완러최수모할는있지르만겠않지음을화지적예있유수적어바레를기도랍할니지다지에터스경는에우달간현한와리단대하택약공을간기파일히전수를▁i▁modern▁away스▁space▁they▁for.gcc -O0지속적인 디버깅을 위해 의도적으로 모든 것을 유출/유출합니다.예.gcc -O0일관된 디버깅을 위해 의도적으로 최적화되지 않기 때문에 고대 컴파일러에 적합한 모델이 아닙니다.
컴파일러가 생성한 asm 동기 부여 가능성
C 문자열 라이브러리의 일반적인 API 설계에서 효율성에 대한 관심이 부족한 점을 고려하면, 이것은 불가능할 수 있습니다.그러나 코드 크기의 이점이 있었을 수도 있습니다. (초기 컴퓨터에서는 코드 크기가 CPU 시간보다 더 어려운 제한이었습니다.)
초기 C 컴파일러의 품질에 대해서는 잘 모르지만, PDP-11과 같은 훌륭한 단순/직교 아키텍처에 대해서도 최적화에 뛰어나지 않은 것은 확실합니다.
함수 호출 후에 문자열 포인터를 사용하는 것이 일반적입니다.ASM 수준에서, 당신(컴파일러)은 아마도 그것을 호출 전에 그것을 레지스터에 가지고 있을 것입니다.호출 규약에 따라 스택에 밀어 넣거나 호출 규약이 첫 번째 인수를 지정하는 오른쪽 레지스터에 복사합니다.(즉, 어디에)strcpy기대하고 있음).또는 미리 계획하는 경우 호출 규약에 대한 올바른 레지스터에 포인터가 이미 있습니다.
그러나 함수는 모든 arg-passing 레지스터를 포함하여 일부 레지스터를 clobber로 호출합니다. (따라서 함수가 레지스터에 arg를 가져올 때 스크래치 레지스터에 복사하는 대신 거기에서 증분할 수 있습니다.)
호출자로서 함수 호출을 통해 무언가를 유지하기 위한 코드젠 옵션은 다음과 같습니다.
- 로컬 스택 메모리에 저장/재로드합니다. (또는 최신 복사본이 아직 메모리에 있는 경우 다시 로드하십시오.)
- 전체 함수의 시작/끝 부분에 호출 취소 레지스터를 저장/복원하고 함수 호출 전에 포인터를 해당 레지스터 중 하나에 복사합니다.
- 함수는 레지스터의 값을 반환합니다. (물론 C 소스가 입력 변수 대신 반환 값을 사용하도록 작성된 경우에만 작동합니다.)
dst = strcpy(dst, src);(네스트하지 않는 경우).
모든 아키텍처의 모든 호출 규칙 레지스터에서 포인터 크기의 반환 값을 반환하는 것을 알고 있으므로 라이브러리 함수에 추가 명령을 하나 더 사용하면 반환 값을 사용하려는 모든 호출자에서 코드 크기를 저장할 수 있습니다.
당신은 아마도 초기 C 컴파일러들로부터 더 나은 asm을 얻었을 것입니다.strcpy컴파일러가 콜 주변의 포인터를 콜-디버깅 레지스터에 저장하거나 스택에 흘리게 하는 것보다 (레지스터에 저장됨).여전히 그럴 수도 있습니다.
그나저나, 많은 ISA에서 반환 값 레지스터는 첫 번째 arg-passing 레지스터가 아닙니다.그리고 base+index 주소 지정 모드를 사용하지 않는 한 strcpy가 포인터 증가 루프의 레지스터를 복사하는 데 추가 명령(및 다른 레지스터를 연결)이 필요합니다.
PDP-11 툴체인은 일반적으로 일종의 스택-args 호출 규약을 사용하여 항상 스택의 Args를 푸시합니다.얼마나 많은 콜 보존 레지스터와 콜 클로버 레지스터가 정상인지는 잘 모르겠지만 5~6개의 GPreg만 사용할 수 있었습니다(R7은 프로그램 카운터, R6은 스택 포인터, R5는 종종 프레임 포인터로 사용됨).32비트 x86과 비슷하지만 훨씬 더 비좁습니다.
char *bar(char *dst, const char *str1, const char *str2)
{
//return strcat(strcat(strcpy(dst, str1), "separator"), str2);
// more readable to modern eyes:
dst = strcpy(dst, str1);
dst = strcat(dst, "separator");
// dst = strcat(dst, str2);
return dst; // simulates further use of dst
}
# x86 32-bit gcc output, optimized for size (not speed)
# gcc8.1 -Os -fverbose-asm -m32
# input args are on the stack, above the return address
push ebp #
mov ebp, esp #, Create a stack frame.
sub esp, 16 #, This looks like a missed optimization, wasted insn
push DWORD PTR [ebp+12] # str1
push DWORD PTR [ebp+8] # dst
call strcpy #
add esp, 16 #,
mov DWORD PTR [ebp+12], OFFSET FLAT:.LC0 # store new args over our incoming args
mov DWORD PTR [ebp+8], eax # EAX = dst.
leave
jmp strcat # optimized tailcall of the last strcat
이은사용않지는버훨전더다컴니합팩트씬을 하지 않는 버전보다 훨씬 더 컴팩트합니다.dst =그리고 대신 입력 Arg를 사용합니다.strcat(Godbolt 컴파일러 탐색기에서 두 가지를 모두 참조하십시오.
그-O3출력은 매우 다릅니다. 반환 값을 사용하지 않는 버전의 gcc는 사용합니다.stpcpy(꼬리에 포인터를 대고) 그리고 나서.mov- 리터럴 문자열 데이터를 올바른 위치에 직접 저장할 수 있습니다.
하지만 불행하게도,dst = strcpy(dst, src)-O3 버전은 여전히 일반 사용strcpy그 다음에 줄서기strcat~하듯이strlen+mov-어쨌든.
C-string에 연결하거나 C-string에 연결하지 않음
암시적 길이 문자열이 항상 나쁜 것은 아니며 흥미로운 이점이 있습니다(예: 접미사는 복사할 필요 없이 유효한 문자열이기도 합니다).
그러나 C 문자열 라이브러리는 효율적인 코드를 가능하게 하는 방식으로 설계되지 않았습니다.char-한 번에 루프는 일반적으로 자동으로 동기화되지 않으며 라이브러리 기능은 작업 결과를 무시합니다.
gcc와 clang은 반복 카운트가 첫 번째 반복 전에 알려지지 않는 한 루프를 자동으로 동기화하지 않습니다.for(int i=0; i<n ;i++)ICC는 검색 루프를 벡터화할 수 있지만, 여전히 손으로 쓴 asm만큼 잘 할 것 같지는 않습니다.
strncpy 등은 기본적으로 재앙입니다. 예를 들어.strncpy종료를 복사하지 않습니다.'\0'버퍼 크기 제한에 도달하면 수동으로arr[n] = 0;전후에그러나 소스 문자열이 더 짧으면 다음과 같이 패딩됩니다.0바이트 수가 지정된 길이로 늘어남으로써 절대 만질 필요가 없는 메모리 페이지를 만질 수 있습니다. (또한 짧은 문자열을 여전히 많은 공간이 남아 있는 큰 버퍼에 복사하는 것도 매우 비효율적입니다.)버퍼 오버플로를 방지하기 위한 것이 아니라 더 큰 문자열 중간에 쓰기 위해 설계된 것으로 보입니다.
다음과 같은 몇 가지 기능snprintf사용할 수 있으며 항상 null-discovery를 수행합니다.어떤 일을 하는지 기억하는 것은 어렵고, 만약 여러분이 잘못 기억한다면 큰 위험이 있기 때문에, 여러분은 정확성이 중요한 경우에 매번 확인해야 합니다.
브루스 도슨의 말처럼:이미 strncpy 사용을 중지합니다!분명히 몇몇 MSVC 확장들은 다음과 같습니다._snprintf더 나빠요.
strncat POSIX.2001에도 존재하며 관련이 없습니다.strcpy당신이 원하는 대로 행동합니다, 경계 검사.strcpy항상 0-종단으로 끝나는 것.하지만 마치strcat여전히 원래 포인터를 반환하므로 버퍼에 문자열을 효율적으로 추가하는 데 유용하지 않습니다. 동일한 버퍼에서 단순히 반복적으로 호출하는 경우 현재 끝을 찾기 위해 선행 부분을 매번 다시 스캔해야 합니다.맨 페이지에는 "화가 슐레밀"이 언급되어 있습니다.
저는 당신의 추측이 옳다고 생각합니다. 통화를 연결하는 것이 더 쉬워집니다.
코드화하기도 매우 쉽습니다.
반환 값은 일반적으로 AX 레지스터에 남아 있습니다(필수 사항은 아니지만 종종 해당됨).그리고 목적지는 기능이 시작되면 AX 레지스터에 들어갑니다.목적지를 반환하려면 프로그래머가 ...해야 합니다.정확히 아무것도!그냥 그 값을 그 자리에 두세요.
는 그 를 " 그래함다같선수있언이습다니할음과프수로는머를▁the다있▁as▁declare니습▁the▁function"로 선언할 수 있습니다.void하지만 그 반환 값은 이미 올바른 위치에 있으며, 반환을 기다리고 있으며, 반환에 대한 추가 지침도 필요하지 않습니다!아무리 작은 개선에도 불구하고, 어떤 경우에는 유용합니다.
Fluent Interfaces와 동일한 개념입니다.코드를 더 빨리/더 쉽게 읽을 수 있도록 하는 것입니다.
이것은 중첩 목적으로 설정된 것이 아니라 오류 확인을 위해 설정된 것 같습니다.메모리가 c 표준 라이브러리 기능을 제공하지 않는 경우 자체적으로 많은 오류 검사를 수행하지 않으므로 strcpy 호출 중에 무언가가 잘못되었는지 확인하는 것이 더 의미가 있습니다.
if(strcpy(dest, source) == NULL) {
// Something went horribly wrong, now we deal with it
}
언급URL : https://stackoverflow.com/questions/3561427/strcpy-return-value
'programing' 카테고리의 다른 글
| RxJS: TakeUntil() Angular 구성 요소의 ngOnDestroy() (0) | 2023.08.01 |
|---|---|
| PowerShell에서 curl 명령을 사용하는 방법은 무엇입니까? (0) | 2023.08.01 |
| Oracle에서 대/소문자 구분 안 함 (0) | 2023.08.01 |
| fragment를 제거하는 방법, 즉 finish()와 동등한 fragment를 제거하는 방법? (0) | 2023.08.01 |
| HTML 파일을 WebView로 로드 (0) | 2023.08.01 |