문자열을 받아들이는 함수가 있습니다.
void log_out(char *);
그것을 호출 할 때 다음과 같이 형식이 지정된 문자열을 즉석에서 만들어야합니다.
int i = 1;
log_out("some text %d", i);
ANSI C에서 어떻게해야합니까?
오직, sprintf()
int를 반환하기 때문에 이것은 적어도 3 개의 명령을 작성해야 함을 의미합니다.
char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);
단축 할 방법이 있습니까?
답변
sprintf를 사용하십시오 .
int sprintf ( char * str, const char * format, ... );
형식화 된 데이터를 문자열에 쓰기 printf에서 형식이 사용 된 경우 인쇄되는 것과 동일한 텍스트로 문자열을 작성하지만 인쇄되는 대신 내용은 str이 가리키는 버퍼에 C 문자열로 저장됩니다.
버퍼의 크기는 전체 결과 문자열을 포함 할 수있을만큼 충분히 커야합니다 (안전한 버전은 snprintf 참조).
종료 널 문자는 컨텐츠 뒤에 자동으로 추가됩니다.
형식 매개 변수 뒤에 함수는 형식에 필요한만큼의 추가 인수를 예상합니다.
매개 변수 :
str
결과 C- 문자열이 저장되는 버퍼에 대한 포인터. 버퍼는 결과 문자열을 포함 할 수있을만큼 커야합니다.
format
printf의 형식과 동일한 사양을 따르는 형식 문자열을 포함하는 C 문자열 (자세한 내용은 printf 참조).
... (additional arguments)
형식 문자열에 따라 함수는 형식 문자열에서 형식 지정자를 대체하는 데 사용할 값을 포함하는 추가 인수 시퀀스 (또는 n의 경우 저장 위치에 대한 포인터)를 예상 할 수 있습니다. 최소한 형식 지정자에 지정된 값의 수만큼 이러한 인수가 있어야합니다. 추가 인수는 함수에서 무시됩니다.
예:
// Allocates storage
char *hello_world = (char*)malloc(13 * sizeof(char));
// Prints "Hello world!" on hello_world
sprintf(hello_world, "%s %s!", "Hello", "world");
답변
POSIX-2008 호환 시스템 (최신 Linux)을 사용하는 경우 안전하고 편리한 asprintf()
기능을 사용할 수 있습니다. malloc()
메모리 가 충분하므로 최대 문자열 크기에 대해 걱정할 필요가 없습니다. 다음과 같이 사용하십시오.
char* string;
if(0 > asprintf(&string, "Formatting a number: %d\n", 42)) return error;
log_out(string);
free(string);
이것은 안전한 방식으로 문자열을 구성하기 위해 얻을 수있는 최소한의 노력입니다. sprintf()
당신이 질문에 준 코드는 깊이 결함이 :
-
포인터 뒤에 할당 된 메모리가 없습니다. 메모리의 임의의 위치에 문자열을 쓰고 있습니다!
-
썼더라도
char s[42];
괄호 안에 어떤 숫자를 넣어야할지 모르기 때문에 심각한 문제에 봉착 할 것입니다.
-
“안전한”변형을 사용 했더라도
snprintf()
문자열이 잘릴 위험이 있습니다. 로그 파일에 쓸 때 이는 비교적 사소한 문제이지만 유용했을 정보를 정확하게 잘라낼 수있는 가능성이 있습니다. 또한 후행 끝줄 문자를 잘라내어 실패한 줄 끝에 다음 로그 줄을 붙입니다. -
당신의 조합을 사용하려고하면
malloc()
하고snprintf()
모든 경우에 올바른 동작을 생산하기 위해, 당신은 약 두 배 많은 코드로 내가 주어진 것보다 함께 결국asprintf()
, 기본적으로의 기능을 재 프로그램asprintf()
.
스타일 매개 변수 목록 자체를 log_out()
취할 수 있는 래퍼를 제공하려는 경우를 인수로 취하는 printf()
변형 vasprintf()
을 사용할 수 있습니다 va_list
. 다음은 이러한 래퍼의 완벽하게 안전한 구현입니다.
//Tell gcc that we are defining a printf-style function so that it can do type checking.
//Obviously, this should go into a header.
void log_out_wrapper(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void log_out_wrapper(const char *format, ...) {
char* string;
va_list args;
va_start(args, format);
if(0 > vasprintf(&string, format, args)) string = NULL; //this is for logging, so failed allocation is not fatal
va_end(args);
if(string) {
log_out(string);
free(string);
} else {
log_out("Error while logging a message: Memory allocation failed.\n");
}
}
답변
printf 스타일 형식을 사용하여 만든 문자열을 간단한 문자열을 사용하는 이미 가지고있는 함수에 쉽게 전달할 수 있기를 원하는 것처럼 들립니다. 다음을 사용하여 래퍼 함수를 만들 수 있습니다 stdarg.h
시설을 vsnprintf()
(컴파일러 / 플랫폼에 따라 쉽게 사용하지 못할 수도 있습니다) :
#include <stdarg.h>
#include <stdio.h>
// a function that accepts a string:
void foo( char* s);
// You'd like to call a function that takes a format string
// and then calls foo():
void foofmt( char* fmt, ...)
{
char buf[100]; // this should really be sized appropriately
// possibly in response to a call to vsnprintf()
va_list vl;
va_start(vl, fmt);
vsnprintf( buf, sizeof( buf), fmt, vl);
va_end( vl);
foo( buf);
}
int main()
{
int val = 42;
foofmt( "Some value: %d\n", val);
return 0;
}
snprintf()
루틴 제품군의 좋은 구현 (또는 구현)을 제공하지 않는 플랫폼의 경우 Holger Weiss 의 거의 공개 도메인 snprintf()
을 성공적으로 사용 했습니다 .
답변
에 대한 코드가 있으면 log_out()
다시 작성하십시오. 대부분 다음을 수행 할 수 있습니다.
static FILE *logfp = ...;
void log_out(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(logfp, fmt, args);
va_end(args);
}
추가 로깅 정보가 필요한 경우 표시되는 메시지 전후에 인쇄 할 수 있습니다. 이렇게하면 메모리 할당 및 모호한 버퍼 크기 등이 절약됩니다. 아마도 logfp
0 (널 포인터) 으로 초기화 하고 그것이 널인지 확인하고 적절하게 로그 파일을 열어야 할 log_out()
필요가 있습니다. 그러나 기존의 코드는 어쨌든 그것을 처리해야합니다.
이 솔루션의 장점은 마치 변형 된 것처럼 간단히 호출 할 수 있다는 것입니다 printf()
. 실제로 printf()
.
에 대한 코드가없는 경우 log_out()
위에 설명 된 것과 같은 변형으로 대체 할 수 있는지 고려하십시오. 동일한 이름을 사용할 수 있는지 여부는 애플리케이션 프레임 워크와 현재 log_out()
함수 의 궁극적 인 소스에 따라 다릅니다 . 다른 필수 기능과 동일한 개체 파일에있는 경우 새 이름을 사용해야합니다. 정확히 복제하는 방법을 찾을 수 없다면 적절한 양의 메모리를 할당하는 다른 답변에 제공된 것과 같은 변형을 사용해야합니다.
void log_out_wrapper(const char *fmt, ...)
{
va_list args;
size_t len;
char *space;
va_start(args, fmt);
len = vsnprintf(0, 0, fmt, args);
va_end(args);
if ((space = malloc(len + 1)) != 0)
{
va_start(args, fmt);
vsnprintf(space, len+1, fmt, args);
va_end(args);
log_out(space);
free(space);
}
/* else - what to do if memory allocation fails? */
}
당연히 log_out_wrapper()
대신에 log_out()
-를 호출 하지만 메모리 할당 등은 한 번 수행됩니다. 나는 하나의 불필요한 바이트만큼 공간을 초과 할당 할 권리를 보유합니다. 반환 된 길이 vsnprintf()
에 종료 널이 포함 되는지 여부를 다시 확인 하지 않았습니다.
답변
sprintf를 사용하지 마십시오.
String-Buffer가 오버플로되고 프로그램이 충돌합니다.
항상 snprintf 사용
답변
저는 이것을하지 않았으므로 정답을 가리킬 것입니다.
C에는 <stdarg.h>
헤더를 사용하여 지정되지 않은 수의 피연산자를 취하는 함수에 대한 조항이 있습니다 . 함수를로 정의 하고 함수 내부를 void log_out(const char *fmt, ...);
가져올 수 va_list
있습니다. 그런 다음 메모리를 할당 vsprintf()
하고 할당 된 메모리, 포맷 및 va_list
.
또는이를 사용하여 sprintf()
메모리를 할당하고 형식화 된 문자열을 반환하는 것과 유사한 함수를 작성하여 위와 같이 다소간 생성 할 수 있습니다. 메모리 누수가 발생할 수 있지만 로그 아웃하는 경우 중요하지 않을 수 있습니다.
답변
http://www.gnu.org/software/hello/manual/libc/Variable-Arguments-Output.html 은 stderr로 인쇄하는 다음 예제를 제공합니다. 대신 로그 기능을 사용하도록 수정할 수 있습니다.
#include <stdio.h>
#include <stdarg.h>
void
eprintf (const char *template, ...)
{
va_list ap;
extern char *program_invocation_short_name;
fprintf (stderr, "%s: ", program_invocation_short_name);
va_start (ap, template);
vfprintf (stderr, template, ap);
va_end (ap);
}
vfprintf 대신 인쇄 할 적절한 버퍼를 제공해야하는 곳에 vsprintf를 사용해야합니다.