2009-03-30 11 views
4

Gibt es eine einfache Möglichkeit, eine Spalte in einem 2-D-Array als separate 1-D-Array in plain alten C (nicht C++ oder C#) zu referenzieren? Es ist einfach, dies für eine Zeile zu tun. Asssume Ich habe 2 Funktionen:Referenzieren Sie eine 2-D Array-Spalte in C?

double doSomethingWithARow(double theRow[3]); 
double doSomethingWithACol(double theCol[100]); 

Dann könnte ich die erste wie folgt verwenden:

double matrix[100][3]; 
double result; 

// pass a single row to a function as an array 
// this essentially passes the 3-element array at row 48 to the function 
for(int i=0; i < 100; i++) 
{ 
    result = doSomethingWithARow(matrix[i]); 
} 

Was ich es einen Weg wollen einfach eine Spalte zugreifen.

Das einzige, was ich bis jetzt gefunden habe, ist die Matrix zu transformieren, um die Zeilen mit den Spalten zu tauschen. Aber dieser Code soll hinsichtlich Speicher und Geschwindigkeit so effizient wie möglich sein. Mit all den verschlungenen Wegen, auf Zeiger in C zu verweisen, scheint es, als sollte es einen Weg geben, dies zu tun.

Antwort

3

Eine schöne typsichere Art und Weise, dies zu tun, ohne die Abmessungen als separaten Parameter zu spezifiziert ist wie folgt:

#define ROWS 100 
#define COLUMNS 30 

void doSomethingToAllRows(double (*row)[ROWS][COLUMNS], int col, double val) 
{ 
    for(size_t i = 0; i < ROWS; ++i) 
     (*row)[i][col] = val; 
} 

void doSomethingToAllColumns(double (*col)[ROWS][COLUMNS], int row, double val) 
{ 
    for(size_t i = 0; i < COLUMNS; ++i) 
     (*col)[row][i] = val; 
} 

int main(int argc, char **argv) 
{ 
    double matrix[ROWS][COLUMNS]; 

    /* Modify each column of the 10th row with the value of 3 */ 
    doSomethingToAllColumns(&matrix, 10, 3); 

    /* Modify each row of the 10th column with the value of 3 */ 
    doSomethingToAllRows(&matrix, 10, 3); 

    return 0; 
} 

Es ist völlig falsch, ein Doppel ** für diese passieren Grund:

void test() 
{ 
    double **a; 
    int i1 = sizeof(a[0]);//i1 == 4 == sizeof(double*) 

    double matrix[ROWS][COLUMNS]; 
    int i2 = sizeof(matrix[0]);//i2 == 240 == COLUMNS * sizeof(double) 
} 

Wenn Sie in einem Doppelgeben ** dann zugegriffen es wie ein Array Sie einen Absturz, segfault oder nicht definiertes Verhalten verursachen würde.

+0

Ihre Funktionen verwenden Referenzen, während das ursprüngliche Poster C angegeben hat Abgesehen davon ist es eine gute Lösung, wenn Sie verzweifelt vermeiden, einen Container zu verwenden. –

+0

@Andrew Grant: Guter Punkt, ich habe es in Zeiger geändert. –

+0

+1, weil dieser Code meiner Lösung vorzuziehen ist, wenn die Dimensionen der im Programm verwendeten Arrays festgelegt sind. – Stephan202

0

Da die "Spalten", wie Sie sie nennen, nicht im Speicher abgelegt sind, gibt es keine Möglichkeit, diese direkt abzusetzen.

Sie können jedoch ein Array von Zeigern erstellen und Verweise auf die Indizes des anderen Arrays speichern. Sie müssten alle Elemente in Ihrem Array durchlaufen, also ist es wahrscheinlich keine bessere Lösung als alle anderen. Abhängig davon, wie oft Sie auf das Array nach Spalte zugreifen müssen, kann es sich jedoch lohnen.

5

Nun, würden Sie die Größe einer Zeile und die Anzahl der Zeilen bestehen:

double doSomethingWithACol(double *matrix, size_t colID, size_t rowSize, size_t nRows); 

Jetzt können Sie sich die Tatsache zunutze, dass Matrix [i] [j] = Matrix machen + i * rowSize + j;

Alternativ, können Sie auch die folgende Signatur verwenden:

double doSomethingWithACol(double *colPtr, size_t rowSize, size_t nRows); 

Hier finden Sie den Zeiger auf das erste Element der Spalte, die Sie bearbeiten wollen, statt des Zeigers passieren müssen in die erste Reihe.


Beispielcode: dieser Code die Elemente in der zweiten Spalte summiert (zusammenstellen mit gcc -o Haupt -Wall -Wextra -pedantic -std = C99 test.c):

#include <stdio.h> 
#include <stdlib.h> 

double colSum1(double *matrix, size_t colID, size_t rowSize, size_t nRows) 
{ 
    double *c = NULL, *end = matrix + colID + (nRows * rowSize); 
    double sum = 0; 

    for (c = matrix + colID; c < end; c += rowSize) { 
    sum += *c; 
    } 

    return sum; 
} 

double colSum2(double *colPtr, size_t rowSize, size_t nRows) 
{ 
    double *end = colPtr + (nRows * rowSize); 
    double sum = 0; 

    for (; colPtr < end; colPtr += rowSize) { 
    sum += *colPtr; 
    } 

    return sum; 
} 

int 
main(void) 
{ 
    double matrix[4][3] = { 
    {0, 1, 2}, 
    {3, 4, 5}, 
    {6, 7, 8}, 
    {9, 10, 11} 
    }; 

    printf("%f\n", colSum1(*matrix, 1, 3, 4)); 
    printf("%f\n", colSum2(&matrix[0][1], 3, 4)); 
    printf("%f\n", colSum2(matrix[0] + 1, 3, 4)); 

    return EXIT_SUCCESS; 
} 
+0

Sie schlagen mich dazu .... –

+0

Dies wird nicht tun, was Sie denken. Es ist völlig falsch, ein Double ** zu übergeben. Dies führt zu einem segfault in der Anwendung. Ich erkläre mehr in meiner Antwort. –

+0

Der doppelte ** ist nicht notwendig; Das ist mir aufgefallen, als ich meinen Beispielcode geschrieben habe. Es hat jedoch nichts mit segfaults zu tun. – Stephan202

0

Sie können das nicht wirklich tun, weil Arrays in C so gespeichert werden, dass die Elemente jeder Zeile zusammen gespeichert werden. Das bedeutet, dass eine Reihe eines Arrays ein fortlaufender Speicherblock ist, und was C betrifft, könnte es auch ein unabhängiges Array selbst sein. Bei Spalten funktioniert das nicht genauso, weil die Elemente einer Spalte im Speicher nicht fortlaufend sind. vielmehr sind sie in Intervallen von N Bytes beabstandet, wobei jede Zeile N Bytes lang ist. Dies bedeutet, dass Sie effizient auf die verschiedenen Elemente einer Spalte eines 2D-Arrays mithilfe von Zeigerarithmetik zugreifen können. Es gibt jedoch keine Möglichkeit, eine Spalte tatsächlich zu einem Array zu machen, außer durch Kopieren der Elemente in ein neues Array.

0

Nein, gibt es nicht. Das kann nicht sein, da in C ein Array ein fortlaufender Teil des Speichers ist, und es ist trivial, dass die Zeilen und Spalten nicht gleichzeitig hintereinander sein können.

Das gesagt, es ist ziemlich einfach, von einer Zelle einer Spalte zur nächsten zu springen, wenn Sie die Länge der Zeilen kennen. Nehmen Sie das folgende Beispiel:

void processColumn(double *array, int colIdx, int rowLen, int rowCnt) { 
    for (int i = colIdx; i < rowCnt * rowLen; i += rowLen) { 
     // do whatever you want 
    } 
} 

#define N 5 
#define M 10 

double array[N*M]; 

processColumn(array, 3, N, M);