Asignación múltiple en una línea

6 minutos de lectura

avatar de usuario
arriesgado

Acabo de encontrar la declaración en c incrustado (dsPIC33)

sample1 = sample2 = 0;

¿Significaría esto

sample1 = 0;

sample2 = 0;

¿Por qué lo escriben de esta manera? ¿Es esta una codificación buena o mala?

  • ¿Posible duplicado de dos signos iguales en una línea?

    – Baum mit Augen

    5 de marzo de 2019 a las 22:56

Recuerda que la asignación se hace de derecha a izquierda, y que son expresiones normales. Entonces, desde la perspectiva de los compiladores, la línea

sample1 = sample2 = 0;

es lo mismo que

sample1 = (sample2 = 0);

que es lo mismo que

sample2 = 0;
sample1 = sample2;

Es decir, sample2 se le asigna cero, entonces sample1 se le asigna el valor de sample2. En la práctica lo mismo que asignar ambos a cero como lo adivinaste.

  • Probablemente valga la pena señalar que no hay secuencia en esta expresión. Lo que significa que no es correcto suponer que sample2 se asigna primero, y que el valor asignado a sample1 se lee literalmente de sample2. Más correctamente, sample1 se le asigna valor 0 convertido a tipo de sample2. Y la encomienda a sample1 en realidad puede suceder antes de la asignación a sample2.

    – Ant

    14 de octubre de 2013 a las 8:10


  • Gracias por la respuesta, evitaría sample1=sample2=0 en nombre del problema de legibilidad y secuenciación como se destaca en AndreyT.

    – arriesgado

    15 de octubre de 2013 a las 7:07

  • A esto le agregaría que sample2=0 devuelve un valor r que es igual al valor asignado en sample2. Por lo tanto, es una declaración de asignación y tiene un valor de retorno adicional, igual al valor que asignó. Ese valor devuelto (rvalue) es lo que se está asignando a sample1.

    – hazem

    30 de julio de 2015 a las 3:34

avatar de usuario
Hormiga

Formalmente, para dos variables t y u de tipo T y U respectivamente

T t;
U u;

la asignación

t = u = X;

(donde X es algún valor) se interpreta como

t = (u = X);

y es equivalente a un par de asignaciones independientes

u = X;
t = (U) X;

Nótese que el valor de X se supone que alcanza la variable t “como si” hubiera pasado por variable u primero, pero no hay ningún requisito para que suceda literalmente de esa manera. X simplemente tiene que convertirse al tipo de u antes de ser asignado a t. El valor no tiene que ser asignado a u primero y luego copiado de u para t. Las dos asignaciones anteriores en realidad no están secuenciadas y pueden ocurrir en cualquier orden, lo que significa que

t = (U) X;
u = X;

también es un programa de ejecución válido para esta expresión. (Tenga en cuenta que esta libertad de secuenciación es específica del lenguaje C, en el que el resultado de una asignación en un valor r. En C++, la asignación se evalúa como un valor l, lo que requiere que las asignaciones “encadenadas” sean secuenciadas).

No hay forma de decir si es una buena o mala práctica de programación sin ver más contexto. En los casos en que las dos variables están estrechamente relacionadas (como x y y coordenada de un punto), establecerlos en algún valor común usando la asignación “encadenada” es en realidad una buena práctica (incluso diría “práctica recomendada”). Pero cuando las variables no están relacionadas en absoluto, mezclarlas en una sola asignación “encadenada” definitivamente no es una buena idea. Especialmente si estas variables tienen diferentes tipos, lo que puede llevar a consecuencias no deseadas.

  • Creo que la norma especifica que el efecto es no como si la variable se escribiera primero, una distinción que podría ser extremadamente importante si la variable en cuestión es volatile.

    – Super gato

    18 mayo 2015 a las 17:30

Creo que no hay una buena respuesta en lenguaje C sin una lista de ensamblaje real 🙂

Así que para un programa simplista:

int main() {
        int a, b, c, d;
        a = b = c = d = 0;
        return a;
}

Tengo este ensamblado (Kubuntu, gcc 4.8.2, x86_64) con -O0 opción por supuesto 😉

main:
        pushq   %rbp
        movq    %rsp, %rbp

        movl    $0, -16(%rbp)       ; d = 0
        movl    -16(%rbp), %eax     ; 

        movl    %eax, -12(%rbp)     ; c = d
        movl    -12(%rbp), %eax     ;

        movl    %eax, -8(%rbp)      ; b = c
        movl    -8(%rbp), %eax      ;

        movl    %eax, -4(%rbp)      ; a = b
        movl    -4(%rbp), %eax      ;

        popq    %rbp

        ret                         ; return %eax, ie. a

Entonces gcc es Realmente encadenando todas las cosas.

  • Un compilador es libre de realizar cualquier transformación de código, que no cambie el comportamiento observable. Luego la asamblea no prueba nada en general. Lo único en lo que puede confiar es en la especificación estándar de c ++ (si el compilador promete obedecer el estándar).

    – Marcos

    26 de junio de 2016 a las 11:51

Usted mismo puede decidir si esta forma de codificar es buena o mala.

  1. Simplemente vea el código ensamblador de las siguientes líneas en su IDE.

  2. Luego cambie el código a dos asignaciones separadas y vea las diferencias.

Además de esto, también puede intentar activar/desactivar las optimizaciones (tanto las optimizaciones de tamaño como las de velocidad) en su compilador para ver cómo afecta eso al código ensamblador.

avatar de usuario
kjelderg

Los resultados son los mismos. Algunas personas prefieren encadenar tareas si todas tienen el mismo valor. No hay nada malo con este enfoque. Personalmente, encuentro esto preferible si las variables tienen significados estrechamente relacionados.

avatar de usuario
Mefistón

Como se notó anteriormente,

sample1 = sample2 = 0;

es igual a

sample2 = 0;
sample1 = sample2;

El problema es ese Riscy preguntó acerca de incrustado c, que a menudo se usa para controlar registros directamente. Muchos de los registros del microcontrolador tienen un propósito diferente en las operaciones de lectura y escritura. Entonces, en el caso general, no es lo mismocomo

sample2 = 0;
sample1 = 0;

Por ejemplo, sea UDR un registro de datos UART. Leer desde UDR significa obtener el valor recibido del búfer de entrada, mientras que escribir en UDR significa poner el valor deseado en el búfer de transmisión y activar la comunicación. En ese caso,

sample = UDR = 0;

significa lo siguiente: a) transmitir valor de cero usando UART (UDR = 0;) y b) leer el búfer de entrada y colocar los datos en sample valor (sample = UDR;).

Puede ver que el comportamiento del sistema integrado podría ser mucho más complicado de lo que el autor del código espera que sea. Utilice esta notación con cuidado al programar MCU.

sample1 = sample2 = 0;

significa

sample1 = 0;
sample2 = 0;  

si y solo si sample2 se declara antes.
No puedes hacerlo de esta manera:

int sample1 = sample2 = 0; //sample1 must be declared before assigning 0 to it

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad