versiones software php5 net informacion español descripcion c memcpy strcpy

c - php5 - software de php



C strcpy()-mal? (17)

Bueno, strcpy () no es tan malvado como strdup (), al menos strcpy () es parte del Estándar C.

Algunas personas parecen pensar que la función strcpy() C es mala o mala. Aunque admito que generalmente es mejor usar strncpy() para evitar desbordamientos de búfer, la siguiente (una implementación de la función strdup() para aquellos que no tienen la suerte de tenerla) usa con seguridad strcpy() y nunca debe desbordarse:

char *strdup(const char *s1) { char *s2 = malloc(strlen(s1)+1); if(s2 == NULL) { return NULL; } strcpy(s2, s1); return s2; }

*s2 Se garantiza que *s2 tiene suficiente espacio para almacenar *s1 , y el uso de strcpy() nos evita tener que almacenar el resultado de strlen() en otra función para usar más adelante como el parámetro de longitud innecesaria (en este caso) para strncpy() . Sin embargo, algunas personas escriben esta función con strncpy() , o incluso memcpy() , que requieren un parámetro de longitud. Me gustaría saber qué piensa la gente sobre esto. Si cree que strcpy() está a salvo en ciertas situaciones, dígalo. Si tiene una buena razón para no usar strcpy() en esta situación, indíquelo. Me gustaría saber por qué podría ser mejor usar strncpy() o memcpy() en situaciones como esta. Si cree que strcpy() está bien, pero no aquí, explique.

Básicamente, solo quiero saber por qué algunas personas usan memcpy() cuando otras usan strcpy() y aún otras usan plain strncpy() . ¿Hay alguna lógica para preferir uno sobre los tres (sin tener en cuenta las comprobaciones del búfer de los dos primeros)?


Creo que Strncpy también es malvado.

Para protegerse realmente de los errores de programación de este tipo, debe hacer que sea imposible escribir código que (a) se vea bien y (b) supere un búfer.

Esto significa que necesita una abstracción de cadena real, que almacena el búfer y la capacidad de forma opaca, los une, para siempre y comprueba los límites. De lo contrario, terminas pasando cadenas y sus capacidades en toda la tienda. Una vez que llegue a las operaciones de cadena reales, como modificar el medio de una cadena, es casi tan fácil pasar la longitud incorrecta a strncpy (y especialmente a strncat), como llamar a strcpy con un destino demasiado pequeño.

Por supuesto, todavía puede preguntar si usar strncpy o strcpy en la implementación de esa abstracción: strncpy es más seguro allí siempre que esté completamente convencido de lo que hace. Pero en el código de la aplicación de manejo de cadenas, confiar en strncpy para evitar los desbordamientos de búfer es como usar la mitad de un condón.

Por lo tanto, su reemplazo de strdup podría verse algo como esto (el orden de las definiciones cambió para mantenerlo en suspenso):

string *string_dup(const string *s1) { string *s2 = string_alloc(string_len(s1)); if (s2 != NULL) { string_set(s2,s1); } return s2; } static inline size_t string_len(const string *s) { return strlen(s->data); } static inline void string_set(string *dest, const string *src) { // potential (but unlikely) performance issue: strncpy 0-fills dest, // even if the src is very short. We may wish to optimise // by switching to memcpy later. But strncpy is better here than // strcpy, because it means we can use string_set even when // the length of src is unknown. strncpy(dest->data, src->data, dest->capacity); } string *string_alloc(size_t maxlen) { if (maxlen > SIZE_MAX - sizeof(string) - 1) return NULL; string *self = malloc(sizeof(string) + maxlen + 1); if (self != NULL) { // empty string self->data[0] = ''/0''; // strncpy doesn''t NUL-terminate if it prevents overflow, // so exclude the NUL-terminator from the capacity, set it now, // and it can never be overwritten. self->capacity = maxlen; self->data[maxlen] = ''/0''; } return self; } typedef struct string { size_t capacity; char data[0]; } string;

El problema con estas abstracciones de cadena es que nadie puede estar de acuerdo en una (por ejemplo, si las idiosincrasias de strncpy mencionadas en los comentarios anteriores son buenas o malas, ya sea que necesite cadenas inmutables y / o de copia en escritura que compartan búferes cuando cree una subcadena). , etc). Entonces, aunque en teoría solo debes sacar uno del estante, puedes terminar con uno por proyecto.


El mal viene cuando la gente lo usa así (aunque lo que sigue es súper simplificado):

void BadFunction(char *input) { char buffer[1024]; //surely this will **always** be enough strcpy(buffer, input); ... }

Que es una situación que pasa sorprendiendo a menudo.

Pero sí, strcpy es tan bueno como strncpy en cualquier situación en la que esté asignando memoria para el búfer de destino y ya haya usado strlen para encontrar la longitud.


En la situación que usted describe, strcpy es una buena opción. Este strdup solo tendrá problemas si el s1 no se terminó con un ''/ 0''.

Me gustaría agregar un comentario que indique por qué no hay problemas con Strcpy, para evitar que otros (y usted dentro de un año) se pregunten sobre su corrección por demasiado tiempo.

Strncpy a menudo parece seguro, pero puede meterte en problemas. Si la "cadena" de origen es más corta que el conteo, rellena el objetivo con ''/ 0'' hasta que alcance el conteo. Eso puede ser malo para el rendimiento. Si la cadena de origen es más larga que el conteo, strncpy no agrega un ''/ 0'' al destino. Es probable que esto te lleve a problemas más adelante cuando esperas que una cadena "/ 0 ''termine". ¡Así que strncpy también debe usarse con precaución!

Solo usaría memcpy si no estuviera trabajando con cadenas terminadas en ''/ 0'', pero eso parece ser una cuestión de gusto.


Esta respuesta utiliza size_t y memcpy() para un memcpy() rápido y simple.

Es mejor usar type size_t ya que es el tipo devuelto por strlen() y usado por malloc() y memcpy() . int no es el tipo adecuado para estas operaciones.

memcpy() rara vez es más lento que strcpy() o strncpy() y, a menudo, significativamente más rápido.

// Assumption: `s1` points to a C string. char *strdup(const char *s1) { size_t size = strlen(s1) + 1; char *s2 = malloc(size); if(s2 != NULL) { memcpy(s2, s1, size); } return s2; }

§7.1.1 1 "Una cadena es una secuencia contigua de caracteres terminada por e incluyendo el primer carácter nulo. ..."


Estoy de acuerdo. Sin embargo, recomendaría contra strncpy() , ya que siempre ajustará la salida a la longitud indicada. Esta es una decisión histórica, que creo que fue realmente desafortunada, ya que empeora seriamente el rendimiento.

Considere el código como este:

char buf[128]; strncpy(buf, "foo", sizeof buf);

Esto no escribirá los cuatro caracteres esperados en buf , sino que escribirá "foo" seguido de 125 caracteres cero. Si, por ejemplo, está recopilando muchas cadenas cortas, esto significará que su rendimiento real es mucho peor de lo esperado.

Si está disponible, prefiero usar snprintf() , escribiendo lo anterior como:

snprintf(buf, sizeof buf, "foo");

Si en lugar de copiar una cadena no constante, se hace así:

snprintf(buf, sizeof buf, "%s", input);

Esto es importante, ya que si la input contiene% de caracteres, snprintf() los interpretaría, abriendo estantes llenos de latas de gusanos.


Estoy siguiendo las reglas here . Dejame citar de ello

strncpy se introdujo inicialmente en la biblioteca de C para tratar con campos de nombre de longitud fija en estructuras tales como entradas de directorio. Dichos campos no se usan de la misma manera que las cadenas: el nulo final no es necesario para un campo de longitud máxima, y ​​la configuración de bytes finales para nombres más cortos en nulo garantiza comparaciones eficientes en el campo. strncpy no es por origen un `` strcpy limitado '''' y el Comité ha preferido reconocer la práctica existente en lugar de alterar la función para adaptarla mejor a tal uso.

Por esa razón, no obtendrá un ''/0'' final en una cadena si golpea la n no encuentra un ''/0'' en la cadena de origen hasta el momento. Es fácil utilizarlo de forma incorrecta (por supuesto, si conoce ese escollo, puede evitarlo). Como dice la cita, no fue diseñado como una clave acotada. Y preferiría no usarlo si no es necesario. En su caso, claramente su uso no es necesario y usted lo probó. ¿Por qué entonces usarlo?

Y en general, el código de programación también se trata de reducir la redundancia. Si sabe que tiene una cadena que contiene ''n'' caracteres, ¿por qué le dice a la función de copia que copie un máximo de n caracteres? Usted hace la comprobación redundante. Es poco sobre el rendimiento, pero mucho más sobre el código consistente. Los lectores se preguntarán qué puede hacer strcpy , que podría cruzar los n caracteres y que hace necesario limitar la copia, solo para leer en los manuales que esto no puede suceder en ese caso. Y ahí comienza la confusión entre los lectores del código.

Para el uso racional de str- , str- o strn- , elegí entre ellos como en el documento vinculado anterior:

mem- cuando quiero copiar bytes en bruto, como bytes de una estructura.

str- copiar una cadena terminada en nulo, solo cuando no se puede desbordar al 100%.

strn- al copiar una cadena terminada en nulo hasta cierta longitud, rellenando los bytes restantes con cero. Probablemente no es lo que quiero en la mayoría de los casos. Es fácil olvidar el hecho con el relleno cero final, pero es por diseño como lo explica la cita anterior. Entonces, solo codificaría mi propio bucle pequeño que copia caracteres, agregando un ''/0'' final:

char * sstrcpy(char *dst, char const *src, size_t n) { char *ret = dst; while(n-- > 0) { if((*dst++ = *src++) == ''/0'') return ret; } *dst++ = ''/0''; return ret; }

Sólo unas pocas líneas que hacen exactamente lo que quiero. Si quisiera "velocidad bruta" todavía puedo buscar una implementación portátil y optimizada que haga exactamente este trabajo de Strcpy acotado . Como siempre, primero el perfil y luego meterse con él.

Más tarde, C obtuvo funciones para trabajar con caracteres anchos, llamados wcs- y wcsn- (para C99 ). Yo los usaría de la misma manera.


Francamente, si está haciendo mucho manejo de cadenas en C, no debe preguntarse si debería usar strcpy o strncpy o memcpy . Debes encontrar o escribir una biblioteca de cadenas que proporcione una abstracción de nivel superior. Por ejemplo, uno que realiza un seguimiento de la longitud de cada cadena, le asigna memoria y le proporciona todas las operaciones de cadena que necesita.

Es casi seguro que esto garantizará que cometa muy pocos de los tipos de errores que generalmente se asocian con el manejo de la cadena C, como los desbordamientos de búfer, olvidando terminar una cadena con un byte NUL, etc.

La biblioteca podría tener funciones como estas:

typedef struct MyString MyString; MyString *mystring_new(const char *c_str); MyString *mystring_new_from_buffer(const void *p, size_t len); void mystring_free(MyString *s); size_t mystring_len(MyString *s); int mystring_char_at(MyString *s, size_t offset); MyString *mystring_cat(MyString *s1, ...); /* NULL terminated list */ MyString *mystring_copy_substring(MyString *s, size_t start, size_t max_chars); MyString *mystring_find(MyString *s, MyString *pattern); size_t mystring_find_char(MyString *s, int c); void mystring_copy_out(void *output, MyString *s, size_t max_chars); int mystring_write_to_fd(int fd, MyString *s); int mystring_write_to_file(FILE *f, MyString *s);

Escribí una para el proyecto Kannel , vea el archivo gwlib / octstr.h. Nos hizo la vida mucho más simple. Por otro lado, una biblioteca de este tipo es bastante simple de escribir, por lo que puede escribir una para usted, aunque solo sea como un ejercicio.


La razón por la que la gente usa strncpy no strcpy es porque las cadenas no siempre terminan en nulo y es muy fácil desbordar el búfer (el espacio que ha asignado para la cadena con strcpy) y sobrescribir un poco de memoria no relacionada.

Con strcpy esto puede suceder, con strncpy esto nunca sucederá. Es por eso que strcpy es considerado inseguro. El mal puede ser un poco fuerte.


Nadie ha mencionado strlcpy , desarrollado por Todd C. Miller y Theo de Raadt . Como dicen en su papel:

El error más común es que strncpy() NUL-termina la cadena de destino. Sin embargo, esto solo es cierto si la longitud de la cadena de origen es menor que el parámetro de tamaño. Esto puede ser problemático al copiar la entrada del usuario que puede ser de una longitud arbitraria en un búfer de tamaño fijo. La forma más segura de usar strncpy() en esta situación es pasarla menos que el tamaño de la cadena de destino y luego terminar la cadena a mano. De esa manera, se garantiza que siempre tendrá una cadena de destino terminada en NUL.

Hay contra-argumentos para el uso de strlcpy ; La página de Wikipedia hace notar que

Drepper argumenta que strlcpy y strlcat hacen que los errores de truncamiento sean más fáciles de ignorar para un programador y, por lo tanto, pueden introducir más errores de los que eliminan. *

Sin embargo, creo que esto solo obliga a las personas que saben lo que están haciendo para agregar una terminación NULL manual, además de un ajuste manual al argumento de strncpy . El uso de strlcpy hace que sea mucho más fácil evitar las strlcpy búfer porque no pudo terminar NULL su búfer.

También tenga en cuenta que la falta de strlcpy en glibc o las bibliotecas de Microsoft no debe ser una barrera para su uso; puede encontrar la fuente de strlcpy y sus amigos en cualquier distribución de BSD, y la licencia es probablemente amigable para su proyecto comercial / no comercial. Ver el comentario en la parte superior de strlcpy.c .


Personalmente, creo que si se puede probar que el código es válido, y se hace tan rápido, es perfectamente aceptable. Es decir, si el código es simple y, por lo tanto, obviamente correcto, entonces está bien.

Sin embargo, su suposición parece ser que mientras su función se está ejecutando, ningún otro hilo modificará la cadena a la que apunta s1 . ¿Qué sucede si esta función se interrumpe después de una asignación de memoria exitosa (y, por lo tanto, la llamada a strlen ), la cadena crece y bam tiene una condición de desbordamiento de búfer ya que strcpy copia al byte NULL?

Lo siguiente podría ser mejor:

char * strdup(const char *s1) { int s1_len = strlen(s1); char *s2 = malloc(s1_len+1); if(s2 == NULL) { return NULL; } strncpy(s2, s1, s1_len); return s2; }

Ahora, la cuerda puede crecer sin que sea tu culpa y estás a salvo. El resultado no será un dup, pero tampoco habrá desbordamientos locos.

La probabilidad de que el código que proporcionó sea realmente un error es bastante baja (bastante cerca de no existir, si no es que no existe, si está trabajando en un entorno que no admite el subprocesamiento en absoluto). Es solo algo en lo que pensar.

ETA : Aquí hay una implementación ligeramente mejor:

char * strdup(const char *s1, int *retnum) { int s1_len = strlen(s1); char *s2 = malloc(s1_len+1); if(s2 == NULL) { return NULL; } strncpy(s2, s1, s1_len); retnum = s1_len; return s2; }

Allí se está devolviendo el número de caracteres. Tú también puedes:

char * strdup(const char *s1) { int s1_len = strlen(s1); char *s2 = malloc(s1_len+1); if(s2 == NULL) { return NULL; } strncpy(s2, s1, s1_len); s2[s1_len+1] = ''/0''; return s2; }

Lo que terminará con un byte NUL . De cualquier manera es mejor que la que puse rápidamente en un principio.


Strlen encuentra hasta el último lugar de terminación nulo.

Pero en realidad los buffers no están terminados en nulo.

Es por eso que la gente usa diferentes funciones.


Tendré la memcpy usar memcpy si ya he calculado la longitud, aunque, por lo general, strcpy está optimizado para trabajar con palabras de máquina, cree que debería proporcionar a la biblioteca tanta información como sea posible, para que pueda utilizar la copia más óptima. mecanismo.

Pero para el ejemplo que da, no importa: si va a fallar, estará en el strlen inicial, por lo que strncpy no le compra nada en términos de seguridad (y presumiblemente strncpy es más lento que para ambos). verifique los límites y para nul), y cualquier diferencia entre memcpy y strcpy no vale la pena cambiar el código de forma especulativa.


Tu código es terriblemente ineficiente porque corre a través de la cadena dos veces para copiarlo.

Una vez en strlen ().

Luego otra vez en strcpy ().

Y no verifica s1 para NULL.

Almacenar la longitud en una variable adicional no le cuesta nada, mientras que ejecutar cada cadena dos veces para copiarla es un pecado fundamental.


memcpy puede ser más rápido que strcpy y strncpy porque no tiene que comparar cada byte copiado con ''/ 0'', y porque ya conoce la longitud del objeto copiado. Puede implementarse de manera similar con el dispositivo de Duff , o usar instrucciones de ensamblador que copien varios bytes a la vez, como movsw y movsd.


char *strdup(const char *s1) { char *s2 = malloc(strlen(s1)+1); if(s2 == NULL) { return NULL; } strcpy(s2, s1); return s2; }

Problemas:

  1. s1 no está terminado, strlen provoca el acceso de memoria no asignada, bloqueos de programa.
  2. s1 no está terminado, pero no provoca el acceso a la memoria de acceso a la memoria no asignada desde otra parte de su aplicación. Se devuelve al usuario (problema de seguridad) o se analiza en otra parte de su programa (aparece heisenbug).
  3. s1 no está terminado, strlen da como resultado un malloc que el sistema no puede satisfacer, devuelve NULL. strcpy se pasa a NULL, el programa se bloquea.
  4. s1 no está terminado, strlen da como resultado un malloc que es muy grande, el sistema asigna demasiada memoria para realizar la tarea en cuestión, se vuelve inestable.
  5. En el mejor de los casos, el código es ineficiente, strlen requiere acceso a todos los elementos de la cadena.

Probablemente hay otros problemas ... Mira, la terminación nula no siempre es una mala idea. Hay situaciones en las que, para la eficiencia computacional, o para reducir los requisitos de almacenamiento, tiene sentido.

Para escribir código de propósito general, por ejemplo, ¿la lógica de negocios tiene sentido? No.


char* dupstr(char* str) { int full_len; // includes null terminator char* ret; char* s = str; #ifdef _DEBUG if (! str) toss("arg 1 null", __WHENCE__); #endif full_len = strlen(s) + 1; if (! (ret = (char*) malloc(full_len))) toss("out of memory", __WHENCE__); memcpy(ret, s, full_len); // already know len, so strcpy() would be slower return ret; }