Pergunta

Eu tenho este pedaço de código (resumido) ...

AnsiString working(AnsiString format,...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

E, na base de que passagem por referência é o preferido sempre que possível, o texto foi alterado desta forma.

AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}

Meu código de chamada é assim: -

AnsiString s1, s2;
    s1 = working("Hello %s", "World");
    s2 = broken("Hello %s", "World");

Mas, S1 contém "Olá Mundo", enquanto s2 tem "Olá (null)". Eu acho que isso é devido às obras va_start maneira, mas eu não sei exatamente o que está acontecendo.

Foi útil?

Solução

Se você olhar para o que va_start expande para fora, você vai ver o que está acontecendo:

va_start(argptr, format); 

torna-se (aproximadamente)

argptr = (va_list) (&format+1);

Se format é um tipo de valor, ele é colocado na pilha direita antes de todos os argumentos variádicos. Se format é um tipo de referência, apenas o endereço é colocado na pilha. Quando você toma o endereço da variável de referência, você obter o endereço ou a variável original (neste caso de um AnsiString temporário criado antes de chamar quebrado), não o endereço do argumento.

Se você não quer passar em torno de classes completas, suas opções são: ou passagem pelo ponteiro, ou colocar em um argumento fictício:

AnsiString working_ptr(const AnsiString *format,...)
{
    ASSERT(format != NULL);
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format->c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");

ou

AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, dummy);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

s1 = working_dummy("Hello %s", 0, "World");

Outras dicas

Aqui está o que o C ++ padrão (18,7 - Outro suporte de tempo de execução) diz sobre va_start() (grifo meu):

As restrições que a ISO C coloca em o segundo parâmetro para o macro va_start() no cabeçalho <stdarg.h> são diferentes neste Padrão internacional. o parâmetro parmN é o identificador do parâmetro mais à direita na variável lista de parâmetros da função definição (o que pouco antes da ...). Se o parmN parâmetro é declarado com uma função, uma matriz ou referência tipo, ou com um tipo que não é compatível com o tipo de que os resultados quando passar um argumento para que não há nenhum parâmetro, o comportamento é indefinido .

Como já foi mencionado, usando varargs em C ++ é perigoso se você usá-lo com itens não-linear, C (e possivelmente até mesmo de outras maneiras).

Dito isto - eu ainda usar printf () o tempo todo ...

Uma análise bom porque você não quer que isso é encontrado em N0695

De acordo com as Normas de Codificação C ++ (Sutter, Alexandrescu):

VarArgs nunca deve ser usado com C ++:

Eles não são do tipo seguro e ter um comportamento indefinido para objetos do tipo classe, que é provavelmente causando o problema.

Aqui está a minha solução fácil (compilado com Visual C ++ 2010):

void not_broken(const string& format,...)
{
  va_list argptr;
  _asm {
    lea eax, [format];
    add eax, 4;
    mov [argptr], eax;
  }

  vprintf(format.c_str(), argptr);
}

Nota lateral:

O comportamento para tipos de classe como varargs argumentos podem ser indefinido, mas é consistente na minha experiência. O compilador empurra sizeof (classe) da memória da classe na pilha. Ou seja, em pseudo-código:

alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);

Para um exemplo muito interessante deste sendo utilizados de uma forma muito criativa, a observação que você pode passar uma instância de CString no lugar de um LPCTSTR para um varargs função diretamente, e ele funciona, e há não lançando os envolvidos. Eu deixá-lo como um exercício para o leitor a descobrir como eles fizeram esse trabalho.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top