Soru Diziyi bir işleve (ve neden C ++ içinde çalışmadığı) geçirme


Derleyen bazı C kodlarına rastladım ama nedenini anlamıyorum. Özellikle, bu formatı kullanarak çok fazla kodu olan bir C kütüphanem var:

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 );
    ...
}

Anlamadığım şey, derleyicinin dizilerde boyutlandırmaya neden izin verdiğidir. Anlayışımın en iyisine göre, boyutlar sabitlenmelidir (ör. xu_col[9][7]) veya tanımlanmamış (ör. xu_col[][]). Yukarıdaki kodda, boyutların derleme zamanı sabitleri olmadığı anlaşılmaktadır.

Derleyici buradaki argümanları görmezden mi geliyor? ya da gerçekten boyutlarda bir derleme zamanı kontrolü yapıyor mu?

Eğer ikincisiyse, boyutları ayrı ayrı geçmek hataya eğilimli görünüyor.

Sorunun ikinci kısmı:

Neden aynı sürüm C ++'da çalışmıyor? Dosya uzantısını tam olarak değiştirdiğimde .c için .cpp ve yeniden derlemeye çalışıyorum

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]);

Bu kodu C ++ 'ya çevirmek için hangi deyimi kullanmam gerektiğini bilmek istiyorum çünkü görünüşe göre önceki deyim C ++' da çalışan bir şeydi.


44
2018-02-21 08:46


Menşei


İlginç bir şekilde, derleyici, ekleyerek bunları dikkate almak için zorlamadıkça dizi boyutları olarak kullanılan bağımsız değişkenlerin değerlerini gerçekten görmezden gelmelidir. static (Örneğin. double x[static n]). Rağmen seyir bir şey yapar (C ++ 'da çalışmıyor ve değerlerin geçerli / kapsam ifadeleri olmasını gerektiriyor) gibi, sorudaki form biraz işe yaramıyor. Dilde sinir bozucu delik. - Leushenko
Ne demek istediğini anlamıyorum static yapar ... anahtar kelime ekleyerek static derleme zamanı kontrolü yapalım mı? - bremen_matt
İlgili: C ++'da dizileri nasıl kullanırım?. - sbi
Eğer farkında değilseniz, C ++ projelerinde "kodu C ++ 'ya dönüştürmeden" C kodu kullanmak mümkündür. - M.M
@ M.M OP, sadece dosyanın uzantısını dönüştürdüğünü ve derlemeye çalışıldığında başarısız olduğunu söyledi. - a concerned citizen


Cevaplar:


C'de, boyut, parametre listesindeki diziden önce geldiği sürece değişken uzunluklu dizi parametresinin boyutunu tanımlamak için işlev parametrelerini kullanmak mümkündür. Bu C ++'da desteklenmez.


60
2018-02-21 08:53



Hangi sürümü eklediğini eklemek isteyebilirsiniz. - Deduplicator
CLA'ya VLA'lar eklenmiş, ancak C11 için standart (isteğe bağlı destek) bırakılmıştır. - TWhelan


C ++ 'da çalışmasının nedeni, C ++' da değil, C kodu ve C ++ 'dır. İki dil bir dilbilgisi değil, bir tarihi paylaşır.

Değişken boyutlu dizileri geçmek için C ++ yöntemi std::vector, muhtemelen referans Eğer vektörü fonksiyonda değiştirmeyi düşünüyorsanız veya const  referans eğer yapmazsan.


37
2018-02-21 08:50



Kullanmak zorunda olduğunu sanıyordum [*] C. değişken uzunluklu dizi fonksiyon parametreleri için - Bathsheba
@Bathsheba: İlk gördüğüm zaman [*] zaten neden açıklayan bir bağlamdaydı [N] orada da çalıştı. Ama ne sözdizimi C ++ 'da çalışmayacak - bu C ++' nın çatal atmasından sonra bir C icadıdır. - MSalters
Düşündüm, çünkü basitçe sorudaki gerçeği yeniden ifade etmek, neden bir cevap değildir. Cevabın büyük olasılıkla C99 ya da daha sonra eklendiği (C ++ 'nın C-yakınının üst kümesi olarak oluşturulduktan çok uzun süre sonra) olduğu, ancak spekülasyondan daha fazla bir şey olması için yeterince C bilmediğime inanıyorum. - Nye
@Nye: C ++ 'nun C ++' ya dönüştürdüğü bölümler var, bu yüzden sebep değil. Ama C ++ zaten vardı std::vector 1998'den beri değişken uzunlukta dizilere ihtiyaç duymadı. - MSalters
@bremen_matt: C sürümünün çalışma zamanı değişken uzunluklu dizide derleme zamanı denetimi yapabileceğini nasıl anlayabilirsiniz? - DevSolar


Anlamadığım şey, derleyicinin dizilerde boyutlandırmaya neden izin verdiğidir. Anlayışımın en iyisine göre, ya boyutlar sabitlenmelidir (örneğin, xu_col [9] [7]) ya da tanımlanmamış (ör., Xu_col [] []). Yukarıdaki kodda, boyutların derleme zamanı sabitleri olmadığı anlaşılmaktadır.

Haklısınız, boyutlar derleme zamanı sabitleri değildir. İki boyutlu bir diziniz varsa, x [line] [col] derleyici, bir elemanın adresini hesaplamak için bir satırdaki elemanların sayısına ihtiyaç duyar. Get_char_2 () ve get_char_3 () örnek koduna bakın.

Eğer değişken uzunluklu dizileri (VLA) işlev parametreleri olarak kullanırsanız, bu sayıyı sağlamanız gerekir (bkz. Get_char_1 örneği). Yazabilirsin:

 my_func( x[][width] )

ya da yazabilirsiniz

 my_func( x[999][width] )

Derleyici buradaki argümanları görmezden mi geliyor? ya da gerçekten boyutlarda bir> derleme zamanı denetimi yapıyor mu?

İlk sayı (999) derleyici tarafından yok sayılacak. İkincisi gerekli. Çizgi boyutu olmadan, derleyici bu 2B dizinin içindeki adresleri hesaplayamaz. Derleyici, C'deki VLA'lar için çalışma zamanı veya derleme zamanı denetimleri yapmaz.

/* 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;
}

12
2018-02-21 16:36





Yaptığı her şey (C cinsinden) adres hesaplamasını kendiniz yapmak zorunda kalmadan çağrılan funcida indeksleme kodu yazmanıza izin verir, örneğin:

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

e karşı

double d= xu_col[i][j];

6
2018-02-21 09:06





Bir parametre, tek boyutlu bir dizi türüne sahip olduğu bildirildiğinde, verilen boyutu yok sayar ve bunun yerine, parametreyi öğe türüne bir işaretçi olarak işler. Yuvalanmış (çok boyutlu) diziler için, bu tür tedavi sadece dış diziye uygulanır. C89'da iç boyutlar sabit boyutlara sahip olmalıydı, ancak C99'da boyutlar ifadeler olabilir. Bir dizinin büyüklüğünü hesaplamak için gerekli olan parametreler dizinin sonuna kadar listelenmemişse, işlevi bildirmek için eski ve yeni söz diziminin ilginç bir karışımını kullanmak gerekir.

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;
}

Dizi boyutlarının şu şekilde belirtildiğini unutmayın. * işlev prototipinde ve işlev tanımının argüman listesindeki türleri belirtmediği, bunun yerine argüman listesi ile açılış ayracı arasındaki tüm parametrelerin türlerini açıklar. Ayrıca derleyicinin dizi bildirimi içindeki satır sayısını görmezden gelmesine karşın, bir akıllı derleyicinin optimizasyonu kolaylaştırmak için kullanabileceğini unutmayın. Etkili olarak, garip "statik" sözdizimi, derleyicinin, değerlerin kod tarafından okunup okunmadığına bakılmaksızın, dizinin herhangi bir bölümünü, belirtilen boyutlara kadar, uygun gördüğü şekilde okumaya davet eder. Bu, kodun dizinin birden fazla öğesinin bir kerede işlenmesinden yararlanabileceği bazı platformlarda yardımcı olabilir.


6
2018-02-21 21:40





Kod örneğinizdeki zorluk, işlev parametrelerinden birinin protoipli olmasıdır. double xu_col[n_col][n_x + n_u], nerede n_x ve n_u değişkenler, sabit değil. Eğer bunu sadece bir double[] bunun yerine, bazı C ++ derleyicileri double (&table)[n_col][n_x + n_u] = (double(&)[n_col][n_x + n_u])xu_col; standart olmayan bir uzantı olarak çalışmak için, ancak taşınabilir yaklaşım gibi erişim yazmak olacaktır xu_col[i*(n_x+n_u) + j]Çok çirkinse yardımcı işlevle basitleştirebilirsiniz.

Alternatif bir yaklaşım, muhtemelen daha çok STL'nin ruhuna uygun olarak, boyutlarını bilen, elemanları doğrusal bir dizide verimlilik için depolayan minimal bir konteyner sınıfı yazmak olabilir. Sonra ilan edebilirsin redim_array<double> table = redim_array<double>(xu_col, n_col*(n_x+n_u)).redim(n_col, n_x+n_u); ve erişim table(i,j).

Diğer cevaplardan bazıları değişken uzunluktaki dizilerin sözdizimini açıkladı, ancak sorunuzun başka bir yönü de dikdörtgen bir iki boyutlu diziyi tek boyutlu bir diziye dolaylı olarak dönüştürmenin yasal olması.

Olan şey, dikdörtgen dizinin bellekte ardışık elemanlar olarak yerleştirilmiş olmasıdır, bu yüzden elemanlara bir gösterici için dejenere olabilir ve daha sonra işlev parametresi bunu farklı bir geometriye sahip bir dizi olarak yorumlayabilir.

İşte bu davranışı gösteren kısa bir küçük program.

#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;
}

² Aşağıdaki yorumlarda, açık argümanlar olmadan dizi argümanlarının aktarılmasının standart olarak teknik olarak yasal olup olmadığı konusunda bir dil avukatlığı vardır. Bunu bir dipnotla yeniden hedeflemeyi seçtim ve yalnızca örneği yoktan yok. Gerçek dünyada, bazen, farklı geometri işaretçileri dizisi olmadan kodu görürsünüz ve bir uyarı oluşturabilir. İki dizinin bellek düzeninin standart olarak aynı olması gerekir.

C ++ 'ya dönüştürmek için, işaretçi-dönüşüm kodunu kullanabilirsiniz, ya da artık referanslar kullanarak kod-kodunu değiştirebilirsiniz.

Yukarıdaki programın C ++ çevirisi. Her şeyden önce dizinin ilk boyutunun geçmesini gerektirir. constexprancak bazı derleyiciler C99 stili değişken uzunluklu dizileri bir uzantı olarak destekler.

#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 C programcıları bazen dizileri ararlar int matrix[ROWS][COLS] ya da diziler gibi char** argv “İki boyutlu diziler.” Burada eskiyi ararım dikdörtgen biçiminde ve ikincisi düzensiz.

² C11 standardındaki fonksiyon argümanlarına ilişkin kısıtlama 'Her argüman, ilgili parametresinin türünün niteliksiz versiyonuna sahip bir nesneye atanabilecek bir türe sahip olmalıdır.' Ayrıca 'Bir parametrenin bir beyanı' '' type of '' '' '' '' 'nitelikli işaretçisine' '' 'olarak ayarlanmalı ve eğer bu yinelemeli olarak geçerliyse, bir türden çok boyutlu bir dizi bu tür bir düz işaretçiye göre ayarlanacaktır.


4
2018-02-22 04:19



rectangular_array( ROWS, COLS, vector ); Yanlış argüman türünü geçirirken bir kısıtlama ihlalidir. Yasal değil. - M.M
@ M.M Bütün dil-avukatınızı yanınıza almadan önce, ikinci kod örneğimde buna çözüm buluyorum, bu yüzden tercih ederseniz kullanın. - Davislor
@ M.M Dil avukatlığını dipnot olarak ekledim. - Davislor


sorunuzun ikinci kısmı ile ilgili olarak:

Neden aynı sürüm C ++'da çalışmıyor? Dosya uzantısını tam olarak .c ile .cpp arasında değiştirdiğimde ve yeniden derlemeye çalıştığımda

Bu sorunun kaynağı C + + 'nın isimleri.

C ++ çalıştırırken isim denetiminden kaçınmak ve bir C kütüphanesine erişmeye çalışmak.

Birden fazla içerme koruması eklendikten sonra C kitaplığı için üstbilgi dosyasının üst kısmına yakın:

#ifdef __cplusplus
extern "C" {
#endif

ve başlık dosyasının sonuna yakın #endif çoklu kapsayıcı korumanın eklenmesi:

#ifdef __cplusplus
}
#endif

Bu bulunmayan kütüphane dosyasındaki fonksiyonların problemini ortadan kaldıracak


0
2018-02-22 09:12



Bu cevabı reddedenler için NEDEN reddettiniz? - user3629249
Ben downvote ama isim mangling yerine VLAs ile daha fazla ilgisi var mı? - wbkang
Sorunun orijinal metni (büyük ölçüde düzenlenmiş gibi görünüyor), arama işlevi bir C ++ programından olduğunda neden C kütüphanesindeki bir fonksiyonun bulunamadığını sorun. - user3629249
Peki bu çok aptalca. Bunun aslında oldukça sık olduğunu düşünüyorum. - wbkang
Orijinal soru, bir derleyici hatası değil bir linker hatası ile ilgilidir, bu nedenle isim karmasının THIS sorusuyla ne kadar ilgili olduğunu göremiyorum. Ve eğer tüm kod C ++ olarak derlenirse (bu soruya işaret edilir - dosya türünün .cpp'ye çevrilmesi), manglingin ismi hala bir sorun olmayacaktır. - Steve Kidd