resueltos - Devolución de la cadena C de una función
leer cadena de caracteres en c (12)
Basándose en su historia de fondo recién agregada con la pregunta, ¿por qué no simplemente devuelve un número entero de 1 a 12 para el mes y deja que la función main () use una instrucción switch o if-else para decidir qué imprimir? Ciertamente no es la mejor manera de ir, char * sería, pero en el contexto de una clase como esta, imagino que es probablemente la más elegante.
Estoy tratando de devolver una cadena C de una función pero no está funcionando. Aquí está mi código.
char myFunction()
{
return "My String";
}
En general, lo estoy llamando así:
int main()
{
printf("%s",myFunction());
}
También he probado otras formas para myFunction
pero no están funcionando. P.ej:
char myFunction()
{
char array[] = "my string";
return array;
}
Nota: ¡No tengo permitido usar punteros!
Poco conocimiento sobre este problema: hay una función que es averiguar qué mes es, por ejemplo; si es 1, entonces devuelve enero, etc.
Entonces cuando va a imprimir, está haciendo esto. printf("Month: %s",calculateMonth(month));
. Ahora el problema es cómo devolver esa cadena desde la función calculateMonth
.
Bueno, en su código está tratando de devolver un String
(en C
que no es más que un conjunto de caracteres terminados nulos), pero el tipo de retorno de su función es char
que está causando todos los problemas para usted. En su lugar, debe escribirlo de esta manera:
const char* myFunction() { return "My String"; }
Y siempre es bueno calificar tu tipo con const
al asignar literales en C a punteros ya que los literales en C no son modificables.
El tipo de devolución de función es un solo carácter. Debería devolver un puntero al primer elemento de la matriz de caracteres. Si no puedes usar punteros, entonces estás jodido. :(
La cadena de CA se define como un puntero a una matriz de caracteres.
Si no puede tener punteros, por definición no puede tener cadenas.
O qué tal este:
void print_month(int month)
{
switch (month)
{
case 0:
printf("january");
break;
case 1:
printf("february");
break;
...etc...
}
}
Y llámalo con el mes que calcules en otro lugar.
Saludos,
Sebastiaan
Puede crear la matriz en la persona que llama, que es la función principal, y pasar la matriz al destinatario que es su myFunction (). Por lo tanto, myFunction puede llenar la cadena en la matriz. Sin embargo, debe declarar myFunction () como
char* myFunction(char * buf, int buf_len){
strncpy(buf, "my string", buf_len);
return buf;
}
y en la función principal, mi función debe llamarse de esta manera
char array[51];
memset(array,0,51);/*all bytes are set to ''/0''*/
printf("%s", myFunction(array,50));/*buf_len arguement is 50 not 51. This is to make sure the string in buf is always null-terminated(array[50] is always ''/0'')*/
Sin embargo, el puntero todavía se usa.
Si realmente no puedes usar punteros, haz algo como esto:
char get_string_char(int index)
{
static char array[] = "my string";
return array[index];
}
int main()
{
for (int i = 0; i < 9; ++i)
printf("%c", get_string_char(i));
printf("/n");
return 0;
}
El número mágico 9 es horrible, este no es un ejemplo de buena programación. Pero usted consigue el punto. Tenga en cuenta que los punteros y las matrices son la misma cosa (un poco), así que esto es un poco engañoso.
¡Espero que esto ayude!
Su problema es con el tipo de devolución de la función, debe ser:
char *myFunction()
... y luego su formulación original funcionará.
Tenga en cuenta que no puede tener cadenas en C sin punteros, en algún lugar de la línea.
Además: suba las advertencias del compilador, debería haberle advertido sobre esa línea de retorno convirtiendo un char *
en char
sin un molde explícito.
Su prototipo de función indica que su función devolverá un carácter. Por lo tanto, no puedes devolver una cadena en tu función.
Tenga en cuenta esta nueva función:
const char* myFunction()
{
static char array[] = "my string";
return array;
}
Definí "matriz" como estática, de lo contrario, cuando la función finaliza, la variable (y el puntero que está devolviendo) se sale del alcance. Como esa memoria está asignada en la pila, se corrompe. La desventaja de esta implementación es que el código no es reentrante y no es seguro para subprocesos.
Otra alternativa sería utilizar malloc para asignar la cadena en el montón, y luego liberar en las ubicaciones correctas de su código. Este código será re-entract y thread safe.
EDITAR:
Como se señaló en el comentario, esta es una práctica muy mala, ya que un atacante puede inyectar código a su aplicación (necesita abrir el código usando gdb, luego hacer un punto de interrupción y modificar el valor de una variable devuelta para desbordar y divertirse solo comienza).
Si es mucho más recomendable dejar que la persona que llama maneje las asignaciones de memoria. Mira este nuevo ejemplo:
char* myFunction( char* output_str, size_t max_len )
{
const char *str = "my string";
size_t l = strlen(str);
if (l+1 > max_len) {
return NULL;
}
strcpy(str, str, l);
return input;
}
Tenga en cuenta que el único contenido que se puede modificar es el que el usuario. Otro efecto secundario: este código ahora es seguro para subprocesos, al menos desde el punto de vista de la biblioteca. El programador que llama a este método debe verificar que la sección de memoria utilizada sea segura para subprocesos.
Un char
es solo un carácter de un byte. No puede almacenar la cadena de caracteres, ni es un puntero (que aparentemente no puede tener). Por lo tanto, no puede resolver su problema sin usar punteros (para lo cual char[]
es azúcar sintáctico).
la firma de tu función debe ser:
const char * myFunction()
{
return "My String";
}
Editar:
Fondo:
Han pasado años desde este post y nunca pensé que sería votado, porque es tan fundamental para C & C ++. Sin embargo, un poco más de discusión debería estar en orden.
En C (y C ++ para el caso), una cadena es simplemente una matriz de bytes terminados con un byte cero. De ahí que el término "cadena-cero" se use para representar este sabor particular de la cadena. Hay otros tipos de cadenas, pero en C (y C ++), este sabor es inherentemente entendido por el lenguaje mismo. Otros lenguajes (Java, Pascal, etc.) usan diferentes metodologías para entender "mi cadena".
Si alguna vez utilizas la API de Windows (que está en C ++), verás con bastante regularidad parámetros de funciones como: "LPCSTR lpszName". La parte ''sz'' representa esta noción de ''cadena-cero'': una matriz de bytes con un terminador nulo (/ cero).
Aclaración:
Por el bien de esta ''introducción'', utilizo la palabra ''bytes'' y ''caracteres'' indistintamente, porque es más fácil de aprender de esta manera. Tenga en cuenta que hay otros métodos (caracteres anchos y sistemas de caracteres de varios bytes, mbcs) que se utilizan para manejar los caracteres internacionales. UTF-8 es un ejemplo de un mbcs. Por el bien de la introducción, silenciosamente ''omito'' todo esto.
Memoria:
Lo que esto significa es que una cadena como "mi cadena" en realidad usa 9 + 1 (= 10!) Bytes. Esto es importante para saber cuándo finalmente se distribuirán las cadenas dinámicamente. Entonces, sin este ''cero de terminación'', no tienes una cadena. Tiene una matriz de caracteres (también llamada memoria intermedia) que se encuentran en la memoria.
Longevidad de los datos:
El uso de la función de esta manera:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // fraught with problems
printf("%s", szSomeString);
}
... por lo general te enviará con fallas no administradas, excepciones / segmentos y cosas por el estilo, especialmente ''en el camino''.
En resumen, aunque mi respuesta es correcta, 9 de cada 10 veces terminarás con un programa que se bloquea si lo usas de esa manera, especialmente si crees que es una "buena práctica" hacerlo de esa manera. En resumen: generalmente no lo es.
Por ejemplo, imagina algún momento en el futuro, la cuerda ahora necesita ser manipulada de alguna manera. En general, un codificador ''tomará el camino fácil'' y (intentará) escribir un código como este:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Es decir, su programa se bloqueará porque el compilador (puede / no) ha liberado la memoria utilizada por szBuffer
para cuando se llama a printf()
en main()
. (Su compilador también debería advertirle de tales problemas de antemano).
Hay dos formas de devolver cadenas que no barf con tanta facilidad.
- buffers de retorno (estáticos o asignados dinámicamente) que viven por un tiempo. En C ++ use ''clases auxiliares'' (por ejemplo,
std::string
) para manejar la longevidad de los datos (lo que requiere cambiar el valor de retorno de la función), o - pasar un búfer a la función que se completa con la información.
Tenga en cuenta que es imposible utilizar cadenas sin utilizar punteros en C. Como he mostrado, también son sinónimos. Incluso en C ++ con clases de plantilla, siempre hay búferes (es decir, punteros) que se utilizan en segundo plano.
Entonces, para responder mejor a la (ahora modificada pregunta). (Seguramente habrá una variedad de "otras respuestas" que se pueden proporcionar).
Respuestas más seguras:
ej. 1. usando cadenas asignadas estáticamente:
const char* calculateMonth(int month)
{
static char* months[] = {"Jan", "Feb", "Mar" .... };
static char badFood[] = "Unknown";
if (month<1 || month>12)
return badFood; // choose whatever is appropriate for bad input. Crashing is never appropriate however.
else
return months[month-1];
}
int main()
{
printf("%s", calculateMonth(2)); // prints "Feb"
}
Lo que hace la ''estática'' aquí (a muchos programadores no les gusta este tipo de ''asignación'') es que las cadenas se ponen en el segmento de datos del programa. Es decir, está asignado permanentemente.
Si pasas a C ++, utilizarás estrategias similares:
class Foo
{
char _someData[12];
public:
const char* someFunction() const
{ // the final ''const'' is to let the compiler know that nothing is changed in the class when this function is called.
return _someData;
}
}
... pero probablemente sea más fácil usar clases de ayuda, como std::string
, si está escribiendo el código para su propio uso (y no como parte de una biblioteca para compartir con otros).
por ejemplo, 2. utilizando búferes definidos por el llamante:
Esta es la forma más "infalible" de pasar cadenas. Los datos devueltos no están sujetos a manipulación por la parte llamante. Es decir, por ejemplo, una parte llamante puede abusar fácilmente de uno y exponerlo a fallas de la aplicación. De esta forma, es mucho más seguro (aunque usa más líneas de código):
void calculateMonth(int month, char* pszMonth, int buffersize)
{
const char* months[] = {"Jan", "Feb", "Mar" .... }; // allocated dynamically during the function call. (Can be inefficient with a bad compiler)
if (!pszMonth || buffersize<1)
return; // bad input. Let junk deal with junk data.
if (month<1 || month>12)
{
*pszMonth = ''/0''; // return an ''empty'' string
// OR: strncpy(pszMonth, "Bad Month", buffersize-1);
}
else
{
strncpy(pszMonth, months[month-1], buffersize-1);
}
pszMonth[buffersize-1] = ''/0''; // ensure a valid terminating zero! Many people forget this!
}
int main()
{
char month[16]; // 16 bytes allocated here on the stack.
calculateMonth(3, month, sizeof(month));
printf("%s", month); // prints "Mar"
}
Hay muchas razones por las que el segundo método es mejor, especialmente si está escribiendo una biblioteca para ser utilizada por otros (no es necesario que trabaje en un esquema particular de asignación / desasignación, los terceros no pueden descifrar su código, no es necesario que establezca un enlace a una biblioteca de administración de memoria específica), pero como todo código, depende de usted lo que más le guste. Por esa razón, la mayoría de las personas optan por, por ejemplo, 1 hasta que se han quemado tantas veces que se niegan a escribir de esa manera nunca más;)
renuncia:
Me retiré hace varios años y mi C está un poco oxidada ahora. Este código de demostración debería compilarse correctamente con C (sin embargo, está bien para cualquier compilador de C ++).