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
답변
가장 많이 본 방법은 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처럼 잘못된 언어를 사용하고 있다고 생각합니다.
댓글
- What 정확히 코드 냄새라고 생각하십니까? 자세히 설명해주세요.
- en.m.wikipedia.org/wiki/Cod 참조 e_smell 을 참조하세요. 그러나 문자열이 아닌 문자열을 인쇄 할 수 있도록 변환하는 것은 나쁜 습관입니다. 가짜 코딩에 대한 자세한 내용은 en.m.wikipedia.org/wiki/Design_smell 을 참조하세요.
답변
솔직히 문자열 반환이 복잡하고 작업 집약적이며 오류가 발생하기 쉬운 작업이 아닌 다른 언어로 전환 할 수 있습니다.
코드의 99 %를 변경하지 않고 그대로 둘 수있는 C ++ 또는 Objective-C를 고려할 수 있습니다.
sysctlbyname