문자열을 반환하는 함수, 좋은 스타일?

C 프로그램에서 종종 ADT의 문자열 표현을 만드는 방법이 필요합니다. 어떤 식 으로든 화면에 문자열을 출력 할 필요가 없더라도 이런 디버깅 방법이 있으면 깔끔합니다. 그래서 이런 기능이 자주 등장합니다.

char * mytype_to_string( const mytype_t *t ); 

실제로 반환 할 문자열에 대한 메모리를 처리하기위한 세 가지 옵션이 여기에 있다는 것을 알고 있습니다.

대안 1 : 함수의 정적 char 배열에 반환 문자열 저장 . 모든 호출에서 문자열을 덮어 쓴다는 점을 제외하고는 많은 생각이 필요하지 않습니다. 어떤 경우에는 문제가 될 수 있습니다.

대안 2 : 함수 내에서 malloc을 사용하여 힙에 문자열을 할당합니다. 버퍼의 크기 나 덮어 쓰기를 생각할 필요가 없기 때문에 정말 깔끔합니다.하지만 완료되면 문자열을 free ()해야한다는 것을 기억해야합니다. 그런 다음 임시 변수에 할당해야합니다. 해제 할 수 있습니다. 힙 할당은 스택 할당보다 훨씬 느리므로 루프에서 반복되면 병목 현상이 발생합니다.

대안 3 : 버퍼에 대한 포인터를 전달하고 호출자가 할당하도록합니다. 이 버퍼는 다음과 같습니다.

char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen ); 

이것은 호출자에게 더 많은 노력을 제공합니다. 또한이 대안이 인수 순서에 대한 다른 옵션을 제공한다는 것을 알았습니다. 처음과 마지막에 어떤 주장을해야합니까? (실제로 6 가지 가능성)

그러면 어떤 것을 선호해야합니까? 이유는 무엇입니까? C 개발자들 사이에 어떤 종류의 작성되지 않은 표준이 있습니까?

Comments

  • 참고로 대부분의 운영 체제는 옵션 3을 사용합니다. 호출자는 어떻게 든 버퍼를 할당하고 버퍼 포인터와 용량을 알려줍니다. 수신자는 버프를 채 웁니다. er 및 버퍼가 충분하지 않은 경우 실제 문자열 길이 를 반환합니다. 예 : OS X 및 iOS의 sysctlbyname

답변

가장 많이 본 방법은 2와 3입니다.

사용자가 제공 한 버퍼는 실제로 사용하기 매우 간단합니다.

char[128] buffer; mytype_to_string(mt, buffer, 128); 

대부분의 구현은 사용 된 버퍼의 양을 반환하지만

옵션 2는 동적으로 연결된 라이브러리를 사용할 수있는 곳에서 사용할 때 더 느리고 위험합니다. 다른 런타임 (및 다른 힙). 따라서 다른 라이브러리에서 malloc 된 것을 해제 할 수 없습니다. 이를 처리하려면 free_string(char*) 기능이 필요합니다.

댓글

  • 감사합니다! 나는 대안 3도 가장 좋아한다고 생각합니다. 그러나 나는 printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));와 같은 일을 할 수 있기를 원하므로 ‘ 사용 된 길이를 반환하는 대신 포인터를 반환하고 싶습니다. 문자열에. 동적 라이브러리 주석은 정말 중요합니다.
  • sizeof(buffer) - 1\0 종결 자?
  • @ Michael-O 버퍼 크기에 null 용어가 포함되어 있지 않습니다. 즉, 넣을 수있는 최대 문자열이 전달 된 크기보다 1이 작다는 의미입니다. 이것은 snprintf와 같은 표준 라이브러리에서 안전한 문자열 기능이 사용하는 패턴입니다.
  • @ratchetfreak 설명해 주셔서 감사합니다. 그 지혜로 대답을 확장하면 좋을 것 같습니다.

답변

# 3에 대한 추가 디자인 아이디어

가능하면 mytype mytype_to_string()와 동일한 .h 파일에 있습니다.

#define MYTYPE_TO_STRING_SIZE 256 

이제 사용자는 그에 따라 코딩 할 수 있습니다.

char buf[MYTYPE_TO_STRING_SIZE]; puts(mytype_to_string(mt, buf, sizeof buf)); 

주문

처음에 배열의 크기는 VLA 유형을 허용합니다.

char * mytype_to_string( const mytype_t *mt, size_t bufsize, char *buf[bufsize]); 

단일 차원에서는 그다지 중요하지 않지만 2 개 이상에서는 유용합니다.

void matrix(size_t row, size_t col, double matrix[row][col]); 

크기를 먼저 읽는 것이 다음 C에서 선호되는 관용구임을 기억합니다. 참조를 찾아야합니다 ….

답변

@ratchetfreak “의 훌륭한 답변에 추가로, 대안 # 3은 표준 C 라이브러리 함수와 유사한 패러다임 / 패턴을 따른다는 점을 지적합니다.

예 : strncpy.

 char * strncpy ( char * destination, const char * source, size_t num );  

동일한 패러다임을 따르는 것이 도움이됩니다. 새로운 개발자 (또는 미래의 자신)가 귀하의 기능을 사용해야 할 때인지 부하를 줄일 수 있습니다.

게시물과의 유일한 차이점은 인수는 인수 목록에서 가장 먼저 나열되는 경향이 있습니다.따라서 :

 char * mytype_to_string( char *buf, const mytype_t *mt, size_t buflen );  

Answer

대부분의 경우 @ratchet_freak을 에코합니다 (128보다 sizeof buffer를 약간 조정). 하지만 저는 이상한 반응으로 여기에 뛰어 들고 싶습니다. 이상한 것은 어떻습니까? 동료들로부터 이상한 외모를 얻고 더욱 설득력을 가져야하는 문제 외에 왜 안 되겠습니까? 그리고 저는 이것을 제공합니다 :

// Note allocator parameter. char* mytype_to_string(allocator* alloc, const mytype_t* t) { char* buf = allocate(alloc, however_much_you_need); // fill out buf based on "t" contents return buf; } 

사용 예 :

void func(my_type a, my_type b) { allocator alloc = allocator_new(); const char* str1 = mytype_to_string(&alloc, &a); if (!str1) goto oom; const char* str2 = mytype_to_string(&alloc, &b); if (!str2) goto oom // do something with str1 and str2 goto finish; oom: errno = ENOMEM; finish: // Frees all memory allocated through `alloc`. allocator_purge(&alloc); } 

이렇게하면 할당자를 매우 효율적으로 만들 수 있습니다 (더 효율적 할당 / 할당 해제 비용과 메모리 액세스에 대한 참조 지역성 측면에서 모두 malloc보다 높습니다. 할당 요청에 대한 일반적인 경우에 포인터를 증가시키는 것만 포함하는 아레나 할당자가 될 수 있습니다. 큰 연속 블록에서 순차적 인 방식으로 메모리를 풀링합니다 (첫 번째 블록에는 힙이 필요하지도 않음). 위치-스택에 할당 할 수 있음). 오류 처리를 단순화합니다. 또한 이것은 가장 논쟁의 여지가있을 수 있지만 “당신이 전달한 할당 자에게 메모리를 할당 할 것이며 명시적인 해제 (allocator_purge)이 스타일을 일관되게 사용하는 경우 가능한 모든 단일 함수에서 이러한 동작을 문서화 할 필요가 없습니다. 할당 자 매개 변수의 수용은 그것을 아주 분명하게 만들어줍니다.

모르겠습니다. 가능한 가장 단순하고 가능한 아레나 할당자를 구현하는 것과 같은 반론을 얻습니다 (모든 요청에 대해 최대 정렬을 사용하십시오). 너무 많은 작업이 필요합니다. 제 직설적 인 생각은 “우리는 무엇입니까, 파이썬 프로그래머입니까? 그렇다면 파이썬을 사용할 수도 있습니다. 이러한 세부 사항이 중요하지 않으면 파이썬을 사용하십시오.” 나는 “진지하다. 더 정확한 코드를 작성할뿐만 아니라, 참조의 지역 성과 같은 것들을 무시하면서 왼쪽과 오른쪽으로 생성하는 버그에 걸림돌이되기 때문에 파이썬으로 훨씬 더 효율적으로 작성할 가능성이 매우 높은 C 프로그래밍 동료가 많이있다. 나는 그렇지 않다” 데이터 지역성 및 최적의 명령어 선택과 같은 것에 관심이있는 C 프로그래머라면 여기에서 간단한 아레나 할당 자에 대해 무서워하는 것이 무엇인지 확인하십시오. 이는 최소한 필요한 인터페이스 유형보다 생각할 것이 훨씬 적습니다. 호출자는 인터페이스가 반환 할 수있는 모든 개별 항목을 명시 적으로 해제 할 수 있습니다. 이는 오류가 발생하기 쉬운 정렬의 개별 할당 해제에 대한 대량 할당 해제를 제공합니다. 적절한 C 프로그래머는 특히 프로파일 러에서 핫스팟으로 나타날 때 malloc에 대한 반복적 인 호출에 도전합니다. 내 관점에서 2020 년에도 C 프로그래머가 되려면 더 많은 ” oomph “가 더 있어야합니다. 더 이상 메모리 할당 자와 같은 것을 피하십시오.

그리고 이것은 우리가 256 바이트를 할당하고 결과 문자열이 더 큰 고정 크기 버퍼를 할당하는 경우가 없습니다. 함수가 버퍼 오버런을 피하더라도 (예 : sprintf_s) 이러한 오류로부터 적절하게 복구 할 수있는 더 많은 코드가 있습니다. 이러한 오류는 할당 자 사례에서 생략 할 수 있습니다. 하드웨어의 물리적 주소 지정 공간 (위의 코드가 처리하지만 처리 할 필요는 없음)을 모두 사용하지 않는 한 할당 자 사례에서 이러한 경우를 처리 할 필요가 없습니다. div id = “f02efd3394”>

사전 할당 된 버퍼 부족 ” 개별적으로 ” 메모리 부족 “).

답변

당신이 제안하는 것은 나쁜 코드 냄새, 대안 3이 나에게 가장 잘 들립니다. 또한 @ gnasher729처럼 잘못된 언어를 사용하고 있다고 생각합니다.

댓글

답변

솔직히 문자열 반환이 복잡하고 작업 집약적이며 오류가 발생하기 쉬운 작업이 아닌 다른 언어로 전환 할 수 있습니다.

코드의 99 %를 변경하지 않고 그대로 둘 수있는 C ++ 또는 Objective-C를 고려할 수 있습니다.

답글 남기기

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