[C] ¿Qué hace este while de una sola linea?

Iniciado por GGZ, 25 Noviembre 2015, 01:31 AM

0 Miembros y 1 Visitante están viendo este tema.

GGZ

#include <stdio.h>

int main (void){

  int a,c;
  a=c=3;

  while ((a=c=getchar()) == '1');

  printf ("c:%c\n",c);
  printf ("a:%c\n",a);

  return 0;
}



[dresuer@emachine TRABAJILLO]$ echo '3' | ./TEST
c:3
a:3
[dresuer@emachine TRABAJILLO]$ echo '1' | ./TEST
c:

a:

[dresuer@emachine TRABAJILLO]$


¿Qué hace este programa?

¿Por qué a y c valen una nueva linea?

¿a=c='\n'?

No entiendo.
LET'S DO STUFF!!

class_OpenGL

Lo que hace getchar, como dice su nombre, es tomar un byte del buffer stdin. Cuando tu introduces un carácter en la consola, se guarda ese carácter Y un salto de línea. Entonces, getchar toma ese carácter introducido y deja el salto de línea sin eliminar. Entonces, esta es la secuencia:

1º Introducimos un 1 por consola y le damos a enter para seguir. En el buffer de stdin se guarda la siguiente secuencia: 0x310A (31 es '1' en hexadecimal y 0A es un salto de línea).
2º Getchar toma el primer byte de esa secuencia (31).
3º Como La condición 31 == '1' se cumple, entonces el la sentencia while se ejecuta (en este caso, la sentencia es nula)
4º Comparamos de nuevo. Como en el buffer de stdin aún hay datos, entonces el programa no se pausa, sino que toma datos de esa secuencia. En este caso, getchar retorna 0x0A, lo que es un salto de línea. Como 0x0A es distinto de '1', entonces el while ya no se ejecuta y las variables a y c se quedan con el valor de salto de línea (0x0A).

Conclusiones:
-Si introducimos un '1', entonces las variables a y c guardarán un salto de línea.
-Si ponemos cualquier otro carácter, las variables a y c guardarán ese carácter y el buffer de stdin se quedará con un elemento por leer.

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

GGZ

Me parece muy confusa tu forma de explicar.

Todavía no lo entiendo bien, o sea yo creía que solo cuando se daba la igualdad no se hacía la asignación algo así

¿Podrías explicarmelo de nuevo, con otras palabras?

Saludos!
LET'S DO STUFF!!

class_OpenGL

#3
Antes de nada, quiero dejar una nota aclaratoria. El sistema operativo tiene un buffer (espacio de memoria) para los valores de entrada de una consola. En C y C++, podemos acceder indirectamente a este buffer como si de un archivo se tratara.

Definición de stdin según cplusplus:
FILE * stdin;
(Puede diferir entre compiladores)

Para más información, consulta: esta página
__________________________________________

Aunque la sentencia "a=c=getchar()" esté dentro de la condición del while, la igualación se hace igual. Es como si hicieras:
Código (cpp) [Seleccionar]
a = c= getchar();
while(a == '1') { // a y c tienen el mismo valor, puedes poner la que quieras
   a = c = getchar();
}

Que a su vez, equivale a
Código (cpp) [Seleccionar]
a = getchar();
c = a;
while(a == '1') { // a y c tienen el mismo valor, puedes poner la que quieras
   a = getchar();
   c = a;
}


Vamos a analizar este segundo código:

-Primero, tienes que saber que en las operaciones de igualdad se ejecutan de derecha a izquierda, por lo que primero se ejecuta la función getchar() y posteriormente el valor de retorno se le asigna a la variable 'a'.
Cuando ejecutas esta función, si el buffer que maneja stdin no tiene datos (ese buffer lo guarda el sistema operativo, no tenemos acceso a el directamente) entonces el programa se pausa y espera a que el usuario introduzca datos. Cuando introducimos datos, al final tenemos que pulsar enter, y ese enter se guarda (es decir, se guarda el salto de línea). Por ejemplo, si introducimos un '1' y le damos a enter, se va a guardar la siguiente secuencia de bytes: 0x31 (equivale a '1') y 0x0A (equivale a 'salto de línea' o '\n'). Entonces, la función getchar va a tomar el primer byte que se encuentra. En este caso, va a ser el 0x31. Los datos obtenidos se borran del buffer de stdin, por lo que ahora este buffer tiene solamente el salto de línea (0x0A)

-Segundo, igualamos a la variable c el valor de a, es decir, a partir de esa sentencia, a y c tienen exactamente el mismo valor (si seguimos el ejemplo anterior, a y c valen '1').

-Tercero, en el while, comprobamos si el usuario ha introducido un '1'. De ser así, el while ejecutaría el bloque de sentencias del while. Como en este ejemplo sí que hemos introducido un '1', entonces el while se ejecutará. Al ejecutarse el while, llamamos a la función getchar() de nuevo. Como ya he dicho, getchar toma datos del buffer de stdin. En este caso, este buffer SI tiene datos, por lo que el programa NO se va a pausar de nuevo. Entonces, los datos que hay son un salto de línea (como vimos en el paso 1), por lo que getchar toma ese salto de línea, borra ese dato del buffer de stdin y se lo asigna a la variable 'a'. Entonces, ahora la variable 'a' tiene el valor de un salto de línea.

-Cuarto, le asignamos el valor de 'a' a la variable 'c'.

Por todo esto, las variables 'a' y 'c' equivalen a un salto de línea cuando introduces un '1'.

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

GGZ

#4
Bueno, sí entendí mejor por lo menos la idea.

Ahora si yo quisiera por ejemplo escribir esto pero sin un while ¿Cómo lo haría?

while ((buffer[0] = c = getchar()) == ' ' || c == '\t' || c=='\n');

O sea yo en realidad todavía no entiendo que hace ese while cuando se cumple la condición y si no se cumple.

:-\

Saludos!
LET'S DO STUFF!!

class_OpenGL

#5
No entiendo cuál era el objetivo final de este código. Con esa información, podríamos ayudarte mejor y con respuestas más claras.

Tienes que saber que esas operaciones  son binarias, es decir, se involucran dos "cosas". Por ejemplo, el operador = es binario puesto que necesita dos cosas: una variable a la que asignar un valor, y el dicho valor (a = b, se necesita tanto "a" como "b"). Pero te preguntarás: ¿qué pasa con la sentencia a = 5 + 1 + 3 * 5 - 7? Pues bien, esa operación, a la hora de compilar, se divide en operaciones de dos en dos. Normalmente, el orden de preferencia va de izquierda a derecha, pero con el operador = va de derecha a izquierda. Vamos a expresar la sentencia que he puesto de ejemplo en operaciones binarias:

1º Está el operador =, por lo que es de derecha a izquierda. Primero tenemos que procesar 5 + 1 + 3 * 5 - 7.
2º El operador * tiene preferencia sobre los operadores + y -, por lo que en primer lugar se procesa 3 * 5. Una vez procesado, quedaría así:
5 + 1 + 15 - 7
3º Como entre los operadores + y - no hay preferencia, la expresión se evalua de izquierda a derecha:
>> 6 + 15 - 7
>> 21 - 7
>> 14
4º La expresión final quedaría así:
a = 14
Por tanto, a la variable a se le asigna el valor 14.

Para saber más sobre la preferencia entre operadores, consulta : esta página

Una vez sabido esto, puedes analizar la expresión que tienes en tu while

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

GGZ

#6
 c=getchar();
buffer[0]=c;
while (c == ' ' || c == '\t' || c=='\n')buffer[0]=c=getchar();  // SI ENCONTRAS UN ESPACIO, UN SALTO DE LINEA O UN TABULADOR ENTONCES SEGUI LEYENDO


¿Está bien ese razonamiento?

Que es lo mismo que

while ((buffer[0] = c = getchar()) == ' ' || c == '\t' || c=='\n');

¿O me equivoco?
LET'S DO STUFF!!

class_OpenGL


Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

GGZ

Este es todo el programa

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#define MAXTOKEN 100
#define NUMBER '0'

struct node {
  int v;
  struct node *next;
};

struct node *head = NULL;

void push(int i) {
  struct node *nuevo;
  nuevo = (struct node *) malloc (sizeof(struct node));
  if (nuevo==NULL){printf("No hay memoria suficiente.\n"); exit(-1);}
  nuevo->v =i;
  nuevo->next = head;
  head = nuevo;
}

int pop() {

  struct node *nuevo;
  int aux;

  nuevo = head;

  if (!nuevo) return 0;
  head = nuevo->next;

  aux = nuevo->v;
  free(nuevo);
  return aux;

}

int get_token(char buffer[]) {
  int a,c;

  c=getchar();
  buffer[0]=c;
  while (c == ' ' || c == '\t' || c=='\n')buffer[0]=c=getchar();  // SI ENCONTRAS UN ESPACIO UN SALTO DE LINEA O UN TABULADOR SEGUI LEYENDO

  buffer[1] = '\0';

  if (!isdigit(buffer[0]))
    return c;

  a = 0;
  if (isdigit(c))
    while (isdigit(buffer[++a] = c = getchar()));
     
  buffer[a] = '\0';
  if (c != EOF)
    return NUMBER;
}

void mostrar_lista(){
  struct node *auxiliar;
  int i;
  i=0;
  auxiliar=head;
  while (auxiliar!=NULL){
      printf("%d\n", auxiliar->v);
      auxiliar=auxiliar->next;
      i++;
  }
}

int main(void) {
  int type;
  int n;
  int sustraendo;
  int divisor;

  char buffer[MAXTOKEN];

  while((type = get_token(buffer)) != EOF) {
    switch(type) {
    case NUMBER:
      n = atoi(buffer); // convierte de cadena a entero
      push(n);
      break;
    case '+':
      push(pop() + pop());
      break;
    case '-':
      sustraendo = pop();
      push(pop() - sustraendo);
      break;
    case '*':
      push(pop() * pop());
      break;
    case '/':
      divisor = pop();
      if (divisor != 0){
          push(pop() / divisor);
      }
      else {
          printf("¡ERROR! ¡No se puede dividir por cero!\n");
      }
      break;
    default:
      printf("error: comando desconocido %s\n", buffer);
      break;
    }
  }

  mostrar_lista();

  return 0;

}



Calculadora polaca inversa
LET'S DO STUFF!!

class_OpenGL

#9
Curioso. Nunca había oído hablar de la notación polaca inversa. Cada día se aprende algo :D

Una cosilla. No he analizado tu código, pero ¿no sería mejor usar
scanf("%d %d %c", &operando1, &operando2, &operador);
?

Ya te digo que no he analizado tu código

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL