Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

140
Views
El comportamiento del bucle for dividido entre casos de cambio

Mientras jugaba con el código, noté un comportamiento extraño que no sé explicar la lógica detrás

 void foo(int n) { int m = n; while (--n > 0) { switch (n) { case -1: case 0: for (int j = 0; j < m; ++j) default: printf(":-)"); break; } } } int main() { foo(10); return 0; }

Esperaría que printf se ejecutara, digamos 10 veces. Luego vi que continúa ejecutándose (imagine 100000 en lugar de 10) y supuse que los desarrolladores (VS) interpretaron el printf dentro de for (bastante esperado), por lo tanto, la salida se realiza n veces para que cada entrada switch .

Pero luego resultó que j nunca se inicializó.

Mi pregunta es, porque ? ¿Es este un comportamiento indefinido? ¿No es este un código supuestamente estándar?

over 4 years ago · Santiago Trujillo
5 answers
Answer question

0

default es solo una etiqueta (dirección a donde salta el código si n no es -1 o 0). Por lo tanto, cuando n no es -1 o 0, el flujo ingresa al cuerpo del bucle for saltándose la inicialización de j . También puede escribir el mismo código que este, por lo que sería más claro lo que está sucediendo aquí:

 int m = n; while (--n > 0) { switch (n) { case -1: case 0: for (int j = 0; j < m; ++j) { default: printf(":-)"); } break; } }

(Tenga en cuenta que, como lo menciona @alagner en los comentarios, no se compilará con el compilador de C++, sino que se compilará perfectamente con el de C, por lo que esto es lo suficientemente bueno como para demostrar mi punto y explicar cómo se ve el código).

Entonces sí, dado que j no está inicializado, es un comportamiento indefinido. Si habilita las advertencias del compilador, le advertirá al respecto ( https://godbolt.org/z/rzGraP ):

 warning: 'j' may be used uninitialized in this function [-Wmaybe-uninitialized] 12 | for (int j = 0; j < m; ++j) | ^
over 4 years ago · Santiago Trujillo Report

0

Un bloque switch es efectivamente un conjunto glorificado de sentencias goto . Los diferentes casos no introducen alcances ni ninguna estructura lógica al código. En realidad, son solo objetivos a los que debe saltar la declaración de switch .

En este programa, la etiqueta default: está dentro de un bucle for anidado. Cuando se activa el caso default , el programa salta dentro del ciclo como si hubiera una instrucción goto allí. El bloque switch es equivalente a:

 if (n == -1 || n == 0) { goto before_loop; } else { goto inside_loop; } before_loop: for (int j = 0; j < m; ++j) { inside_loop: printf(":-)"); }

Esto es peligroso porque saltar a inside_loop: salta sobre j = 0 . Como ha observado, j todavía se declara pero no se inicializa, y acceder a él conduce a un comportamiento indefinido.

over 4 years ago · Santiago Trujillo Report

0

Tal como se publicó, el código tiene un comportamiento indefinido porque cuando el switch salta a la etiqueta default: dentro del cuerpo de la instrucción for , omite la inicialización de j en el bucle interno, lo que provoca un comportamiento indefinido cuando j se prueba y disminuye cuando el bucle itera.

Omitir la inicialización con un salto directo a un nuevo ámbito no está permitido en C++. Tal restricción no está presente en el lenguaje C, por compatibilidad con código histórico donde no necesariamente causa problemas, pero los compiladores modernos detectan este error y se quejan. Recomiendo usar -Wall -Wextra -Werror para evitar errores tontos.

Tenga en cuenta que modificado como se muestra a continuación, se define por completo, se imprime :) 90 veces (9 iteraciones del ciclo externo, por 10 iteraciones del ciclo interno) y finaliza con éxito:

 #include <stdio.h> void foo(int n) { int m = n; while (--n > 0) { int j = 0; switch (n) { case -1: case 0: for (j = 0; j < m; ++j) default: printf(":-)"); break; } } } int main() { foo(10); printf("\n"); return 0; }
over 4 years ago · Santiago Trujillo Report

0

Muchas buenas explicaciones, pero falta el punto clave: que el compilador coloca una instrucción jmp después de la instrucción printf porque acaba de compilar una instrucción for . El jmp salta a la condición del ciclo y continúa allí (usando un j no inicializado).

over 4 years ago · Santiago Trujillo Report

0

Para empezar, la función foo que tiene el tipo de retorno int no devuelve nada.

El ciclo while :

 while (--n > 0) { //.. }

obtiene el control solo en el caso de que el valor de la expresión de decremento --n sea mayor que 0 .

Es decir, dentro del bucle while , la variable n no es ni igual a 0 ni a -1 .

Por lo tanto, el control se pasará de inmediato a la etiqueta default dentro de la declaración de switch .

 switch (n) { case -1: case 0: for (int j = 0; j < m; ++j) default: printf(":-)"); break; }

De manera equivalente, puede reescribir el ciclo while sin la declaración de switch la siguiente manera para que quede más claro:

 while (--n > 0) { goto Default; for (int j = 0; j < m; ++j) { Default: printf(":-)"); } }

Es decir, el control se pasa inmediatamente dentro del ciclo for . De acuerdo con el Estándar C (6.8.5 Sentencias de iteración)

4 Una sentencia de iteración hace que una sentencia llamada cuerpo del bucle se ejecute repetidamente hasta que la expresión de control se compara igual a 0. La repetición ocurre independientemente de si se ingresa al cuerpo del bucle desde la sentencia de iteración o mediante un salto .

Significa que el bucle for contendrá una declaración:

 printf(":-)");

será ejecutado.

Sin embargo, se omite la inicialización inicial de la variable j en el ciclo for . Del estándar C (6.2.4 Duraciones de almacenamiento de objetos)

6 Para un objeto de este tipo que no tiene un tipo de matriz de longitud variable, su vida útil se extiende desde la entrada en el bloque con el que está asociado hasta que la ejecución de ese bloque termina de alguna manera. (Ingresar a un bloque cerrado o llamar a una función suspende, pero no finaliza, la ejecución del bloque actual). Si el bloque se ingresa recursivamente, se crea una nueva instancia del objeto cada vez. El valor inicial del objeto es indeterminado. Si se especifica una inicialización para el objeto, se realiza cada vez que se alcanza la declaración o literal compuesto en la ejecución del bloque; de lo contrario, el valor se vuelve indeterminado cada vez que se alcanza la declaración.

Entonces, la variable j tiene un valor indeterminado. Significa que el bucle for y, como resultado, la función misma, tienen un comportamiento indefinido.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!