¿Cuál es la diferencia entre Delphi TStringList y String?
Cadena es un tipo de datos que almacena cadenas.
Introducción a los principios de los tipos de cadenas en Delphi
La operación de cadenas en Delphi es muy simple, pero la situación detrás de escena es bastante complicada. El método tradicional de operación de cadenas de Pascal es diferente al de Windows y absorbe el método de operación de cadenas del lenguaje C. Delphi de 32 bits agrega un tipo de cadena larga, que es potente y es el tipo de cadena predeterminado de Delphi.
Tipo de cadena En TurboPascal y Delphi de 16 bits de Borland, el tipo de cadena tradicional es una secuencia de caracteres, y el encabezado de la secuencia es un byte de longitud, que indica la longitud de la cadena actual. Dado que solo se utiliza un byte para representar la longitud de la cadena, la cadena no puede exceder los 255 caracteres. Esta limitación de longitud trae inconvenientes a las operaciones de cadenas, porque la longitud de cada cadena debe ser fija (el valor máximo es 255, por supuesto, también puede declarar una cadena más corta para ahorrar espacio de almacenamiento).
Los tipos de cadenas son similares a los tipos de matrices. De hecho, una cadena es casi una matriz de tipos de caracteres, por lo que el hecho de que pueda acceder a los caracteres de una cadena utilizando el símbolo [] ilustra el punto anterior.
Para superar las limitaciones de las cadenas Pascal tradicionales, Delphi de 32 bits añade soporte para cadenas largas. Entonces * * * hay tres tipos de cadenas:
ShortString El tipo de cadena corta es también el tipo de cadena Pascal tradicional mencionado anteriormente. Estas cadenas sólo pueden tener un máximo de 255 caracteres, lo mismo que las cadenas en Delphi de 16 bits. Cada carácter de una cadena corta es de tipo ANSIChar (tipo de carácter estándar).
El tipo de cadena larga ANSIString es un tipo de cadena de longitud variable recién agregado. Este tipo de cadena se asigna dinámicamente en la memoria mediante el recuento de referencias y la tecnología de copia en escritura. No hay límite para la longitud de dichas cadenas (¡se pueden almacenar hasta 2 mil millones de caracteres!) y su tipo de carácter también es ANSIChar.
El tipo de cadena larga WideString es similar al tipo ANSIString, excepto que se basa en el tipo de carácter WideChar, que es un carácter Unicode de doble byte.
Uso de cadenas largas
Si una cadena se define simplemente como una cadena, entonces puede ser una cadena corta o una cadena larga ANSI, según el valor de $H. directiva de compilación, $H (provincia exacta) representa una cadena larga (tipo ANSIString). Las cadenas largas son las cadenas utilizadas por los controles en la biblioteca Delphi.
La cadena larga de Delphi se basa en el mecanismo de recuento de referencias. Utiliza el recuento de referencias para rastrear las variables de cadena en la memoria que hacen referencia a la misma cadena cuando la cadena ya no se utiliza, es decir, cuando la cadena ya no se utiliza. El recuento de referencias llega a cero, la memoria se libera.
Si desea aumentar la longitud de una cadena y no hay memoria libre adyacente a la cadena, es decir, no hay espacio de expansión para la cadena en la misma unidad de almacenamiento, entonces esta cadena debe copiarse completamente en otra ubicación de almacenamiento. Cuando esto sucede, el programa de soporte de tiempo de ejecución de Delphi reasignará la memoria para la cadena de una manera completamente transparente.
Para asignar de manera eficiente el espacio de almacenamiento requerido, puede utilizar el proceso SetLength para establecer la longitud máxima de la cadena, por ejemplo:
SetLength (String1, 200
The); El proceso SetLength solo completa una solicitud de memoria, la memoria en realidad no se asigna. Solo reserva la memoria que se necesitará en el futuro sin utilizar realmente esta memoria. Esta tecnología se originó en el sistema operativo Windows y ahora Delphi la utiliza para asignar memoria dinámicamente. Por ejemplo, cuando solicita una matriz grande, el sistema reserva memoria de la matriz pero no la asigna.
Generalmente, no es necesario establecer la longitud de la cadena, pero cuando necesita pasar una cadena larga como parámetro a una función API (después de la conversión de tipo), debe usar SetLength para reservar memoria. para la cadena. Espacio, lo explicaré más adelante.
Observe las cadenas en la memoria
Para ayudarlo a comprender mejor los detalles de administración de memoria de las cadenas, escribí un ejemplo simple StrRef. En el programa declaro dos cadenas completas: Str1 y Str2. Cuando se presiona el primer botón, el programa asigna una constante de cadena a la primera variable y luego asigna la primera variable a la segunda variable:
str 1:= 'Hola'
str 2:= str 1;
Además de la manipulación de cadenas, este programa también utiliza la siguiente función StringStatus para mostrar el estado interno de una cadena en un cuadro de lista:
Función estado de cadena(const Str: cadena): cadena;
Inicio
Resultado: = 'Dirección:' IntToStr (Entero (Str))
, Longitud: ' IntToStr (Longitud (Str))
, Referencia: ' inttostr(pinteger(integer(str)-8)^)
, Valor: ' Str;
End;
En la función StringStatus, es muy importante pasar una cadena con parámetros constantes. Pasar por copia (parámetro de valor) tendrá efectos secundarios porque se generará una referencia adicional a la cadena durante la ejecución de la función; por el contrario, pasar por parámetro de referencia (var) o constante (const) no producirá esta situación; Como en este ejemplo no se espera que se modifique la cadena, se eligieron argumentos constantes. Para obtener la dirección de memoria de una cadena (esto ayuda a identificar el contenido real de la cadena y también ayuda a observar si dos variables de cadena diferentes se refieren a la misma área de memoria), convierto el tipo de cadena a través de un mapa de tipos a entero . Una cadena es en realidad una referencia, es decir, un puntero: la variable de cadena almacena la dirección de memoria real de la cadena.
Para extraer la información del recuento de referencias, aproveché un hecho poco conocido: la longitud de la cadena y la información del recuento de referencias en realidad se almacenan en la cadena, entre el contenido real y la ubicación de memoria señalada por el Anteriormente, su desplazamiento negativo era -4 para la longitud de la cadena (este valor se obtiene fácilmente usando la función Longitud) y -8 para el recuento de referencia.
Pero asegúrese de recordar que la información interna anterior sobre el desplazamiento puede cambiar en futuras versiones de Delphi. Es difícil garantizar que las características no escritas en documentos oficiales de Delphi permanezcan sin cambios en el futuro.
Al ejecutar este ejemplo, verá que las dos cadenas tienen el mismo contenido, la misma ubicación de memoria y un recuento de referencias de 2, como se muestra en la parte superior del cuadro de lista en la Figura 7.1. Ahora, si cambia el valor de una de las cadenas, la dirección de memoria de la cadena actualizada también cambiará. Esto es el resultado de la tecnología de copia sobre escritura.
El código del evento OnClick del segundo botón (Cambiar) es el siguiente, y el resultado se muestra en la segunda parte del list box 7.1:
Procedimiento TFormStrRef. BtnChangeClick(remitente: al objeto);
Inicio
str 1[2]:= ' a ';
Cuadro de lista 1. elementos . Add(' str 1[2]:= '' a '');
Cuadro de lista 1. elementos Add(' str 1-' string status(str 1));
Cuadro de lista 1. items . Add(' str 2-' string status(str 2));
End;
Tenga en cuenta que BtnChangeClick solo se puede ejecutar después de ejecutar BtnAssignClick. Por lo tanto, el segundo botón no se puede utilizar después de iniciar el programa (la propiedad Activada del botón se establece en falso después de completar el primer método y se activa el segundo botón); Eres libre de ampliar este ejemplo y utilizar la función StringStatus para explorar las características de cadenas largas en otras situaciones.
La asignación dinámica puede utilizar cualquier función para asignar memoria. De hecho, el sistema finalmente llama a GetMem. Otros Nuevo, AllocMem, SetLength, etc. Simplemente realice alguna inicialización además de llamar a GetMem, como borrar la memoria. La liberación se puede realizar a través de Dispose o FreeMem, y el sistema eventualmente llamará a FreeMem. Disponer es equivalente a Finalizar (p). FreeMem(p);
La función de Finalize es solo liberar automáticamente cadenas y matrices dinámicas en estructuras o matrices, mientras que FreeMem libera directamente la memoria a la que apunta el puntero, por ejemplo:
Tipo
TMyRec = registro
Nombre: cadena
x, Y: entero
Fin;
pmy rec = ^tmyrec;
Definir variable
MyRec: pmy rec;
Inicio
Nuevo(MyRec); // El compilador calculará automáticamente la cantidad de memoria a asignar en función del tamaño de MyRec y luego generará código para llamar a GetMem y borrar el campo Nombre que contiene.
Myrek. name: = str 1 str 2;
Disposition (MyRec); //Además de llamar a FreeMem para liberar la memoria de la estructura MyRec, la memoria utilizada por Name también se borrará automáticamente (si la cadena recuento de referencias señalado por Nombre = 1);
//FreeMem(my rec); si llama a FreeMem directamente para liberar MyRec, provocará una pérdida de memoria debido a la cadena señalada por MyRec. Nombre no publicado (recuento de referencias -1).
Fin;
Debido a la particularidad de la gestión de memoria de cadenas de Delphi, existen muchas técnicas para aprovechar al máximo sus ventajas para generar código muy eficiente, como usar TList para guardar cadenas. (no TStringList), el enfoque general es guardar un puntero PString en TList. Items[i], por lo que es necesario reasignar una parte de la memoria y copiar la cadena original. La eficiencia es muy baja cuando la cantidad de datos es grande, pero si aprovecha al máximo el recuento de referencias de cadenas y las habilidades de conversión de tipos de conversión, puede guardar la cadena directamente como un puntero en TList.
Elemento[i]: Por ejemplo:
Definir variables
lista: TList;
GlobalString1, GlobalString2: cadena
.. .
Prueba del programa;
Definir variables
tmp: string;
Inicio
tmp: = global cadena 1 cadena global 2;
Lista. Add(pointer(tmp)); //Guarda tmp como puntero en la lista.
{Dado que tmp se liberará automáticamente al final del proceso de prueba, si sale directamente, se guardará un puntero no válido en la lista, por lo que debe engañar al compilador haciéndole creer que tmp ha sido liberado. liberado, lo que equivale a no cambiar el recuento de referencias de tmp (en el caso actual de 1), ejecute una declaración equivalente a tmp: = ' '. Debido a que direct tmp := ' ' modificará el recuento de referencias y posiblemente liberará memoria, usamos cast para convertir tmp a un número entero y establecer este número entero en 0 (es decir, nulo). Esta declaración es completamente equivalente a pointer(tmp):= nil; solo una preferencia personal. Me gusta usar números enteros (tmp): = 0.
}
Entero (tmp): = 0;
Fin;
1. El compilador de Delphi es un tipo de datos básico en Delphi, y PChar es solo un puntero a una cadena terminada en cero.
2. La memoria de la cadena se asigna en el montón y la variable de cadena es en realidad un puntero a una cadena terminada en cero. Al mismo tiempo, también tiene la función de recuento de referencias, guarda la longitud de la cadena y libera automáticamente el espacio ocupado cuando el recuento de referencias llega a cero.
3. Asignar una cadena a otra cadena es solo una simple asignación de puntero. No produce una acción de copia, solo aumenta el recuento de referencias de la cadena. el tipo de variable PCCAR al tipo de variable cadena producirá una acción de copia real, es decir, toda la cadena apuntada por PCCAR se copiará a la memoria asignada para la cadena;
5. El tipo de variable es simplemente asignar el valor del puntero de la cadena al tipo de variable PCCAR. El recuento de referencia de la cadena no cambiará debido a esta operación, porque en este caso PCCAR dependerá de la cadena. Cuando el recuento de referencias de la cadena es cero, es probable que PCCAR apunte a una dirección de memoria no válida, por lo que esta situación debe manejarse con cuidado en el programa.
6. La velocidad de procesamiento de PCCar es mucho más rápida que la de las cadenas, pero PCCar es una forma retrógrada de administrar las cadenas. PCCar existe solo por compatibilidad con tipos y sistemas operativos anteriores (a menudo se usa al llamar a la API de Windows), por lo que se recomienda usar cadenas normalmente.