Buscar..


Introducción

Un operador en un lenguaje de programación es un símbolo que le dice al compilador o intérprete que realice una operación matemática, relacional o lógica específica y produzca un resultado final.

C tiene muchos operadores poderosos. Muchos operadores de C son operadores binarios, lo que significa que tienen dos operandos. Por ejemplo, en a / b , / es un operador binario que acepta dos operandos ( a , b ). ¿Hay algunos operadores unarios que toman un operando (por ejemplo: ~ , ++ ) y solo un operador ternario ? : .

Sintaxis

  • operador expr1
  • operador expr2
  • expr1 operador expr2
  • expr1? expr2: expr3

Observaciones

Los operadores tienen una aridad , una precedencia y una asociatividad .

  • Arity indica el número de operandos. En C, existen tres aridades de operadores diferentes:

    • Unario (1 operando)
    • Binario (2 operandos)
    • Ternario (3 operandos)
  • La precedencia indica qué operadores "enlazan" primero a sus operandos. Es decir, qué operador tiene prioridad para operar en sus operandos. Por ejemplo, el lenguaje C obedece a la convención de que la multiplicación y la división tienen prioridad sobre la suma y la resta:

    a * b + c
    

    Da el mismo resultado que

    (a * b) + c
    

    Si esto no es lo que se quería, se puede forzar la precedencia utilizando paréntesis, porque tienen la precedencia más alta de todos los operadores.

    a * (b + c)
    

    Esta nueva expresión producirá un resultado que difiere de las dos expresiones anteriores.

    El lenguaje C tiene muchos niveles de precedencia; A continuación se muestra una tabla de todos los operadores, en orden descendente de prioridad.

    Tabla de precedencia

    Los operadores Asociatividad
    () [] -> . de izquierda a derecha
    ! ~ ++ -- + - * (desreferencia) (type) sizeof De derecha a izquierda
    * (multiplicación) / % de izquierda a derecha
    + - de izquierda a derecha
    << >> de izquierda a derecha
    < <= > >= de izquierda a derecha
    == != de izquierda a derecha
    & de izquierda a derecha
    ^ de izquierda a derecha
    | de izquierda a derecha
    && de izquierda a derecha
    || de izquierda a derecha
    ?: De derecha a izquierda
    = += -= *= /= %= &= ^= |= <<= >>= De derecha a izquierda
    , de izquierda a derecha
  • La asociatividad indica cómo se vinculan los operadores de igual precedencia de manera predeterminada, y hay dos tipos: de izquierda a derecha y de derecha a izquierda . Un ejemplo de enlace de izquierda a derecha es el operador de resta ( - ). La expresion

    a - b - c - d
    

    tiene tres restas de precedencia idéntica, pero da el mismo resultado que

    ((a - b) - c) - d
    

    porque el más a la izquierda - se une primero a sus dos operandos.

    Un ejemplo de asociatividad de derecha a izquierda son los operadores de referencia * y post-incremento ++ . Ambos tienen la misma prioridad, por lo que si se usan en una expresión como

    * ptr ++
    

    , esto es equivalente a

    * (ptr ++)
    

    porque el operador unario más a la derecha ( ++ ) se une primero a su único operando.

Operadores relacionales

Los operadores relacionales comprueban si una relación específica entre dos operandos es verdadera. El resultado se evalúa como 1 (lo que significa verdadero ) o 0 (lo que significa falso ). Este resultado se usa a menudo para afectar el flujo de control (a través de if , while , for ), pero también se puede almacenar en variables.

Es igual a "=="

Comprueba si los operandos suministrados son iguales.

1 == 0;         /* evaluates to 0. */
1 == 1;         /* evaluates to 1. */

int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr == yptr;   /* evaluates to 0, the operands hold different location addresses. */
*xptr == *yptr; /* evaluates to 1, the operands point at locations that hold the same value. */

Atención: ¡Este operador no debe confundirse con el operador de asignación ( = )!

No es igual a "! ="

Comprueba si los operandos suministrados no son iguales.

1 != 0;         /* evaluates to 1. */
1 != 1;         /* evaluates to 0. */

int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr != yptr;   /* evaluates to 1, the operands hold different location addresses. */
*xptr != *yptr; /* evaluates to 0, the operands point at locations that hold the same value. */

Este operador devuelve efectivamente el resultado opuesto al del operador igual ( == ).

No "!"

Compruebe si un objeto es igual a 0 .

El ! También se puede utilizar directamente con una variable de la siguiente manera:

!someVal

Esto tiene el mismo efecto que:

someVal == 0

Mayor que ">"

Comprueba si el operando de la izquierda tiene un valor mayor que el operando de la derecha

5 > 4      /* evaluates to 1. */
4 > 5      /* evaluates to 0. */
4 > 4      /* evaluates to 0. */

Menos que "<"

Comprueba si el operando de la izquierda tiene un valor más pequeño que el operando de la derecha

5 < 4      /* evaluates to 0. */
4 < 5      /* evaluates to 1. */
4 < 4      /* evaluates to 0. */

Mayor o igual que "> ="

Comprueba si el operando de la mano izquierda tiene un valor mayor o igual que el operando de la derecha.

5 >= 4      /* evaluates to 1. */
4 >= 5      /* evaluates to 0. */
4 >= 4      /* evaluates to 1. */

Menor o igual que "<="

Comprueba si el operando de la izquierda tiene un valor más pequeño o igual que el operando de la derecha.

5 <= 4      /* evaluates to 0. */
4 <= 5      /* evaluates to 1. */
4 <= 4      /* evaluates to 1. */

Operadores de Asignación

Asigna el valor del operando de la derecha a la ubicación de almacenamiento nombrada por el operando de la izquierda y devuelve el valor.

int x = 5;      /* Variable x holds the value 5. Returns 5. */ 
char y = 'c';   /* Variable y holds the value 99. Returns 99 
                 * (as the character 'c' is represented in the ASCII table with 99).
                 */
float z = 1.5;  /* variable z holds the value 1.5. Returns 1.5. */
char const* s = "foo"; /* Variable s holds the address of the first character of the string 'foo'. */

Varias operaciones aritméticas tienen un operador de asignación compuesto .

a += b  /* equal to: a = a + b */
a -= b  /* equal to: a = a - b */
a *= b  /* equal to: a = a * b */
a /= b  /* equal to: a = a / b */
a %= b  /* equal to: a = a % b */
a &= b  /* equal to: a = a & b */
a |= b  /* equal to: a = a | b */
a ^= b  /* equal to: a = a ^ b */
a <<= b /* equal to: a = a << b */
a >>= b /* equal to: a = a >> b */

Una característica importante de estas asignaciones compuestas es que la expresión en el lado izquierdo ( a ) solo se evalúa una vez. Por ejemplo, si p es un puntero

*p += 27;

desreferencias p solo una vez, mientras que lo siguiente lo hace dos veces.

*p = *p + 27;

También se debe tener en cuenta que el resultado de una asignación como a = b es lo que se conoce como un valor de r . Por lo tanto, la asignación realmente tiene un valor que luego se puede asignar a otra variable. Esto permite el encadenamiento de asignaciones para establecer múltiples variables en una sola declaración.

Este valor puede usarse en las expresiones de control de las sentencias if (o bucles o sentencias de switch ) que guardan algún código en el resultado de otra expresión o llamada de función. Por ejemplo:

char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
    /* do something with buffer */
    free(buffer);
}
else
{
    /* report allocation failure */
}

Debido a esto, se debe tener cuidado para evitar un error tipográfico común que puede conducir a errores misteriosos.

int a = 2;
/* ... */
if (a = 1)
    /* Delete all files on my hard drive */

Esto tendrá resultados desastrosos, ya que a = 1 siempre se evaluará como 1 y, por lo tanto, la expresión de control de la sentencia if siempre será verdadera (lea más sobre este escollo común aquí ). El autor casi seguramente quiso usar el operador de igualdad ( == ) como se muestra a continuación:

int a = 2;
/* ... */
if (a == 1)
    /* Delete all files on my hard drive */

Asociatividad de operadores

int a, b = 1, c = 2;
a = b = c;

Esto asigna c a b , que devuelve b , que es lo que se asigna a a . Esto sucede porque todos los operadores de asignación tienen asociatividad derecha, lo que significa que la operación más a la derecha en la expresión se evalúa primero, y procede de derecha a izquierda.

Operadores aritméticos

Aritmética básica

Devuelva un valor que sea el resultado de aplicar el operando de la mano izquierda al operando de la derecha, utilizando la operación matemática asociada. Se aplican reglas matemáticas normales de conmutación (es decir, la suma y la multiplicación son conmutativas, la resta, la división y el módulo no lo son).

Operador de adición

El operador de suma ( + ) se utiliza para agregar dos operandos juntos. Ejemplo:

#include <stdio.h>

int main(void)
{
    int a = 5;
    int b = 7;

    int c = a + b; /* c now holds the value 12 */

    printf("%d + %d = %d",a,b,c); /* will output "5 + 7 = 12" */

    return 0;
}

Operador de resta

El operador de sustracción ( - ) se usa para restar el segundo operando del primero. Ejemplo:

#include <stdio.h>

int main(void)
{
    int a = 10;
    int b = 7;

    int c = a - b; /* c now holds the value 3 */

    printf("%d - %d = %d",a,b,c); /* will output "10 - 7 = 3" */

    return 0;
}

Operador de multiplicación

El operador de multiplicación ( * ) se utiliza para multiplicar ambos operandos. Ejemplo:

#include <stdio.h>

int main(void)
{    
    int a = 5;
    int b = 7;

    int c = a * b; /* c now holds the value 35 */

    printf("%d * %d = %d",a,b,c); /* will output "5 * 7 = 35" */

    return 0;
}

No debe confundirse con el operador de * desreferencia.

Operador de la división

El operador de división ( / ) divide el primer operando por el segundo. Si ambos operandos de la división son enteros, devolverá un valor entero y descartará el resto (use el operador de módulo % para calcular y adquirir el resto).

Si uno de los operandos es un valor de punto flotante, el resultado es una aproximación de la fracción.

Ejemplo:

#include <stdio.h>

int main (void)
{
    int a = 19 / 2 ; /* a holds value 9   */
    int b = 18 / 2 ; /* b holds value 9   */
    int c = 255 / 2; /* c holds value 127 */
    int d = 44 / 4 ; /* d holds value 11  */
    double e = 19 / 2.0 ; /* e holds value 9.5   */
    double f = 18.0 / 2 ; /* f holds value 9.0   */
    double g = 255 / 2.0; /* g holds value 127.5 */
    double h = 45.0 / 4 ; /* h holds value 11.25 */

    printf("19 / 2 = %d\n", a);    /* Will output "19 / 2 = 9"    */
    printf("18 / 2 = %d\n", b);    /* Will output "18 / 2 = 9"    */
    printf("255 / 2 = %d\n", c);   /* Will output "255 / 2 = 127" */
    printf("44 / 4 = %d\n", d);    /* Will output "44 / 4 = 11"   */
    printf("19 / 2.0 = %g\n", e);  /* Will output "19 / 2.0 = 9.5"    */
    printf("18.0 / 2 = %g\n", f);  /* Will output "18.0 / 2 = 9"      */
    printf("255 / 2.0 = %g\n", g); /* Will output "255 / 2.0 = 127.5" */
    printf("45.0 / 4 = %g\n", h);  /* Will output "45.0 / 4 = 11.25"  */

    return 0;
}

Operador Modulo

El operador de módulo ( % ) recibe solo operandos enteros, y se utiliza para calcular el resto después de que el primer operando se divide por el segundo. Ejemplo:

#include <stdio.h>

int main (void) {
    int a = 25 % 2;    /* a holds value 1  */
    int b = 24 % 2;    /* b holds value 0  */
    int c = 155 % 5;   /* c holds value 0  */
    int d = 49 % 25;   /* d holds value 24 */

    printf("25 % 2 = %d\n", a);     /* Will output "25 % 2 = 1"    */
    printf("24 % 2 = %d\n", b);     /* Will output "24 % 2 = 0"    */
    printf("155 % 5 = %d\n", c);    /* Will output "155 % 5 = 0"   */
    printf("49 % 25 = %d\n", d);    /* Will output "49 % 25 = 24"  */

    return 0;
}

Operadores de Incremento / Decremento

Los operadores de incremento ( a++ ) y decremento ( a-- ) son diferentes porque cambian el valor de la variable a la que se aplican sin un operador de asignación. Puede usar los operadores de incremento y decremento antes o después de la variable. La ubicación del operador cambia el tiempo de la incrementación / disminución del valor antes o después de asignarlo a la variable. Ejemplo:

#include <stdio.h>

int main(void)
{
    int a = 1;
    int b = 4;
    int c = 1;
    int d = 4;

    a++;
    printf("a = %d\n",a);    /* Will output "a = 2" */
    b--;
    printf("b = %d\n",b);    /* Will output "b = 3" */

    if (++c > 1) { /* c is incremented by 1 before being compared in the condition */
        printf("This will print\n");    /* This is printed */
    } else {
        printf("This will never print\n");    /* This is not printed */
    }

    if (d-- < 4) {  /* d is decremented after being compared */
        printf("This will never print\n");    /* This is not printed */
    } else {
        printf("This will print\n");    /* This is printed */
    }
}

Como muestra el ejemplo de c y d muestra, ambos operadores tienen dos formas, como notación de prefijo y la notación postfix. Ambos tienen el mismo efecto al incrementar ( ++ ) o disminuir ( -- ) la variable, pero difieren según el valor que devuelven: las operaciones de prefijo realizan la operación primero y luego devuelven el valor, mientras que las operaciones de posfijo primero determinan el valor que es ser devuelto, y luego hacer la operación.

Debido a este comportamiento potencialmente contraintuitivo, el uso de operadores de incremento / decremento dentro de expresiones es controvertido.

Operadores logicos

Y lógico

Realiza un AND lógico booleano de los dos operandos que devuelven 1 si ambos operandos son distintos de cero. El operador lógico AND es de tipo int .

0 && 0  /* Returns 0. */
0 && 1  /* Returns 0. */
2 && 0  /* Returns 0. */
2 && 3  /* Returns 1. */

O lógico

Realiza un OR lógico booleano de los dos operandos que devuelven 1 si alguno de los operandos no es cero. El operador lógico OR es de tipo int .

0 || 0  /* Returns 0. */
0 || 1  /* Returns 1.  */
2 || 0  /* Returns 1.  */
2 || 3  /* Returns 1.  */

Lógica NO

Realiza una negación lógica. El operador lógico NOT es de tipo int . El operador NO verifica si al menos un bit es igual a 1, si es así devuelve 0. En caso contrario, devuelve 1;

!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */

Evaluación de corto circuito

Hay algunas propiedades cruciales comunes a && y || :

  • el operando de la izquierda (LHS) se evalúa completamente antes de que se evalúe el operando de la derecha (RHS),
  • hay un punto de secuencia entre la evaluación del operando de la izquierda y el operando de la derecha,
  • y, lo más importante, el operando de la derecha no se evalúa en absoluto si el resultado del operando de la izquierda determina el resultado general.

Esto significa que:

  • si el LHS se evalúa como 'verdadero' (distinto de cero), el RHS de || no se evaluará (porque el resultado de "verdadero O cualquier cosa" es "verdadero"),
  • si el LHS se evalúa como "falso" (cero), el RHS de && no se evaluará (porque el resultado de "falso Y cualquier cosa" es "falso").

Esto es importante ya que le permite escribir código como:

const char *name_for_value(int value)
{
    static const char *names[] = { "zero", "one", "two", "three", };
    enum { NUM_NAMES = sizeof(names) / sizeof(names[0]) };
    return (value >= 0 && value < NUM_NAMES) ? names[value] : "infinity";
}

Si se pasa un valor negativo a la función, el value >= 0 término se evalúa como falso y el value < NUM_NAMES term no se evalúa.

Incremento / Decremento

Los operadores de incremento y decremento existen en forma de prefijo y postfijo .

int a = 1;
int b = 1;
int tmp = 0;

tmp = ++a;        /* increments a by one, and returns new value; a == 2, tmp == 2  */
tmp = a++;        /* increments a by one, but returns old value; a == 3, tmp == 2 */
tmp = --b;        /* decrements b by one, and returns new value; b == 0, tmp == 0 */
tmp = b--;        /* decrements b by one, but returns old value; b == -1, tmp == 0 */

Tenga en cuenta que las operaciones aritméticas no introducen puntos de secuencia , por lo que ciertas expresiones con ++ o -- operadores pueden introducir un comportamiento indefinido .

Operador Condicional / Operador Ternario

Evalúa su primer operando y, si el valor resultante no es igual a cero, evalúa su segundo operando. De lo contrario, evalúa su tercer operando, como se muestra en el siguiente ejemplo:

a = b ? c : d;

es equivalente a:

if (b)
    a = c;
else 
    a = d;

Este pseudo-código lo representa: condition ? value_if_true : value_if_false . Cada valor puede ser el resultado de una expresión evaluada.

int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */

El operador condicional puede estar anidado. Por ejemplo, el siguiente código determina el mayor de tres números:

big= a > b ? (a > c ? a : c)
           : (b > c ? b : c);

El siguiente ejemplo escribe enteros pares en un archivo y enteros impares en otro archivo:

#include<stdio.h>

int main()
{
    FILE *even, *odds;
    int n = 10;
    size_t k = 0;

    even = fopen("even.txt", "w");
    odds = fopen("odds.txt", "w");

    for(k = 1; k < n + 1; k++)
    {
        k%2==0 ? fprintf(even, "\t%5d\n", k)
               : fprintf(odds, "\t%5d\n", k);
    }
    fclose(even);
    fclose(odds);

    return 0;
}

El operador condicional se asocia de derecha a izquierda. Considera lo siguiente:

exp1 ? exp2 : exp3 ? exp4 : exp5

Como la asociación es de derecha a izquierda, la expresión anterior se evalúa como

exp1 ? exp2 : ( exp3 ? exp4 : exp5 )

Operador de coma

Evalúa su operando izquierdo, descarta el valor resultante y luego evalúa sus operandos de derechos y el resultado produce el valor de su operando más a la derecha.

int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */

El operador de coma introduce un punto de secuencia entre sus operandos.

Tenga en cuenta que la coma utilizada en las llamadas de funciones que separan argumentos NO es el operador de coma , sino que se llama separador, que es diferente del operador de coma . Por lo tanto, no tiene las propiedades del operador de coma .

La llamada anterior printf() contiene el operador de coma y el separador .

printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/*           ^        ^ this is a comma operator */
/*           this is a separator */

El operador de coma se utiliza a menudo en la sección de inicialización, así como en la sección de actualización de un bucle for . Por ejemplo:

for(k = 1; k < 10; printf("\%d\\n", k), k += 2);   /*outputs the odd numbers below 9/*

/* outputs sum to first 9 natural numbers */
for(sumk = 1, k = 1; k < 10; k++, sumk += k)
    printf("\%5d\%5d\\n", k, sumk);

Operador de Reparto

Realiza una conversión explícita en el tipo dado del valor resultante de evaluar la expresión dada.

int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */

Aquí, el valor de x se convierte en un double , la división también promueve el valor de y al double , y el resultado de la división, se pasa un double a printf para imprimir.

operador de tamaño

Con un tipo como operando.

Se evalúa en el tamaño en bytes, de tipo size_t , de objetos del tipo dado. Requiere paréntesis alrededor del tipo.

printf("%zu\n", sizeof(int)); /* Valid, outputs the size of an int object, which is platform-dependent. */
printf("%zu\n", sizeof int); /* Invalid, types as arguments need to be surrounded by parentheses! */

Con una expresión como operando.

Se evalúa en el tamaño en bytes, de tipo size_t , de objetos del tipo de la expresión dada. La expresión en sí no es evaluada. No se requieren paréntesis; sin embargo, debido a que la expresión dada debe ser única, se considera la mejor práctica usarlos siempre.

char ch = 'a';
printf("%zu\n", sizeof(ch)); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
printf("%zu\n", sizeof ch);  /* Valid, will output the size of a char object, which is always 1 for all platforms. */

Aritmética de puntero

Además de puntero

Dado un puntero y un tipo escalar N , se evalúa como un puntero al elemento N th del tipo apuntado a que se corresponde directamente con el objeto apuntado en la memoria.

int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */

No importa si el puntero se usa como el valor de operando o el valor escalar. Esto significa que cosas como 3 + arr son válidas. Si arr[k] es el miembro k+1 de una matriz, arr+k es un puntero a arr[k] . En otras palabras, arr o arr+0 es un puntero a arr[0] , arr+1 es un puntero a arr[2] , y así sucesivamente. En general, *(arr+k) es igual que arr[k] .

A diferencia de la aritmética habitual, la adición de 1 a un puntero a un int agregará 4 bytes al valor de la dirección actual. Como los nombres de matriz son punteros constantes, + es el único operador que podemos usar para acceder a los miembros de una matriz mediante notación de puntero utilizando el nombre de la matriz. Sin embargo, al definir un puntero a una matriz, podemos obtener más flexibilidad para procesar los datos en una matriz. Por ejemplo, podemos imprimir los miembros de una matriz de la siguiente manera:

#include<stdio.h>
static const size_t N = 5
    
int main()
{
    size_t k = 0;
    int arr[] = {1, 2, 3, 4, 5};
    for(k = 0; k < N; k++)
    {
        printf("\n\t%d", *(arr + k));
    }
    return 0;
}

Al definir un puntero a la matriz, el programa anterior es equivalente a lo siguiente:

#include<stdio.h>
static const size_t N = 5
    
int main()
{
    size_t k = 0;
    int arr[] = {1, 2, 3, 4, 5};
    int *ptr = arr; /* or int *ptr = &arr[0]; */
    for(k = 0; k < N; k++)
    {
        printf("\n\t%d", ptr[k]);
        /* or   printf("\n\t%d", *(ptr + k)); */
        /* or   printf("\n\t%d", *ptr++); */
    }
    return 0;
}

Ver que se acceda a los miembros de la matriz arr utilizando los operadores + y ++ . Los otros operadores que se pueden usar con el puntero ptr son - y -- .

Resta de puntero

Dados dos punteros al mismo tipo, se evalúa en un objeto de tipo ptrdiff_t que contiene el valor escalar que debe agregarse al segundo puntero para obtener el valor del primer puntero.

int arr[] = {1, 2, 3, 4, 5};
int *p = &arr[2];
int *q = &arr[3];
ptrdiff_t diff = q - p;

printf("q - p = %ti\n", diff); /* Outputs "1". */
printf("*(p + (q - p)) = %d\n", *(p + diff)); /* Outputs "4". */

Operadores de Acceso

Los operadores de acceso miembro (DOT . Y la flecha -> ) se utilizan para acceder a un miembro de una struct .

Miembro de objeto

Se evalúa en el lvalor que denota el objeto que es miembro del objeto accedido.

struct MyStruct
{
    int x;
    int y;
};

struct MyStruct myObject;
myObject.x = 42;
myObject.y = 123;

printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Outputs ".x = 42, .y = 123". */

Miembro de objeto apuntado

Azúcar sintáctica para la desreferenciación seguida por el acceso de miembros. Efectivamente, una expresión de la forma x->y es una abreviatura de (*x).y - pero el operador de flecha es mucho más claro, especialmente si los punteros de la estructura están anidados.

struct MyStruct
{
    int x;
    int y;
};

struct MyStruct myObject;
struct MyStruct *p = &myObject;

p->x = 42;
p->y = 123;

printf(".x = %i, .y = %i\n", p->x, p->y); /* Outputs ".x = 42, .y = 123". */
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Also outputs ".x = 42, .y = 123". */

Dirección de

El unario & operador es la dirección del operador. Evalúa la expresión dada, donde el objeto resultante debe ser un valor l. Luego, se evalúa en un objeto cuyo tipo es un puntero al tipo del objeto resultante y contiene la dirección del objeto resultante.

int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */

Desreferencia

El operador unario * referencia a un puntero. Se evalúa en el lvalor resultante de la desreferenciación del puntero que resulta de evaluar la expresión dada.

int x = 42;
int *p = &x;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 42, *p = 42". */

*p = 123;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 123, *p = 123". */

Indexación

La indexación es azúcar sintáctica para la adición de punteros seguida de la eliminación de referencias. Efectivamente, una expresión de la forma a[i] es equivalente a *(a + i) , pero se prefiere la notación explícita del subíndice.

int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */

Intercambiabilidad de la indexación.

Agregar un puntero a un entero es una operación conmutativa (es decir, el orden de los operandos no cambia el resultado), por lo tanto, pointer + integer == integer + pointer .

Una consecuencia de esto es que arr[3] y 3[arr] son equivalentes.

printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */

El uso de una expresión 3[arr] lugar de arr[3] generalmente no se recomienda, ya que afecta la legibilidad del código. Tiende a ser popular en concursos de programación ofuscados.

Operador de llamada de función

El primer operando debe ser un puntero de función (un designador de función también es aceptable porque se convertirá en un puntero a la función), identificando la función a llamar, y todos los demás operandos, si los hay, se conocen colectivamente como los argumentos de la llamada de función. . Se evalúa en el valor de retorno que resulta de llamar a la función apropiada con los argumentos respectivos.

int myFunction(int x, int y)
{
    return x * 2 + y;
}

int (*fn)(int, int) = &myFunction;
int x = 42;
int y = 123;

printf("(*fn)(%i, %i) = %i\n", x, y, (*fn)(x, y)); /* Outputs "fn(42, 123) = 207". */
printf("fn(%i, %i) = %i\n", x, y, fn(x, y)); /* Another form: you don't need to dereference explicitly */

Operadores de Bitwise

Los operadores bitwise se pueden usar para realizar operaciones a nivel de bit en variables.
A continuación se muestra una lista de los seis operadores bitwise admitidos en C:

Símbolo Operador
Y en el bit y
| bitwise inclusive O
^ OR exclusivo a nivel de bit (XOR)
~ bitwise no (complemento de uno)
<< desplazamiento lógico a la izquierda
>> cambio lógico a la derecha

El siguiente programa ilustra el uso de todos los operadores bitwise:

#include <stdio.h>

int main(void)
{
   unsigned int a = 29;    /* 29 = 0001 1101 */  
   unsigned int b = 48;    /* 48 = 0011 0000 */
   int c = 0;           

   c = a & b;              /* 32 = 0001 0000 */ 
   printf("%d & %d = %d\n", a, b, c );

   c = a | b;              /* 61 = 0011 1101 */
   printf("%d | %d = %d\n", a, b, c );

   c = a ^ b;              /* 45 = 0010 1101 */
   printf("%d ^ %d = %d\n", a, b, c );

   c = ~a;                 /* -30 = 1110 0010 */
   printf("~%d = %d\n", a, c );

   c = a << 2;             /* 116 = 0111 0100 */
   printf("%d << 2 = %d\n", a, c );

   c = a >> 2;             /* 7 = 0000 0111 */
   printf("%d >> 2 = %d\n", a, c );

   return 0;
}

Las operaciones bitwise con tipos firmados deben evitarse porque el bit de signo de tal representación de bit tiene un significado particular. Se aplican restricciones particulares a los operadores de turno:

  • El desplazamiento a la izquierda de un bit 1 en el bit firmado es erróneo y conduce a un comportamiento indefinido.

  • El desplazamiento a la derecha de un valor negativo (con el bit de signo 1) se define por la implementación y, por lo tanto, no es portátil.

  • Si el valor del operando derecho de un operador de cambio es negativo o es mayor o igual que el ancho del operando izquierdo promovido, el comportamiento no está definido.

Enmascaramiento:

Enmascaramiento se refiere al proceso de extracción de los bits deseados de (o la transformación de los bits deseados en) una variable mediante el uso de operaciones lógicas a nivel de bits. El operando (una constante o variable) que se utiliza para realizar el enmascaramiento se denomina máscara .

El enmascaramiento se utiliza de muchas maneras diferentes:

  • Para decidir el patrón de bits de una variable entera.
  • Para copiar una parte de un patrón de bits dado a una nueva variable, mientras que el resto de la nueva variable se rellena con 0s (utilizando AND a nivel de bits)
  • Para copiar una parte de un patrón de bits dado a una nueva variable, mientras que el resto de la nueva variable se rellena con 1s (usando bitwise OR).
  • Para copiar una parte de un patrón de bits dado a una nueva variable, mientras que el resto del patrón de bits original se invierte dentro de la nueva variable (utilizando OR exclusivo en modo de bits).

La siguiente función utiliza una máscara para mostrar el patrón de bits de una variable:

#include <limits.h>
void bit_pattern(int u)
{
    int i, x, word;
    unsigned mask = 1;
    word = CHAR_BIT * sizeof(int);
    mask = mask << (word - 1);    /* shift 1 to the leftmost position */
    for(i = 1; i <= word; i++)
    {
        x = (u & mask) ? 1 : 0;  /* identify the bit */
        printf("%d", x);         /* print bit value */
        mask >>= 1;              /* shift mask to the right by 1 bit */
    }
}

_ Alineación de

C11

Consulta el requisito de alineación para el tipo especificado. El requisito de alineación es una potencia integral positiva de 2 que representa el número de bytes entre los cuales se pueden asignar dos objetos del tipo. En C, el requisito de alineación se mide en size_t .

El nombre de tipo no puede ser un tipo incompleto ni un tipo de función. Si se utiliza una matriz como tipo, se usa el tipo del elemento de matriz.

Con frecuencia se accede a este operador a través de la macro de conveniencia alignof desde <stdalign.h> .

int main(void)
{
    printf("Alignment of char = %zu\n", alignof(char));
    printf("Alignment of max_align_t = %zu\n", alignof(max_align_t));
    printf("alignof(float[10]) = %zu\n", alignof(float[10]));
    printf("alignof(struct{char c; int n;}) = %zu\n",
            alignof(struct {char c; int n;}));    
}

Salida posible:

Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4

http://en.cppreference.com/w/c/language/_Alignof

Comportamiento a corto circuito de operadores lógicos.

El cortocircuito es una funcionalidad que omite la evaluación de partes de una condición (if / while / ...) cuando puede. En el caso de una operación lógica en dos operandos, el primer operando se evalúa (a verdadero o falso) y si hay un veredicto (es decir, el primer operando es falso cuando se usa &&, el primer operando es verdadero cuando se usa ||) el segundo operando es no evaluado.

Ejemplo:

#include <stdio.h>
 
int main(void) {
  int a = 20;
  int b = -5;
 
  /* here 'b == -5' is not evaluated,
     since a 'a != 20' is false. */
  if (a != 20 && b == -5) {
    printf("I won't be printed!\n");
  }
   
  return 0;
}

Compruébelo usted mismo:

#include <stdio.h>
 
int print(int i) {
  printf("print function %d\n", i);
  return i;
}
 
int main(void) {
  int a = 20;
 
  /* here 'print(a)' is not called,
     since a 'a != 20' is false. */
  if (a != 20 && print(a)) {
    printf("I won't be printed!\n");
  }

  /* here 'print(a)' is called,
     since a 'a == 20' is true. */
  if (a == 20 && print(a)) {
    printf("I will be printed!\n");
  }

  return 0;
}

Salida:

$ ./a.out
print function 20
I will be printed!

El cortocircuito es importante cuando se quiere evitar evaluar términos que son (computacionalmente) costosos. Además, puede afectar considerablemente el flujo de su programa como en este caso: ¿Por qué este programa se imprime "bifurcado"? ¿4 veces?



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow