sirven resueltos que punteros puntero para operaciones los funciones estructura ejercicios ejemplo dev datos con cadenas apuntadores c++ c gcc systems-programming

resueltos - ¿Cuál es el código más corto para escribir directamente en una dirección de memoria en C/C++?



punteros a cadenas (6)

Me gusta especificar los bits de control reales en una estructura, luego asignarlos a la dirección de control. Algo como:

typedef struct uart_ctl_t { unsigned other_bits : 31; unsigned disable : 1; }; uart_ctl_t *uart_ctl = 0x4000C030; uart_ctl->disable = 1;

(Disculpas si la sintaxis no es del todo correcta, no he codificado en C durante bastante tiempo ...)

Estoy escribiendo código a nivel de sistema para un sistema integrado sin protección de memoria (en un ARM Cortex-M1, compilando con gcc 4.3) y necesito leer / escribir directamente en un registro asignado en memoria. Hasta ahora, mi código se ve así:

#define UART0 0x4000C000 #define UART0CTL (UART0 + 0x30) volatile unsigned int *p; p = UART0CTL; *p &= ~1;

¿Hay alguna forma más corta (más corta en código, quiero decir) que no use un puntero? Busco una manera de escribir el código de asignación real tan corto como esto (estaría bien si tuviera que usar más #defines):

*(UART0CTL) &= ~1;

Todo lo que intenté hasta ahora terminó con gcc quejándose de que no podía asignar algo al valor ...


Me gustaría ser un nitpick: ¿estamos hablando de C o C ++?

Si C, cedo a la respuesta de Chris voluntariamente (y me gustaría que se eliminara la etiqueta C ++).

Si C ++, desaconsejo el uso de esos desagradables C-Casts y #define completo.

La forma idiomática de C ++ es usar una variable global:

volatile unsigned int& UART0 = *((volatile unsigned int*)0x4000C000); volatile unsigned int& UART0CTL = *(&UART0 + 0x0C);

Declaro una variable global con tipo , que obedecerá las reglas de alcance (a diferencia de las macros).

Se puede usar fácilmente (no es necesario usar *() ) y, por lo tanto, es aún más corto.

UART0CTL &= ~1; // no need to dereference, it''s already a reference

Si quieres que sea puntero, entonces sería:

volatile unsigned int* const UART0 = 0x4000C000; // Note the const to prevent rebinding

Pero, ¿para qué usar un puntero de const que no puede ser nulo? Esto es semánticamente para qué se crearon las referencias.


Otra opción que me gusta de las aplicaciones incrustadas es usar el enlazador para definir secciones para sus dispositivos y asignar su variable a esas secciones. Esto tiene la ventaja de que si está apuntando a múltiples dispositivos, incluso del mismo proveedor, como TI, por lo general tendrá que alterar los archivos del enlazador en un dispositivo por dispositivo. es decir, diferentes dispositivos en la misma familia tienen diferentes cantidades de memoria mapeada directa interna, y de placa a placa, es posible que tenga diferentes cantidades de RAM y hardware en diferentes ubicaciones. Aquí hay un ejemplo de la documentación de GCC:

Normalmente, el compilador coloca los objetos que genera en secciones como datos y bss. Sin embargo, a veces, necesita secciones adicionales, o necesita que determinadas variables en particular aparezcan en secciones especiales, por ejemplo, para asignar hardware especial. El atributo de sección especifica que una variable (o función) vive en una sección en particular. Por ejemplo, este pequeño programa utiliza varios nombres de sección específicos:

struct duart a __attribute__ ((section ("DUART_A"))) = { 0 }; struct duart b __attribute__ ((section ("DUART_B"))) = { 0 }; char stack[10000] __attribute__ ((section ("STACK"))) = { 0 }; int init_data __attribute__ ((section ("INITDATA"))); main() { /* Initialize stack pointer */ init_sp (stack + sizeof (stack)); /* Initialize initialized data */ memcpy (&init_data, &data, &edata - &data); /* Turn on the serial ports */ init_duart (&a); init_duart (&b); }

Utilice el atributo de sección con variables globales y no con variables locales, como se muestra en el ejemplo.

Puede usar el atributo de sección con variables globales inicializadas o sin inicializar, pero el vinculador requiere que cada objeto se defina una vez, con la excepción de que las variables sin inicializar van provisionalmente a la sección común (o bss) y se pueden "definir" de manera múltiple. El uso del atributo section modificará la sección en la que entra la variable y puede hacer que el vinculador emita un error si una variable sin inicializar tiene varias definiciones. Puede forzar que una variable se inicialice con el indicador -fno-common o el atributo nocommon.


Puede ir más allá de la respuesta de Chris si desea que los registros de hardware se vean como simples variables:

#define UART0 0x4000C000 #define UART0CTL (*((volatile unsigned int *) (UART0 + 0x30))) UART0CTL &= ~1;

Es una cuestión de gustos que puede ser preferible. He trabajado en situaciones en las que el equipo quería que los registros parecieran variables, y trabajé en un código donde la falta de referencia agregada se consideraba "ocultar mucho", por lo que la macro para un registro se dejaría como un puntero que tenía que ser referenciados explícitamente (como en la respuesta de Chris).


#define UART0 ((volatile unsigned int*)0x4000C000) #define UART0CTL (UART0 + 0x0C)


#define UART0CTL ((volatile unsigned int *) (UART0 + 0x30))

:-PAG

Editado para agregar: Oh, en respuesta a todos los comentarios acerca de cómo la pregunta está etiquetada como C ++ y C, aquí hay una solución de C ++. :-PAG

inline unsigned volatile& uart0ctl() { return *reinterpret_cast<unsigned volatile*>(UART0 + 0x30); }

Esto se puede pegar directamente en un archivo de encabezado, al igual que en la macro de estilo C, pero tiene que usar la sintaxis de llamada de función para invocarla.