c++ - resueltos - memoria dinamica en c
Un tipo de búfer dinámico en C++? (9)
No soy exactamente un novato en C ++, pero he tenido tratos serios en el pasado, por lo que mi conocimiento de sus instalaciones es bastante vago.
Estoy escribiendo un programa rápido de prueba de concepto en C ++ y necesito un búfer de datos binarios de tamaño dinámico. Es decir, recibiré datos de un socket de red y no sé cuánto habrá (aunque no más de unos pocos MB). Podría escribir un búfer de este tipo, pero ¿para qué molestarme si la biblioteca estándar probablemente ya tenga algo? Estoy usando VS2008, por lo que alguna extensión específica de Microsoft está bien para mí. Solo necesito cuatro operaciones:
- Crear el búfer
- Escribir datos en el búfer (basura binaria, no terminada en cero)
- Obtenga los datos escritos como una matriz char (junto con su longitud)
- Libera el búfer
¿Cuál es el nombre de la clase / conjunto de funciones / lo que necesito?
Añadido: Varios votos van a std::vector
. Todo bien y bien, pero no quiero insertar varios MB de byte a byte de datos. El zócalo me dará datos en pocos trozos grandes de KB, así que me gustaría escribirlos todos a la vez. Además, al final necesitaré obtener los datos como un simple carácter *, ya que tendré que pasar todo el blob a algunas funciones de la API de Win32 sin modificar.
Con respecto a su comentario "No veo un anexo ()", la inserción al final es la misma cosa.
vec.insert (vec.end,
Quieres un std::vector
:
std::vector<char> myData;
vector
automáticamente asignará y desasignará su memoria para usted. Use push_back
para agregar nuevos datos (el vector
cambiará de tamaño para usted si es necesario), y el operador de indexación []
para recuperar los datos.
Si en algún momento puede adivinar la cantidad de memoria que necesitará, sugiero la reserve
llamadas para que los siguientes push_back
no tengan que reasignar tanto.
Si desea leer en un trozo de memoria y agregarlo a su búfer, lo más fácil probablemente sería algo como:
std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
char rawBuffer[BufferSize];
const unsigned bytesRead = get_network_data(rawBuffer, sizeof(rawBuffer));
if (bytesRead <= 0) {
break;
}
myData.insert(myData.end(), rawBuffer, rawBuffer + bytesRead);
}
myData
ahora tiene todos los datos leídos, leyendo parte por parte. Sin embargo, estamos copiando dos veces.
En lugar de eso intentamos algo como esto:
std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
const size_t oldSize = myData.size();
myData.resize(myData.size() + BufferSize);
const unsigned bytesRead = get_network_data(&myData[oldSize], BufferSize);
myData.resize(oldSize + bytesRead);
if (bytesRead == 0) {
break;
}
}
Que se lee directamente en el búfer, a costa de la asignación excesiva de vez en cuando.
Esto se puede hacer más inteligente, por ejemplo, duplicando el tamaño del vector para cada tamaño para amortizar los tamaños, como lo hace la primera solución de manera implícita. Y, por supuesto, puede reserve()
un búfer mucho más grande por adelantado si tiene un conocimiento a priori del tamaño probable del búfer final, para minimizar el tamaño.
Ambos quedan como un ejercicio para el lector. :)
Finalmente, si necesita tratar sus datos como una matriz en bruto:
some_c_function(myData.data(), myData.size());
std::vector
está garantizado para ser contiguo.
Si usa std :: vector, solo lo está usando para administrar la memoria en bruto por usted. Podría simplemente hacer de malloc
el búfer más grande que crea que necesitará, y realizar un seguimiento de los bytes de lectura / offset de escritura hasta el momento (son lo mismo). Si llegas al final ... ya sea realloc
o elige una manera de fallar.
Lo sé, no es muy C ++ y, pero este es un problema simple y las otras propuestas parecen ser formas pesadas de introducir una copia innecesaria.
Un voto más para std :: vector. Código mínimo, omite la copia adicional. Código de GMan:
std::vector<char> buffer;
static const size_t MaxBytesPerRecv = 1024;
size_t bytesRead;
do
{
const size_t oldSize = buffer.size();
buffer.resize(oldSize + MaxBytesPerRecv);
bytesRead = receive(&buffer[oldSize], MaxBytesPerRecv); // pseudo, as is the case with winsock recv() functions, they get a buffer and maximum bytes to write to the buffer
myData.resize(oldSize + bytesRead); // shrink the vector, this is practically no-op - it only modifies the internal size, no data is moved/freed
} while (bytesRead > 0);
En cuanto a las funciones de WinAPI, use & buffer [0] (sí, es un poco torpe, pero así es) para pasar a los argumentos char *, buffer.size () como longitud.
Y una nota final, puede usar std :: string en lugar de std :: vector, no debería haber ninguna diferencia (excepto que puede escribir buffer.data () en lugar de & buffer [0] si el buffer es una cadena)
Una alternativa que no es de STL pero podría ser útil - Boost.Circular buffer
Use std::vector , una matriz en crecimiento que garantiza que el almacenamiento sea contiguo (su tercer punto).
basic_streambuf un vistazo a Boost basic_streambuf , que está diseñado para este tipo de propósito. Si no puedes (o no quieres) usar Boost, consideraría std::basic_streambuf
, que es bastante similar, pero un poco más de trabajo para usar. De cualquier manera, básicamente se deriva de esa clase base y sobrecarga underflow()
para leer datos del socket en el búfer. Normalmente, adjuntará un std::istream
al búfer, de modo que otro código lo lea de la misma forma en que lo haría el usuario desde el teclado (o lo que sea).
std::string
funcionaría para esto:
- Soporta nulos incrustados.
- Puede agregarle fragmentos de datos de múltiples bytes llamando a
append()
con un puntero y una longitud. - Puede obtener su contenido como una matriz char llamando a los
data()
y la longitud actual llamando asize()
olength()
. - La liberación del búfer es manejada automáticamente por el destructor, pero también puede llamar a
clear()
para borrar su contenido sin destruirlo.
std::vector<unsigned char> buffer;
Cada push_back agregará un nuevo carácter al final (se reasignará si es necesario). Puede llamar a Reserva para minimizar el número de asignaciones si sabe aproximadamente cuántos datos espera.
buffer.reserve(1000000);
Si tienes algo como esto:
unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);