Las cadenas se interpretan como punteros cuando las pasa a una función en C, por ejemplo, func(str) pasará un puntero al bloque de memoria llamado str. Entonces, de forma predeterminada, las cadenas ya son punteros cuando se pasan a una función. ¿Hay alguna manera de pasar una cadena por valor en la que no tenga que terminar manipulando el contenido de str ?
Primero, ¿qué es una cadena? Es una matriz de caracteres y luego un \0 . Sólo para asegurarnos de que todos estamos de acuerdo en eso.
Entonces, la pregunta es, ¿puedes pasar una matriz de caracteres por valor? Y la respuesta es sí, pero solo si está dentro de una estructura o unión:
struct String40Chars { char str[41]; }; void f(struct String40Chars s) { // s was passed by value - including s.str }Sin embargo, no puede pasar una matriz directamente debido a una peculiaridad histórica:
void f(char str[41]) { // you would think this would work, // but actually, the compiler converts it to void f(char *str) }Eso es desafortunado para las personas que realmente quieren pasar matrices por valor.
Por suerte, normalmente no es necesario. En todos los casos que se me ocurren, un puntero es lo suficientemente bueno y también más rápido, ya que no necesita copiar la matriz. Tal vez pueda explicar por qué necesita pasar una matriz por valor.
PD: hablas de que "las cadenas son punteros", pero eso no tiene sentido. Las cadenas son matrices. Puede tener punteros a matrices, pero el puntero no es la matriz.
La única forma de lograr una semántica de valor con argumentos de cadena en C es tener una estructura o unión que envuelva la cadena.
Algo como:
#define STR_BY_VALUE_MAX_LENGTH 128 typedef struct StrByValue_ { char str[STR_BY_VALUE_MAX_LENGTH]; } StrByValue; void f(StrByValue strByVal) { // Changes to strByVal will not affect the caller } Por supuesto, la persona que llama tendrá que inicializar el objeto StrByValue copiando la cadena (por ejemplo, usando strcpy) en el campo str . También puede agregar un campo len a la estructura StrByValue si no desea utilizar cadenas terminadas en cero.
Otra respuesta es almacenar la cadena en la memoria de la pila, que no se puede cambiar de tamaño después de la inicialización, lo que no creo que sea un corredor largo. Según yo, la cadena debe almacenarse en la memoria del montón para que podamos expandirla más tarde, piense en una estructura de datos que se expande cuando se agrega algo al final.
Para implementar esto bien y con algo de sintaxis azucarada, necesitamos crear 3 funciones, a saber, init_string() , append_string() y free_string() .
init_string() : inicializa el puntero y asigna len = INIT_STR_LENappend_string() : se puede usar para la concatenación de dos cadenasfree_string() : libera el montón asignado ptr , por lo tanto, no hay fugas de memoria #include <stdio.h> #include <string.h> #include <stdlib.h> typedef struct { char *ptr; size_t len; } string; string init_string(const char *s) { string x; x.len = strlen(s); x.ptr = malloc(x.len + 1); if (!x.ptr) { fprintf(stderr, "err: memory can't be allocated\n"); exit(EXIT_FAILURE); } memcpy(x.ptr, s, x.len + 1); return x; } void append_string(string *s, const char *str) { if (!str || !s) { fprintf(stderr, "err: null was passed\n"); exit(EXIT_FAILURE); } size_t str_l = strlen(str); s->ptr = realloc(s->ptr, s->len + str_l + 1); if (!s->ptr) { fprintf(stderr, "err: memory can't be allocated\n"); exit(EXIT_FAILURE); } memcpy(s->ptr + s->len, str, str_l + 1); s->len += str_l; } void free_string(string *s) { if (!s) { fprintf(stderr, "err: null was passed\n"); exit(EXIT_FAILURE); } free(s->ptr); s->len = 0; } void foo(string a) // got the value of a { // cannot modified permanently } void bar(string *a) // got the address of a { // can be modified permanently } int main(void) { string str = init_string("Hello world, I'm a c-string."); puts(str.ptr); append_string(&str, "stackoverflow"); puts(str.ptr); free_string(&str); return EXIT_SUCCESS; }