将数组传递给函数(以及它在C++中不起作用的原因)

bre*_*att 44 c c++

我遇到了一些编译的C代码,但我不明白为什么.具体来说,我有一个C库,它有很多使用这种格式的代码:

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 );
    ...
}
Run Code Online (Sandbox Code Playgroud)

我不明白的是编译器允许在数组中调整大小的原因.据我所知,尺寸必须固定(例如xu_col[9][7])或未定义(例如xu_col[][]).在上面的代码中,似乎大小不是编译时常量.

编译器是否只是忽略了这里的参数?或者它是否真的在维度上进行编译时检查?

如果是后者,那么分别传递尺寸似乎容易出错.

问题的第二部分是:

为什么相同的版本在C++中不起作用?当我从字面上文件扩展名从改变.c.cpp,并尝试重新编译,我得到

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]);
Run Code Online (Sandbox Code Playgroud)

我想知道我应该用哪个成语来将这个代码转换为C++,因为显然以前的习惯用法是用C语言编写的,而不是C++.

Ste*_*ocy 59

在C中,只要大小位于参数列表中的数组之前,就可以使用函数参数来定义可变长度数组参数的大小.C++不支持此功能.

  • 在C99中添加了VLA,但是保留了C11的标准(可选支持). (11认同)
  • 您可能想要添加添加的版本. (6认同)

MSa*_*ers 36

它在C中工作的原因,而不是在C++中工作的原因仅仅是因为它是C代码而不是C++.这两种语言共享历史,而不是语法.

C++的方法传递可变大小的数组是std::vector,可能是由参考如果打算修改向量的函数,或者通过const 参考如果不这样做.

  • 因为简单地重述问题中给出的事实而不是对原因的回答.我怀疑答案可能是因为它是在C99或更高版本中添加的(在C++被创建为C的近超集之后很久),但我不知道足够的C不仅仅是猜测. (6认同)
  • @Nye:C99的某些部分使它成为C++,所以这不是原因.但是自1998年以来C++已经有了`std :: vector`.它不需要可变长度数组. (4认同)
  • @bremen_matt:你如何看待C版本可以对运行时变量长度数组进行编译时检查? (4认同)
  • @Bathsheba:我第一次看到`[*]`已经在上下文中解释为什么`[N]`也在那里工作.但是这两种语法都不适用于C++ - 这是C++发布后的C发明. (3认同)

小智 11

我不明白的是编译器允许在数组中调整大小的原因.据我所知,大小必须固定(例如xu_col [9] [7])或未定义(例如xu_col [] []).在上面的代码中,似乎大小不是编译时常量.

你是对的,大小不是编译时常量.如果你有一个二维数组,x [line] [col],编译器需要一行中的元素数来计算一个元素的地址.查看get_char_2()和get_char_3()示例代码.

如果使用可变长度数组(VLA)作为函数参数,则必须提供这些数字(请参阅get_char_1示例).你可以写:

 my_func( x[][width] )
Run Code Online (Sandbox Code Playgroud)

或者你可以写

 my_func( x[999][width] )
Run Code Online (Sandbox Code Playgroud)

编译器是否只是忽略了这里的参数?或者它是否真的对维度进行了>编译时检查?

编译器将忽略第一个数字(999).第二是需要的.如果没有行大小,编译器就无法计算这些2D数组中的地址.编译器不对C中的VLA执行运行时或编译时检查.

/* 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;
}
Run Code Online (Sandbox Code Playgroud)


Pau*_*vie 5

它所做的一切(在C中)允许您在被调用的函数中编写索引代码而无需自己进行地址计算,例如:

double d= xu_col[i*row_size + j]; //get element [i,j]
Run Code Online (Sandbox Code Playgroud)

double d= xu_col[i][j];
Run Code Online (Sandbox Code Playgroud)


sup*_*cat 5

当参数声明为具有一维数组类型时,C忽略给定的大小,而是将参数视为指向元素类型的指针.对于嵌套(多维)数组,此类处理仅应用于外部数组.在C89中,内部尺寸必须具有固定尺寸,但在C99中尺寸可以是表达式.如果直到数组之后才列出计算数组大小所需的参数,则必须使用新旧语法的奇怪混合来声明函数,例如

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,数组大小*在函数原型中指定,并且函数定义不在参数列表中指定类型,而是描述参数列表和左大括号之间的所有参数类型.另请注意,虽然编译器可能会忽略数组声明中的行数,但智能编译器可能能够使用它来促进优化.实际上,奇怪的"静态"语法邀请编译器按照它认为合适的方式读取数组的任何部分,直到给定的大小,无论代码是否读取了这些值.这在某些平台上可能会有所帮助,在这些平台上,代码可能会同时处理多个数组项目而受益.