Por que esse código corrompe a memória?
-
03-07-2019 - |
Pergunta
Esta é uma pergunta bastante iniciante que deve ser responsável razoavelmente rapidamente ...
Basicamente, após a primeira chamada para Printf dentro eco, o conteúdo de args está corrompido. Parece -me que estou passando as dicas incorretamente. Mas não consegue descobrir por quê?
#define MAX_PRINT_OUTPUT 4096
void Echo(char *args[MAX_COMMAND_ARGUMENTS], int argCount)
{
for (int i = 1; i < argCount; ++i)
{
Printf("%s ", args[i]);
Printf("\n");
}
};
void Printf(const char *output, ...)
{
va_list args;
char formattedOutput[MAX_PRINT_OUTPUT];
va_start(args, output);
vsnprintf(formattedOutput, sizeof(formattedOutput), output, args);
va_end(args);
g_PlatformDevice->Print(formattedOutput);
};
void RiseWindows::Print(const char *output)
{
//Corruption appears to occur as soon as this function is entered
#define CONSOLE_OUTPUT_SIZE 32767
char buffer[CONSOLE_OUTPUT_SIZE];
char *pBuffer = buffer;
const char *pOutput = output;
int i = 0;
while (pOutput[i] && ((pBuffer - buffer) < sizeof(buffer) - 1))
{
if (pOutput[i] == '\n' && pOutput[i+1] == '\r' )
{
pBuffer[0] = '\r';
pBuffer[1] = '\n';
pBuffer += 2;
++i;
}
else if (pOutput[i] == '\r')
{
pBuffer[0] = '\r';
pBuffer[1] = '\n';
pBuffer += 2;
}
else if (pOutput[i] == '\n')
{
pBuffer[0] = '\r';
pBuffer[1] = '\n';
pBuffer += 2;
}
else
{
*pBuffer = pOutput[i];
++pBuffer;
}
++i;
}
*pBuffer = 0;
SendMessage(this->ConsoleWindow.hwndBuffer, EM_LINESCROLL, 0, 0xffff);
SendMessage(this->ConsoleWindow.hwndBuffer, EM_SCROLLCARET, 0, 0);
SendMessage(this->ConsoleWindow.hwndBuffer, EM_REPLACESEL, 0, (LPARAM)buffer);
};
NOTA Este não é um código de produção, apenas prova de conceito.
EDITAR g_platformDevice é do tipo RiseWindows, se isso não estava claro ...
EDITAR Isso está em uma plataforma Windows XP em execução em vs2008
ATUALIZARPara qualquer pessoa interessada, o problema parece ter sido uma pilha de chamadas transbordada, mais abaixo na pilha, então essa outra grande matriz estava sendo definida. Refatorando isso eliminou a corrupção da memória. Então, atingiu o empilhamento de agressão!
Solução
Você não mencionou em qual ambiente esse código é executado. Pode ser que você esteja soprando sua pilha. Você está declarando uma matriz de 32767 bytes na pilha em RiseWindows :: Print. Em alguns ambientes de sistema incorporados com os quais estou familiarizado com isso, seria uma má notícia. Você pode aumentar o tamanho da pilha e/ou alocar esse buffer na pilha apenas para testar essa teoria? Você pode querer fazer com que esse buffer um vetor de std ::, ou possivelmente, um vetor de membro privado para evitar alocá -lo e realocá -lo toda vez que você ligar para a impressão.
Nesse sentido, qual o tamanho do max_print_output?
Outras dicas
Provavelmente não é o bug que você está perguntando, mas no seu loop você é o duplo incremento pbuffer em alguns casos, o que pode estar empurrando você no final do buffer, porque você só verifica o comprimento-1 (para terminação nula).
Presidente aleatório: tenho a sensação de que o problema é causado por esta linha no printf:
char formattedOutput[MAX_PRINT_OUTPUT];
A razão pela qual acho que isso é porque você tem alguns indicadores obviamente declarados e alguns indicados obviamente indicados. Uma variedade de chars é um ponteiro - de maneira alguma, mas não é óbvio. Na definição de função de eco args é definida como uma matriz bidimensional porque você a tem como
*args[MAX_COMMAND_ARGS]
Você quer isso? Meu palpite é que algo está sendo aprovado sem intenção como uma referência em vez de um valor, porque o que é um ponteiro vs. matriz é vagamente definido e você está passando um ponteiro para um ponteiro que é o início de uma matriz em vez de um ponteiro que é o início de uma matriz. Como você disse que é corrompido quando você entra no RiseWindows :: Imprima meu palpite é que você está passando a coisa errada.
Além disso, um ponteiro const para um char preserva apenas o valor do ponteiro, até onde eu sei, não o valor do conteúdo no ponteiro.
Você tentou a estratégia de divisão e conquista?
- Comece a comentar as linhas até que funcione.
- Uma vez que funcione corretamente as linhas de descomamento até você pressionar onde está o erro.
Observe a memória apontada por args [] na janela separada enquanto você passo a passo também pode ser útil.
Posso sugerir passar com um depurador para ver onde o código corrompe?
while (Poutput [i] && ((pbuffer - buffer) <sizeof (buffer) - 1))
mudar para:
while (Poutput [i] && ((pbuffer - buffer) <sizeof (buffer) - 2))
Você está escrevendo 2 caracteres por vez, então precisa ter espaço para dois caracteres.
Não tenho certeza se é como deve funcionar ou não, mas o eco não impede o primeiro elemento de args
// Changed i=1 to i=0;
for (int i = 0; i < argCount; ++i)
{
Printf("%s ", args[i]);
Printf("\n");
}
Você deseja mover seu #Define da chamada de função para o topo do arquivo:
Provavelmente, isso não está causando a corrupção neste caso, mas não é padrão e pode facilmente causar problemas na linha.
A teoria do trabalho é que estamos soprando a pilha com:
char buffer [console_output_size]; char *pbuffer = buffer;
Em vez disso, tente:
char *pbuffer = novo char [console_output_size];
E depois lembre -se de ligar para excluir [] pbuffer no final.
Eu realmente não investigei isso, mas você tem seus tipos confusos ... isso é extremamente pedante, mas faz a diferença em C.
Aqui, você tem uma única variedade de char.
char formattedOutput[MAX_PRINT_OUTPUT];
E aqui, você tem uma função que espera um ponteiro const Char.
void RiseWindows::Print(const char *output)
Tentar:
void RiseWindows::Print(const char output[])
Além disso, percebo que você está modificando a memória nesses buffers - tem certeza de que pode fazer isso? No mínimo, tenho certeza de que você não pode usar arbitUamente mais sem alocar mais memória. (Dica dica!)
Eu alocaria minha própria matriz e copiaria a string nela. Eu usaria funções de string para substituir as linhas de novo conforme aplicável.
Finalmente, sugiro fortemente que você use a string std :: aqui. (Embora você não consiga colocá -los nas coisas de varargs - você terá que usar as cordas C para elas, mas copie -as de volta para STD :: String's quando puder).