Ich möchte ein großes 2D-Array in C wiederholt auf Null setzen. Dies ist, was ich im Moment mache:
// Array of size n * m, where n may not equal m
for(j = 0; j < n; j++)
{
for(i = 0; i < m; i++)
{
array[i][j] = 0;
}
}
Ich habe versucht, memset zu verwenden:
memset(array, 0, sizeof(array))
Dies funktioniert jedoch nur für 1D-Arrays. Wenn ich den Inhalt des 2D-Arrays drucke, ist die erste Zeile Nullen, aber dann habe ich eine Menge zufälliger großer Zahlen und es stürzt ab.
memset
, weil Sie erwähnt haben, dass Sie nur eine Zeile auf Null gesetzt haben.int d0=10, d1=20; int arr[d0][d1]
wurde undmemset(arr, 0, sizeof arr);
wie erwartet funktioniert hat (gcc 3.4.6, kompiliert mit-std=c99 -Wall
Flags). Mir ist klar, dass "es funktioniert auf meiner Maschine" bedeutet, in die Hocke zu gehen, abermemset(arr, 0, sizeof arr);
es hätte funktionieren sollen.sizeof arr
sollte die Anzahl der vom gesamten Array verwendeten Bytes zurückgeben (d0 * d1 * sizeof (int)).sizeof array[0] * m * n
gibt Ihnen nicht die richtige Größe des Arrays.int array[][10]
,sizeof(array) == sizeof(int*)
ist die Größe der ersten Dimension nicht bekannt. Das OP hat nicht angegeben, wie das Array erhalten wurde.Wenn
array
es sich wirklich um ein Array handelt, können Sie es mit "nullen":Es gibt jedoch zwei Punkte, die Sie kennen sollten:
array
es sich tatsächlich um ein "Two-D-Array" handelt, dhT array[M][N];
für einen Typ deklariert wurdeT
.array
deklariert wurde. Wenn Sie es an eine Funktion übergeben, wird der Namearray
zu einem Zeiger undsizeof
gibt Ihnen nicht die Größe des Arrays.Lassen Sie uns ein Experiment machen:
Auf meinem Computer wird Folgendes gedruckt:
Obwohl
arr
es sich um ein Array handelt, zerfällt es bei Übergabe in einen Zeiger auf sein erstes Elementf()
, und daher sind die eingedruckten Größenf()
"falsch". Inf()
der Größe vonarr[0]
ist auch die Größe des Arraysarr[0]
, das ein "Array [5] vonint
" ist. Es ist nicht die Größe von aint *
, da das "Zerfallen" nur auf der ersten Ebene stattfindet, und deshalb müssen wir deklarierenf()
, dass ein Zeiger auf ein Array mit der richtigen Größe verwendet wird.Wie ich bereits sagte, funktioniert das, was Sie ursprünglich getan haben, nur, wenn die beiden oben genannten Bedingungen erfüllt sind. Wenn nicht, müssen Sie tun, was andere gesagt haben:
Schließlich
memset()
und die vonfor
Ihnen gepostete Schleife sind im engeren Sinne nicht gleichwertig. Es könnte (und gab) Compiler geben, bei denen "alle Bits Null" für bestimmte Typen, wie z. B. Zeiger und Gleitkommawerte, nicht gleich Null ist. Ich bezweifle, dass Sie sich darüber Sorgen machen müssen.quelle
memset(array, 0, n*n*sizeof array[0][0]);
Ich denke du meinstm*n
nichtn*n
richtig?memset
funktioniert auf Byte-Ebene (char). Da die zugrunde liegende Darstellung dieselben Bytes enthält1
oder2
nicht, können Sie dies nicht tunmemset
.int
auf Ihrem System 4 Bytes sind" irgendwo vor dem minimalen Arbeitsbeispiel, damit der Leser die Summen leicht berechnen kann.Nun, der schnellste Weg, dies zu tun, besteht darin, es überhaupt nicht zu tun.
Klingt seltsam, ich weiß, hier ist ein Pseudocode:
Eigentlich wird das Array immer noch gelöscht, aber nur, wenn etwas in das Array geschrieben wird. Dies ist hier kein großer Vorteil. Wenn das 2D-Array beispielsweise mithilfe eines Quad-Baums (kein dynamischer Geist) oder einer Sammlung von Datenzeilen implementiert wurde, können Sie den Effekt des Booleschen Flags lokalisieren, benötigen jedoch mehr Flags. Setzen Sie im Quad-Baum einfach das leere Flag für den Wurzelknoten, im Array von Zeilen setzen Sie einfach das Flag für jede Zeile.
Was führt zu der Frage "Warum möchten Sie ein großes 2D-Array wiederholt auf Null setzen?" Wofür wird das Array verwendet? Gibt es eine Möglichkeit, den Code so zu ändern, dass das Array nicht auf Null gesetzt werden muss?
Zum Beispiel, wenn Sie hatten:
Verwenden Sie es also für einen Akkumulationspuffer. Wenn Sie es dann so ändern, wird die Leistung ohne Ende verbessert:
Dies erfordert nicht, dass das Array gelöscht wird, funktioniert aber trotzdem. Und das ist viel schneller als das Löschen des Arrays. Wie gesagt, der schnellste Weg ist, es überhaupt nicht zu tun.
quelle
Wenn Sie wirklich, wirklich von Geschwindigkeit besessen sind (und nicht so sehr von Portabilität), denke ich, wäre der absolut schnellste Weg, dies zu tun, die Verwendung von SIMD-Vektor-Intrinsics. Auf Intel-CPUs können Sie beispielsweise die folgenden SSE2-Anweisungen verwenden:
Jeder Speicherbefehl setzt vier 32-Bit-Ints bei einem Treffer auf Null.
p muss 16-Byte-ausgerichtet sein, aber diese Einschränkung ist auch gut für die Geschwindigkeit, da sie dem Cache hilft. Die andere Einschränkung ist, dass p auf eine Zuordnungsgröße zeigen muss, die ein Vielfaches von 16 Bytes ist, aber dies ist auch cool, weil es uns ermöglicht, die Schleife einfach abzuwickeln.
Haben Sie dies in einer Schleife und rollen Sie die Schleife ein paar Mal ab, und Sie werden einen verrückten schnellen Initialisierer haben:
Es gibt auch eine Variante
_mm_storeu
, die den Cache umgeht (dh das Nullstellen des Arrays verschmutzt den Cache nicht), was unter bestimmten Umständen zu sekundären Leistungsvorteilen führen kann.Eine SSE2-Referenz finden Sie hier: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx
quelle
Wenn Sie das Array mit initialisieren
malloc
, verwenden Siecalloc
stattdessen; Ihr Array wird kostenlos auf Null gesetzt. (Gleiche Leistung offensichtlich wie Memset, nur weniger Code für Sie.)quelle
int array[N][M] = {0};
... zumindest in GCC 4.8.
quelle
Wie wurde Ihr 2D-Array deklariert?
Wenn es so etwas wie:
Sie können es auf Null setzen, indem Sie Folgendes tun:
quelle
memset(a, 0, sizeof(char)*10*10);
funktioniert gut für mich. , wie kommt es?Verwenden Sie calloc anstelle von malloc. calloc initiiert alle Felder auf 0.
int * a = (int *) calloc (n, Größe von (int));
// Alle Zellen von a wurden auf 0 initialisiert
quelle
Ich denke, dass der schnellste Weg, dies von Hand zu tun, darin besteht, dem Code zu folgen. Sie können die Geschwindigkeit mit der Memset-Funktion vergleichen, sie sollte jedoch nicht langsamer sein.
(Ändern Sie den Typ der ptr- und ptr1-Zeiger, wenn sich Ihr Array-Typ von int unterscheidet.)
quelle
memset
bei Zeichentypen.quelle
Sie können dies versuchen
quelle
Dies liegt daran, dass sizeof (Array) die Zuordnungsgröße des Objekts angibt, auf das das Array zeigt . ( Array ist nur ein Zeiger auf die erste Zeile Ihres mehrdimensionalen Arrays). Sie haben jedoch j Arrays der Größe i zugewiesen . Folglich müssen Sie die Größe einer Zeile, die von sizeof (Array) zurückgegeben wird, mit der Anzahl der von Ihnen zugewiesenen Zeilen multiplizieren, z.
Beachten Sie auch, dass sizeof (Array) nur für statisch zugewiesene Arrays funktioniert. Für ein dynamisch zugewiesenes Array würden Sie schreiben
quelle
sizeof
Operatorarray
ist kein Zeiger (wenn er als Array deklariert wurde). Siehe meine Antwort für ein Beispiel.