Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

173
Vistas
_Static_assert en selección genérica no utilizada

Parece que es probable que el operador typeof sea aceptado en el próximo estándar C, y estaba buscando una manera de aprovechar esto para crear una macro usando ISO-C portátil que pueda pasar la longitud de una matriz a o fallará al compilar si se le pasa un puntero. Normalmente, la selección genérica se puede usar para forzar un error del compilador cuando se usa un tipo no deseado al dejarlo fuera de la lista de asociaciones genéricas, pero en este caso, necesitamos una asociación predeterminada para manejar matrices de cualquier longitud, por lo que en su lugar estoy tratando de forzar un error de compilación para la asociación genérica para el tipo que no queremos. Aquí hay un ejemplo de cómo podría verse la macro:

 #define ARRAY_SIZE(X) _Generic(&(X), \ typeof(&X[0]) *: sizeof(struct{_Static_assert(0, "Trying to get the array length of a pointer"); int _a;}), \ default: (sizeof(X) / sizeof(X[0])) \ )

El problema es que _Static_assert se activa incluso cuando la asociación genérica seleccionada es la asociación predeterminada. En aras de la simplicidad, dado que el problema en cuestión no está relacionado con nada que se presente en C23, crearemos un programa de prueba que funcione explícitamente para rechazar un puntero a int:

 #include <stdio.h> #include <stdlib.h> #define ARRAY_SIZE(X) _Generic(&(X), \ int **: sizeof(struct{_Static_assert(0, "Trying to get the array length of a pointer"); int _a;}), \ default: (sizeof(X) / sizeof(X[0])) \ ) int main(void) { int x[100] = {0}; int *y = x; int (*z)[100] = {&x}; printf("length of x: %zu\n", ARRAY_SIZE(x)); printf("length of y: %zu\n", ARRAY_SIZE(y)); printf("length of z: %zu\n", ARRAY_SIZE(z)); printf("length of *z: %zu\n", ARRAY_SIZE(*z)); return EXIT_SUCCESS; }

Construyendo lo anterior con -std=c11 , encuentro que _Static_assert en todas las expansiones de ARRAY_SIZE cuando esperaría tener problemas solo con los punteros que usarán la asociación genérica int ** .

Según 6.5.1.1 p3 del estándar C11 para Selección Genérica,

No se evalúa ninguna de las expresiones de cualquier otra asociación genérica de la selección genérica.

¿Es esto un error en gcc y clang, o hay algo que me he perdido en el estándar que causaría la evaluación en tiempo de compilación de este _Static_assert en la asociación genérica no utilizada?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

No importa qué selección genérica se evalúe.

Cuando la expresión que forma parte de _Status_assert tiene el valor 0, se considera una violación de restricción y se requiere que el compilador genere un diagnóstico.

over 4 years ago · Santiago Trujillo Denunciar

0

Realmente no puede mezclar _Static_assert con expresiones que deberían devolver un valor, como una macro similar a una función. Tal vez podría solucionar eso con una "afirmación estática del hombre pobre", como uno de los trucos feos que usamos antes de C11:

 #define POOR_STATIC_ASSERT(expr) (int[expr]){0} #define CHECK(X) _Generic((&X), \ int **: 0,\ default: (sizeof(X) / sizeof(X[0])) \ ) #define ARRAY_SIZE(X) ( (void)POOR_STATIC_ASSERT(CHECK(X)), CHECK(X) )

Aquí se llama al operador coma para que la macro CHECK devuelva el tamaño o cero, en caso de que un tipo sea válido o no. A continuación, vuelva a llamar a la misma macro para que la devuelva la macro similar a una función ARRAY_SIZE . Esto conducirá a un error críptico de un compilador ISO C como "error: ISO C prohíbe la matriz de tamaño cero".


El siguiente problema es que &(X) en _Generic no garantiza de ninguna manera que se reduzca a un int** , por lo que esta macro no es segura ni confiable. Sin embargo, con respecto a los tamaños de matriz, hay un truco que podemos usar. Un puntero a una matriz sin tamaño (tipo incompleto) es compatible con todas las matrices del mismo tipo de elemento sin importar su tamaño. La macro podría reescribirse como:

 #define POOR_STATIC_ASSERT(expr) (int[expr]){0} #define CHECK(X) _Generic((&X), \ int (*)[]: sizeof(X) / sizeof(X[0]), \ default: 0) #define ARRAY_SIZE(X) ( (void)POOR_STATIC_ASSERT(CHECK(X)), CHECK(X) )

Esto funcionará para cualquier matriz int sin importar el tamaño, pero fallará para todo lo demás.

over 4 years ago · Santiago Trujillo Denunciar

0

Utilizando algunas de las sugerencias de la respuesta de Lundin, se me ocurrió la siguiente solución al problema simplificado:

 #define STATIC_ASSERT_EXPRESSION(X, ERROR_MESSAGE) (sizeof(struct {_Static_assert((X), ERROR_MESSAGE); int _a;})) #define NO_POINTERS(X) _Generic(&(X), \ int (*)[]: 1, \ default: 0 \ ) #define ARRAY_SIZE(X) ( (void)STATIC_ASSERT_EXPRESSION(NO_POINTERS(X), "Cannot retrieve the number of array elements from a pointer"), (sizeof(X) / sizeof(X[0])) )

Para que el caso de uso real sea de tipo genérico usando typeof, que debería llegar al estándar C23, reemplace la macro NO_POINTERS usando esto:

 #define NO_POINTERS(X) _Generic(&(X), \ typeof(*X) (*)[]: 1, \ default: 0 \ )

Al mover _Static_assert fuera de la selección genérica, solo se evaluará con el valor que realmente devuelve la selección, por lo que no se desactivará por existir en una selección no utilizada. Además, el cálculo de la cantidad de elementos también se eliminó de la selección genérica para que la expresión de la selección genérica se pudiera usar de manera segura en una afirmación estática incluso si su matriz era una matriz de longitud variable que requiere que se calcule su tamaño en tiempo de ejecución. .

La afirmación estática en sí se coloca dentro de una estructura anónima de la que tomamos el tamaño para que forme parte de una expresión. Y luego, como en el ejemplo de Lundin, usamos el operador de coma para evaluar esa expresión, y luego descartamos y usamos los resultados del cálculo del tamaño de la matriz.

Con esto, rechazamos los punteros mientras obtenemos la cantidad de elementos tanto en matrices estáticas como en VLA, además recibimos un buen mensaje de error del compilador cuando intentamos pasar un puntero.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda