programming how from example curso compiler code caracteristicas c++ c

c++ - how - extern c



Pasar la matriz a una función(y por qué no funciona en C++) (7)

Lo que no entiendo es por qué el compilador permite dimensionar las matrices. A mi entender, los tamaños deben ser fijos (por ejemplo, xu_col [9] [7]) o indefinidos (por ejemplo, xu_col [] []). En el código anterior, parece que los tamaños no son constantes de tiempo de compilación.

Tienes razón, los tamaños no son constantes en tiempo de compilación. Si tiene una matriz bidimensional, x [línea] [col] el compilador necesita el número de elementos en una línea para calcular la dirección de un elemento. Mire el código de ejemplo get_char_2 () y get_char_3 ().

Si usa arrays de longitud variable (VLA) como parámetros de función, debe proporcionar este número (vea el ejemplo de get_char_1). puedes escribir:

my_func( x[][width] )

o puedes escribir

my_func( x[999][width] )

¿Está el compilador simplemente ignorando los argumentos aquí? ¿O está realmente haciendo una> verificación de compilación en las dimensiones?

El primer número (999) será ignorado por el compilador. El segundo es necesario. Sin el tamaño de línea, el compilador no puede calcular las direcciones dentro de esta matriz 2D. El compilador no realiza verificaciones en tiempo de ejecución o de compilación para VLA en C.

/* file: vla.c * * variable length array example * * compile with: * * gcc -g -Wall -o vla vla.c * */ #include <stdio.h> #include <wchar.h> /* 4 Lines - each line has 8 wide-characters */ wchar_t tab[][8] = { { L"12345678" }, { L"abcdefgh" }, { L"ijklmnop" }, { L"qrstuvwx" } }; /* memory layout: 0x00: 0x0031 0x0032 0x0033 0x0034 0x0035 0x0036 0x0037 0x0038 0x20: 0x0061 0x0062 0x0063 0x0064 0x0065 0x0066 0x0067 0x0068 ... */ /* get character from table w/o variable length array and w/o type */ char get_char_3(int line, int col, int width, int typesize, void *ptr ) { char ch = * (char *) (ptr + width * typesize * line + col * typesize ); printf("line:%d col:%d char:%c/n", line, col, ch ); return ch; } /* get character from table w/o variable length array */ char get_char_2(int line, int col, int width, wchar_t *ptr) { char ch = (char) (ptr + width * line)[col]; printf("line:%d col:%d char:%c/n", line, col, ch ); return ch; } /* get character from table : compiler does not know line length for address calculation until you supply it (width). */ char get_char_1(int line, int col, int width, wchar_t aptr[][width] ) { /* run-time calculation: (width * sizeof(char) * line) + col ??? KNOWN KOWN KNOWN */ char ch = (char) aptr[line][col]; printf("line:%d col:%d char:%c/n", line, col, ch ); return ch; } int main(void) { char ch; ch = tab[1][7]; /* compiler knows line length */ printf("at 1,7 we have: %c/n", ch ); /* sizeof tab[0][0] == sizeof(wchar_t) */ ch = get_char_1(1,7, sizeof(tab[0])/sizeof(tab[0][0]), tab); printf("1 returned char: %c/n", ch ); ch = get_char_2(1,7, sizeof(tab[0])/sizeof(tab[0][0]), (wchar_t*)tab); printf("2 returned char: %c/n", ch ); ch = get_char_3(1,7, sizeof(tab[0])/sizeof(tab[0][0]), sizeof( wchar_t), tab); printf("3 returned char: %c/n", ch ); printf("table size: %lu, line size: %lu, element size: %lu/n", sizeof(tab), sizeof(tab[0]), sizeof(tab[0][0]) ); printf("number of elements per lines: %lu/n", sizeof(tab[0])/sizeof(tab[0][0])); printf("number of lines: %lu/n", sizeof(tab)/sizeof(tab[0])); return 0; }

Me he encontrado con un código C que compila, pero no entiendo por qué. Específicamente, tengo una biblioteca de C que tiene mucho código utilizando este formato:

void get_xu_col(int i_start, int n, double x[n], int n_x, int n_u, int n_col, double xu_col[n_col][n_x + n_u]){ ... } int main(){ ... double xu_col[n_col][n_x + n_u]; get_xu_col( ..., xu_col ); ... }

Lo que no entiendo es por qué el compilador permite dimensionar las matrices. A mi entender, los tamaños deben ser fijos (por ejemplo, xu_col[9][7] ) o indefinidos (por ejemplo, xu_col[][] ). En el código anterior, parece que los tamaños no son constantes de tiempo de compilación.

¿Está el compilador simplemente ignorando los argumentos aquí? ¿O está realmente haciendo una verificación en tiempo de compilación de las dimensiones?

Si es lo último, entonces parece propenso a errores pasar las dimensiones por separado.

La segunda parte de la pregunta es:

¿Por qué no funciona la misma versión en C ++? Cuando literalmente cambio la extensión de archivo de .c a .cpp y trato de compilar, obtengo

candidate function not viable: no known conversion from ''double [n_col][n_x + n_u]'' to ''double (*)[n_x + n_u]'' for 7th argument void get_xu_col(int i_start, int n, double x[n], int n_x, int n_u, int n_col, double xu_col[n_col][n_x + n_u]);

Me gustaría saber qué idioma debo usar para convertir este código a C ++, ya que aparentemente el idioma anterior era algo que funciona en C, pero no en C ++.


Cuando se declara que un parámetro tiene un tipo de matriz unidimensional, C ignora el tamaño dado y, en cambio, trata el parámetro como un puntero al tipo de elemento. Para matrices anidadas (multidimensionales), dicho tratamiento solo se aplica a la matriz externa. En C89, las dimensiones internas tenían que tener tamaños fijos, pero en C99 las dimensiones pueden ser expresiones. Si los parámetros necesarios para calcular el tamaño de una matriz no se enumeran hasta después de la matriz, será necesario utilizar una curiosa combinación de sintaxis antigua y nueva para declarar la función, por ejemplo

int findNonzero(short dat[*][*], int rows, int cols); int findNonzero(dat, rows, cols) int rows,cols; short dat[static rows][cols]; { for (int i=0; i<rows; i++) for (int j=0; j<cols; j++) if (dat[i][j] != 0) return i; return -1; }

Tenga en cuenta que los tamaños de matriz se especifican como * en el prototipo de la función, y que la definición de la función no especifica los tipos en la lista de argumentos, sino que describe todos los tipos de parámetros entre la lista de argumentos y la llave de apertura. Tenga en cuenta también que si bien es probable que el compilador ignore el número de filas en la declaración de la matriz, un compilador inteligente puede usarlo para facilitar la optimización. Efectivamente, la extraña sintaxis "estática" invita al compilador a leer cualquier parte de la matriz, hasta el tamaño dado, según lo considere oportuno, ya sea que el código lea los valores o no. Esto puede ser útil en algunas plataformas donde el código podría beneficiarse del procesamiento de varios elementos de la matriz a la vez.


En C, es posible usar parámetros de función para definir el tamaño de un parámetro de matriz de longitud variable siempre que el tamaño aparezca antes de la matriz en la lista de parámetros. Esto no es compatible con C ++.


La dificultad con el ejemplo de código es que uno de los parámetros de la función es prototipo, double xu_col[n_col][n_x + n_u] , donde n_x y n_u son variables, no constantes. Si simplemente pasa esto como un double[] lugar, algunos compiladores de C ++ podrían permitir un lanzamiento como double (&table)[n_col][n_x + n_u] = (double(&)[n_col][n_x + n_u])xu_col; para trabajar como una extensión no estándar, pero el enfoque portátil sería escribir accesos como xu_col[i*(n_x+n_u) + j] , que podría simplificarse con una función auxiliar si eso es demasiado feo.

Un enfoque alternativo, probablemente más en consonancia con el espíritu de la STL, podría ser escribir una clase de contenedor mínimo que conozca sus dimensiones, almacene elementos en una matriz lineal para mayor eficiencia. Entonces puede declarar redim_array<double> table = redim_array<double>(xu_col, n_col*(n_x+n_u)).redim(n_col, n_x+n_u); y table(i,j) acceso table(i,j) .

Varias de las otras respuestas han descrito la sintaxis de las matrices de longitud variable, pero otro aspecto de su pregunta es cómo es legal convertir implícitamente una matriz bidimensional rectangular¹ en una matriz unidimensional.

Lo que sucede es que la matriz rectangular se presenta como elementos consecutivos en la memoria, por lo que puede degenerar en un puntero a los elementos, y luego el parámetro de función puede interpretar eso como una matriz con una geometría diferente.

Aquí hay un pequeño programa que demuestra este comportamiento.

#include <stddef.h> #include <stdio.h> #include <stdlib.h> #define ROWS 2 #define COLS 4 #define ELEMS (ROWS*COLS) int flatten_array( const ptrdiff_t n, const int a[n] ) { int printed = 0; for ( ptrdiff_t i = 0; i < n; ++i ) printed += printf( "%d ", a[i] ); return printed + printf("/n"); } int rectangular_array( const ptrdiff_t m, const ptrdiff_t n, const int a[m][n] ) { int printed = 0; for ( ptrdiff_t i = 0; i < m; ++i ) { for ( ptrdiff_t j = 0; j < n; ++j ) printed += printf( "%d ", a[i][j] ); printed += printf("/n"); } return printed + printf("/n"); } int main(void) { static const int matrix[ROWS][COLS] = { {11, 12, 13, 14}, {21, 22, 23, 24} }; static const int vector[ELEMS] = {11, 12, 13, 14, 21, 22, 23, 24}; flatten_array( ELEMS, *(const int (*const)[ELEMS])matrix ); printf("/n"); rectangular_array( ROWS, COLS, *(const int (*const)[ROWS][COLS])vector ); return EXIT_SUCCESS; }

En los comentarios a continuación se incluye información sobre la legislación lingüística acerca de si aprobar los argumentos de la matriz sin los conversos explícitos es técnicamente legal por norma. He elegido relegar eso a una nota al pie y simplemente eliminar el ejemplo sin cambios. En el mundo real, a veces verá código sin el puntero a matriz de diferente geometría, y podría generar una advertencia. El diseño de memoria de las dos matrices es requerido por el estándar para que sea el mismo.

Para convertir a C ++, puede usar el truco de conversión de puntero, o ahora puede codificarlo un poco usando referencias.

Aquí hay una traducción de C ++ del programa anterior. Requiere que la primera dimensión de la matriz que se pasa sea constexpr , pero algunos compiladores admiten matrices de longitud variable de estilo C99 como una extensión.

#include <stddef.h> #include <stdio.h> #include <stdlib.h> constexpr ptrdiff_t rows = 2; constexpr ptrdiff_t cols = 4; constexpr ptrdiff_t elems = rows * cols; int flatten_array( const ptrdiff_t n, const int a[] ) { int printed = 0; for ( ptrdiff_t i = 0; i < n; ++i ) printed += printf( "%d ", a[i] ); return printed + printf("/n"); } int rectangular_array( const ptrdiff_t n, const int a[][cols] ) { int printed = 0; for ( ptrdiff_t i = 0; i < n; ++i ) { for ( ptrdiff_t j = 0; j < cols; ++j ) printed += printf( "%d ", a[i][j] ); printed += printf("/n"); } return printed + printf("/n"); } int main(void) { static const int matrix[rows][cols] = { {11, 12, 13, 14}, {21, 22, 23, 24} }; static const int vector[elems] = {11, 12, 13, 14, 21, 22, 23, 24}; flatten_array( elems, (const int(&)[elems])matrix ); printf("/n"); rectangular_array( rows, (const int(&)[rows][cols])vector ); return EXIT_SUCCESS; }

Program Los programadores de C a veces llaman arrays como int matrix[ROWS][COLS] o arrays como char** argv "arrays bidimensionales". Aquí, llamo a la anterior rectangular y a la última irregular .

² La restricción en los argumentos de función en el estándar C11 es ''Cada argumento debe tener un tipo tal que su valor pueda asignarse a un objeto con la versión no calificada del tipo de su parámetro correspondiente.'' Además, ''La declaración de un parámetro como'' ''matriz de tipo'' ''se ajustará a'' ''puntero calificado para escribir'' '''' y, si esto se aplica recursivamente, una matriz multidimensional de algún tipo se ajustará a un puntero plano de ese tipo.


La razón por la que funciona en C, pero no en C ++ es simplemente porque es el código C y no C ++. Los dos idiomas comparten una historia, no una gramática.

El método de C ++ para pasar matrices de tamaño variable es std::vector , probablemente por referencia si pretende modificar el vector en la función, o por referencia const si no lo hace.


Todo lo que hace (en C) le permite escribir código de indexación en la función llamada sin tener que hacer el cálculo de la dirección, por ejemplo:

double d= xu_col[i*row_size + j]; //get element [i,j]

versus

double d= xu_col[i][j];


con respecto a la segunda parte de su pregunta:

¿Por qué no funciona la misma versión en C ++? Cuando literalmente cambio la extensión de archivo de .c a .cpp y trato de compilar, obtengo

La fuente de ese problema es que los nombres de los mangles en C ++.

Para evitar la manipulación de nombres al ejecutar C ++ e intentar acceder a una biblioteca de C.

cerca de la parte superior del archivo de encabezado de la biblioteca C, después de la inserción de la protección de inclusión múltiple:

#ifdef __cplusplus extern "C" { #endif

y cerca del final del archivo de encabezado, antes del #endif de la protección de inclusión múltiple, inserte:

#ifdef __cplusplus } #endif

Eso eliminará el problema de las funciones en el archivo de biblioteca asociado que no se encuentra