सवाल किसी फ़ंक्शन में सरणी पास करना (और यह 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 );
    ...
}

मुझे समझ में नहीं आता है कि क्यों संकलक सरणी में आकार देने की अनुमति देता है। मेरी समझ में सबसे अच्छा, या तो आकार तय किया जाना चाहिए (उदा। xu_col[9][7]) या अपरिभाषित (उदा। xu_col[][])। उपर्युक्त कोड में, ऐसा प्रतीत होता है कि आकार संकलित-समय स्थिरांक नहीं हैं।

क्या संकलक सिर्फ तर्कों को अनदेखा कर रहा है? या यह वास्तव में आयामों पर एक संकलन समय जांच कर रहा है?

यदि यह उत्तरार्द्ध है, तो यह अलग-अलग आयामों को पारित करने के लिए त्रुटि-प्रवण लगता है।

प्रश्न का दूसरा भाग यह है:

सी ++ में एक ही संस्करण क्यों काम नहीं करता है? जब मैं सचमुच फ़ाइल एक्सटेंशन को बदलता हूं .c सेवा मेरे .cpp और मैं recompile करने की कोशिश, मुझे मिलता है

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

मैं जानना चाहता हूं कि इस कोड को सी ++ में बदलने के लिए मुझे किस मुहावरे का उपयोग करना चाहिए, क्योंकि स्पष्ट रूप से पिछली मुहावरे सी में काम करती है, लेकिन सी ++ नहीं।


44
2018-02-21 08:46


मूल


दिलचस्प बात यह है कि कंपाइलर को वास्तव में सरणी आकारों के रूप में उपयोग किए गए तर्कों के मूल्यों को अनदेखा करना चाहिए जबतक कि आप उन्हें जोड़कर विचार करने के लिए मजबूर नहीं करते static (उदाहरण के लिए double x[static n])। के बावजूद देख जैसे यह कुछ करता है (और सी ++ में काम नहीं कर रहा है, और मानों को मान्य / इन-स्कोप एक्सप्रेशन होना आवश्यक है), प्रश्न में फ़ॉर्म थोड़ा बेकार है। भाषा में कष्टप्रद छेद। - Leushenko
मैं समझ नहीं पा रहा हूं कि आप शब्द क्या कह रहे हैं static करेंगे ... कीवर्ड जोड़ना होगा static इसे संकलित समय की जांच करें? - bremen_matt
प्रासंगिक: मैं सी ++ में सरणी का उपयोग कैसे करूं?। - sbi
यदि आप अनजान हैं, तो सी ++ परियोजनाओं में सी कोड का उपयोग करना संभव है, बिना "कोड को C ++ में परिवर्तित करना" - M.M
@ एमएम ओपी ने कहा कि उन्होंने फ़ाइल के विस्तार को परिवर्तित कर दिया है और संकलन करने की कोशिश करते समय असफल रहा। - a concerned citizen


जवाब:


सी में, चरम लंबाई सरणी पैरामीटर के आकार को परिभाषित करने के लिए फ़ंक्शन पैरामीटर का उपयोग करना संभव है जब तक कि पैरामीटर सूची में सरणी से पहले आकार आ जाए। यह सी ++ में समर्थित नहीं है।


60
2018-02-21 08:53



आप जोड़ना चाहते हैं कि कौन सा संस्करण जोड़ा गया है। - Deduplicator
सी 99 में वीएलए जोड़े गए थे, लेकिन सी 11 के लिए मानक (वैकल्पिक समर्थन) छोड़ दिया। - TWhelan


कारण यह सी में काम करता है, लेकिन सी ++ में नहीं है क्योंकि यह सी कोड है और सी ++ नहीं है। दो भाषाएं एक इतिहास साझा करती हैं, न कि व्याकरण।

परिवर्तनीय आकार के सरणी पास करने के लिए सी ++ विधि है std::vector, शायद द्वारा संदर्भ यदि आप फ़ंक्शन में वेक्टर को संशोधित करना चाहते हैं, या द्वारा const  संदर्भ अगर आप नहीं करते हैं।


37
2018-02-21 08:50



मैंने सोचा कि आपको उपयोग करना है [*] सी में चर लंबाई लंबाई सरणी पैरामीटर के लिए। - Bathsheba
@ बाथशेबा: पहली बार मैंने देखा [*] एक संदर्भ में पहले से ही बता रहा था क्यों [N] वहां भी काम किया। लेकिन सी ++ में न तो सिंटैक्स काम करने जा रहा है - सी ++ फोर्कड के बाद यह एक सी आविष्कार है। - MSalters
डाउनवॉटेड क्योंकि सवाल में दिए गए तथ्य को बस आराम देना क्यों नहीं है। मुझे संदेह है कि जवाब शायद इसलिए है क्योंकि इसे सी 99 या बाद में जोड़ा गया था (सी ++ के बाद सी के निकट-सुपरसैट के रूप में बनाया गया था), लेकिन मुझे पर्याप्त सी नहीं पता है कि अटकलों से ज्यादा कुछ भी हो। - Nye
@Nye: सी 99 के कुछ हिस्सों हैं जो इसे सी ++ में बनाते हैं, इसलिए इसका कारण नहीं है। लेकिन सी ++ पहले से ही था std::vector 1 99 8 से। इसे परिवर्तनीय-लंबाई सरणी की आवश्यकता नहीं थी। - MSalters
@bremen_matt: आप कैसे सी संस्करण को रनटाइम-चर-लंबाई सरणी पर संकलन-समय जांच कर सकते हैं? - DevSolar


मुझे समझ में नहीं आता है कि क्यों संकलक सरणी में आकार देने की अनुमति देता है। मेरी समझ में सबसे अच्छा करने के लिए, या तो आकार तय किए जाने चाहिए (उदा। Xu_col [9] [7]) या अपरिभाषित (उदा। Xu_col [] [])। उपर्युक्त कोड में, ऐसा प्रतीत होता है कि आकार संकलित-समय स्थिरांक नहीं हैं।

आप सही हैं, आकार संकलन-समय स्थिरांक नहीं हैं। यदि आपके पास द्वि-आयामी सरणी है, तो x [line] [col] कंपाइलर को तत्व के पते की गणना करने के लिए रेखा में तत्वों की संख्या की आवश्यकता होती है। Get_char_2 () और get_char_3 () उदाहरण कोड देखें।

यदि आप वर्चुअल लम्बाई सरणी (वीएलए) का उपयोग फ़ंक्शन पैरामीटर के रूप में करते हैं तो आपको इन नंबरों को आपूर्ति करना होगा (get_char_1 उदाहरण देखें)। तुम लिख सकते हो:

 my_func( x[][width] )

या आप लिख सकते हैं

 my_func( x[999][width] )

क्या संकलक सिर्फ तर्कों को अनदेखा कर रहा है? या यह वास्तव में आयामों पर संकलन-समय की जांच कर रहा है?

कंपाइलर द्वारा पहली संख्या (99 9) को अनदेखा कर दिया जाएगा। दूसरी जरूरत है। लाइन आकार के बिना, कंपाइलर इन 2 डी-सरणी के अंदर पते की गणना नहीं कर सकता है। कंपाइलर सी में वीएलए के लिए रन-टाइम या संकलन-समय जांच नहीं करता है।

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





यह सब कुछ करता है (सी में) आपको पता गणना करने के बिना बुलाए गए फनसीन में इंडेक्सिंग कोड लिखने की अनुमति देता है, उदाहरण के लिए:

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

बनाम

double d= xu_col[i][j];

6
2018-02-21 09:06





जब एक पैरामीटर को एकल-आयामी सरणी प्रकार के रूप में घोषित किया जाता है, तो सी दिए गए आकार को अनदेखा करता है और इसके बजाय पैरामीटर को तत्व प्रकार के सूचक के रूप में मानता है। नेस्टेड (बहु-आयामी) सरणी के लिए, इस तरह के उपचार केवल बाहरी सरणी पर लागू होता है। सी 8 9 में, आंतरिक आयामों में निश्चित आकार होना चाहिए था, लेकिन सी 99 में आयाम अभिव्यक्ति हो सकते हैं। यदि किसी सरणी के आकार की गणना करने के लिए आवश्यक पैरामीटर को सरणी के बाद तक सूचीबद्ध नहीं किया गया है, तो फ़ंक्शन घोषित करने के लिए पुराने और नए वाक्यविन्यास के उत्सुक मिश्रण का उपयोग करना आवश्यक होगा, उदा।

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

ध्यान दें कि सरणी आकार के रूप में निर्दिष्ट हैं * फ़ंक्शन प्रोटोटाइप में, और फ़ंक्शन परिभाषा तर्क सूची में प्रकार निर्दिष्ट नहीं करती है बल्कि तर्क सूची और उद्घाटन ब्रेस के बीच सभी पैरामीटर के प्रकारों का वर्णन करती है। ध्यान दें कि जब संकलक सरणी घोषणा में पंक्तियों की संख्या को अनदेखा कर सकता है, लेकिन एक स्मार्ट कंपाइलर ऑप्टिमाइज़ेशन की सुविधा के लिए इसका उपयोग करने में सक्षम हो सकता है। प्रभावी रूप से, अजीब "स्थैतिक" वाक्यविन्यास संकलक को दिए गए आकार तक, सरणी के किसी भी हिस्से को पढ़ने के लिए आमंत्रित करता है, क्योंकि यह फिट दिखाई देता है, चाहे कोड द्वारा मान पढ़े जाएं या नहीं। यह कुछ प्लेटफ़ॉर्म पर सहायक हो सकता है जहां कोड को सरणी के एकाधिक आइटम को एक बार में प्रोसेस करने से लाभ हो सकता है।


6
2018-02-21 21:40





आपके कोड नमूने में कठिनाई यह है कि फ़ंक्शन पैरामीटर में से एक को प्रकोप किया जाता है, double xu_col[n_col][n_x + n_u], कहा पे n_x तथा n_u स्थिर हैं, स्थिरांक नहीं। यदि आप इसे सिर्फ एक के रूप में पास करते हैं double[] इसके बजाए, कुछ सी ++ कंपाइलर्स जैसे कलाकारों को अनुमति दे सकते हैं double (&table)[n_col][n_x + n_u] = (double(&)[n_col][n_x + n_u])xu_col; एक गैर मानक विस्तार के रूप में काम करने के लिए, लेकिन पोर्टेबल दृष्टिकोण जैसे पहुंच लिखना होगा xu_col[i*(n_x+n_u) + j], जो कि आप एक सहायक काम के साथ सरल बना सकते हैं अगर वह बहुत बदसूरत है।

एसटीएल की भावना को ध्यान में रखते हुए शायद एक वैकल्पिक दृष्टिकोण, एक न्यूनतम कंटेनर कक्षा लिखना हो सकता है जो इसके आयामों को जानता है, दक्षता के लिए एक रैखिक सरणी में तत्वों को स्टोर करता है। तो आप घोषित कर सकते हैं redim_array<double> table = redim_array<double>(xu_col, n_col*(n_x+n_u)).redim(n_col, n_x+n_u); और पहुंच table(i,j)

कई अन्य उत्तरों ने परिवर्तनीय-लंबाई सरणी के सिंटैक्स का वर्णन किया है, लेकिन आपके प्रश्न का एक अन्य पहलू यह है कि आयताकार दो आयामी सरणी को एक-आयामी सरणी में पूर्ण रूप से परिवर्तित करना कानूनी है।

क्या होता है कि आयताकार सरणी स्मृति में लगातार तत्वों के रूप में रखी जाती है, इसलिए यह तत्वों के लिए एक सूचक को खराब कर सकती है, और फिर फ़ंक्शन पैरामीटर एक अलग ज्यामिति के साथ एक सरणी के रूप में व्याख्या कर सकता है।

यहां एक छोटा सा कार्यक्रम है जो इस व्यवहार को प्रदर्शित करता है।

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

नीचे दिए गए टिप्पणियों में कुछ भाषा-कानूनबद्धता है कि स्पष्ट सीमाओं के बिना सरणी तर्कों को पारित करना तकनीकी रूप से कानूनी रूप से कानूनी है या नहीं। मैंने इसे फुटनोट पर ले जाना चुना है और उदाहरण के साथ कोई उदाहरण नहीं हटाया है। असली दुनिया में, आप कभी-कभी पॉइंटर-टू-एरे-ऑफ-अलग-ज्यामिति कास्ट के बिना कोड देखेंगे, और यह एक चेतावनी उत्पन्न कर सकता है। मानक के द्वारा दो सरणी के मेमोरी लेआउट की आवश्यकता होती है।

सी ++ में कनवर्ट करने के लिए, आप पॉइंटर-रूपांतरण चाल का उपयोग कर सकते हैं, या अब आप संदर्भों का उपयोग कर इसे कोड-गोल्फ कर सकते हैं।

उपर्युक्त कार्यक्रम का सी ++ अनुवाद यहां दिया गया है। यह सभी आवश्यक है कि सरणी के पहले आयाम को पारित किया जा रहा है constexpr, लेकिन कुछ कंपाइलर एक्सटेंशन के रूप में C99-style चर-लंबाई सरणी का समर्थन करते हैं।

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

¹ सी प्रोग्रामर कभी-कभी या तो एरे कहते हैं int matrix[ROWS][COLS] या जैसे सरणी char** argv "द्वि-आयामी सरणी"। यहां, मैं पूर्व को बुलाता हूं आयताकार और बाद वाला टुकड़े टुकड़े कर दिया

² सी 11 मानक में फ़ंक्शन तर्कों पर बाधा है 'प्रत्येक तर्क में एक प्रकार होगा जैसे कि उसका मान किसी ऑब्जेक्ट को उसके संबंधित पैरामीटर के प्रकार के अयोग्य संस्करण के साथ असाइन किया जा सकता है।' इसके अलावा 'पैरामीटर की घोषणा' 'प्रकार की सरणी' 'को' 'टाइप करने के लिए योग्य पॉइंटर' 'में समायोजित किया जाएगा और यदि यह पुनरावर्ती रूप से लागू होता है, तो किसी प्रकार का बहुआयामी सरणी उस प्रकार के एक फ्लैट सूचक में समायोजित की जाएगी।


4
2018-02-22 04:19



rectangular_array( ROWS, COLS, vector ); गलत तर्क प्रकार को पारित करने के लिए एक बाधा उल्लंघन है। यह कानूनी नहीं है। - M.M
@ एमएम इससे पहले कि मैं आपके साथ सभी भाषा-वकील प्राप्त करूं: मैं अपने दूसरे कोड नमूने में इसका समाधान देता हूं, इसलिए यदि आप चाहें तो इसका उपयोग करें। - Davislor
@ एमएम मैंने एक फुटनोट के रूप में भाषा कानूनांकन जोड़ा। - Davislor


आपके प्रश्न के दूसरे भाग के बारे में:

सी ++ में एक ही संस्करण क्यों काम नहीं करता है? जब मैं सचमुच फ़ाइल एक्सटेंशन को .c से .cpp में बदलता हूं और पुनः संकलन करने का प्रयास करता हूं, तो मुझे मिलता है

उस समस्या का स्रोत यह है कि सी ++ मैंगल्स नाम।

C ++ चलाते समय और सी लाइब्रेरी तक पहुंचने का प्रयास करते समय नाम से बचने के लिए।

एकाधिक समावेशी गार्ड डालने के बाद, सी लाइब्रेरी के लिए हेडर फ़ाइल के शीर्ष के पास:

#ifdef __cplusplus
extern "C" {
#endif

और पहले से ही हेडर फ़ाइल के अंत के पास #endif एकाधिक समावेशन गार्ड के, डालें:

#ifdef __cplusplus
}
#endif

इससे गुज़रने वाली आवंटित लाइब्रेरी फ़ाइल में फ़ंक्शंस की समस्या को खत्म नहीं किया जाएगा


0
2018-02-22 09:12



उन लोगों के लिए जिन्होंने इस जवाब को कम किया, आपने इसे क्यों कम किया? - user3629249
मैंने डाउनवोट नहीं किया लेकिन इसका नाम बदलने के बजाए वीएलए के साथ और अधिक करना है? - wbkang
प्रश्न का मूल पाठ (जब बड़े पैमाने पर संपादित किया गया प्रतीत होता है) पूछें कि सी पुस्तकालय में कोई फ़ंक्शन क्यों नहीं मिल सका जब कॉलिंग फ़ंक्शन सी ++ प्रोग्राम से होता है। - user3629249
वैसे यह बहुत मूर्खतापूर्ण है। मुझे लगता है कि यह वास्तव में अक्सर होता है। - wbkang
मूल प्रश्न एक कंपाइलर त्रुटि को संदर्भित करता है जो एक लिंकर त्रुटि नहीं है, इसलिए मैं नहीं देख सकता कि इस प्रश्न के लिए नाम मैंगलिंग कैसे प्रासंगिक है। और यदि सभी कोड को C ++ के रूप में संकलित किया गया है (जो प्रश्न द्वारा निहित है - फ़ाइल प्रकार को .cpp पर स्विच करना) तो नाम मैंगलिंग अभी भी कोई समस्या नहीं होगी। - Steve Kidd