Menu

C'de 2B Diziler: Çok Boyutlu Diziler, Bellek Yerleşimi ve Matrisler

C'de 2B dizilerin nasıl tanımlanacağı, ilk değer verileceği ve üzerlerinde döngü kurulacağı, satır-öncelikli yerleşimin bellekte gerçekte ne anlama geldiği, bir 2B diziyi bir fonksiyona geçirmenin neden sütun sayısını gerektirdiği ve işlenmiş bir matris örneği.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Çok boyutlu dizi, elemanları da dizi olan bir dizidir. int grid[3][4]; özel bir ızgara tipi değildir - her biri dört int'lik bir dizi olan, arka arkaya saklanmış üç elemandır. Bu anlaşıldığında konunun gerisi takip eder: yerleşim, indeksleme aritmetiği ve onları fonksiyonlara geçirmekle ilgili başka türlü şaşırtıcı olan kural.

İki boyut neredeyse her pratik kullanımı kapsar - ızgaralar, tablolar, matrisler, oyun tahtaları, görüntüler - dolayısıyla bu sayfa onunla çalışıyor.

Tanımlamak ve İlk Değer Vermek

int grid[3][4];        // 3 satır, 4 sütun - 12 int

İlk sayı kaç satır, ikincisi kaç sütun olduğudur. İlk değerler düz ya da iç süslü parantezlerle yazılabilir; süslü parantezleri kullanmaya değer, çünkü biçimi gösterirler.

int e[][3] biçimi önemlidir: satır sayısını boş bırakıp ilk değerin karar vermesine izin verebilirsiniz, ama sütun sayısı asla isteğe bağlı değildir. Sonraki bölüm nedenini açıklıyor.

Satır-Öncelikli Yerleşim

C bir 2B diziyi satır-öncelikli sırada saklar: 0. satırın tamamı, sonra 1. satırın tamamı vb., kesintisiz tek bir bellek bloğunda. Perde arkasında satır göstericilerinden oluşan bir dizi yoktur.

int grid[3][4] = {{ 1, 2, 3, 4},
                  { 5, 6, 7, 8},
                  { 9,10,11,12}};

zihninizde canlandirdiginiz         bellekte gercekte durdugu
+----+----+----+----+
|  1 |  2 |  3 |  4 |   satir 0  +--+--+--+--+--+--+--+--+--+--+--+--+
+----+----+----+----+            | 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|
|  5 |  6 |  7 |  8 |   satir 1  +--+--+--+--+--+--+--+--+--+--+--+--+
+----+----+----+----+             \_ satir 0 _/\_ satir 1 _/\_ satir 2 _/
|  9 | 10 | 11 | 12 |   satir 2
+----+----+----+----+            grid[i][j], i*4 + j eleman indeksindedir

O formül, i * sutun + j, mekanizmanın tamamıdır - ve derleyicinin bir şeyi indekslemek için neden sütun sayısını bilmesi gerektiğinin sebebidir. Satır sayısı hesaba hiç girmez.

Yerleşimi adresleri yazdırarak doğrudan görebilirsiniz:

Adresler, bir satırın bitip diğerinin başladığı yer dahil, boşluksuz sizeof(int) kadar tırmanır. Düzleştirilmiş döngü bunu kanıtlar - flat[k] on iki elemanın hepsini tek bir dizi olarak dolaşır.

Bu yerleşimin bilmeye değer bir başarım sonucu da vardır: önce satır sonra sütun döngüsü belleğe sırayla dokunur ki CPU önbelleği bunu sever. Döngü iç içeliğini, iç döngü bir sütundan aşağı inecek şekilde değiştirmek her seferinde bütün bir satır atlar ve büyük bir dizide birkaç kat yavaş çalışabilir.

İç İçe Döngüler

İki boyut iki for döngüsü ister: dıştaki satırı seçer, içteki o satırın sütunlarını tarar.

Sayaçları ne anlama geldiklerine göre adlandırın (satırlar için i/row, sütunlar için j/col) ve sırayı tutarlı tutun - her yerde grid[row][col]. Tüm 2B dizi hatalarının yarısı, yer değiştirmiş bir indeks çiftidir.

#define'lanmış boyutlar da süs değildir: biçimi değiştirdiğinizde artık döngü sınırları ile bildirim birbirinden kopamaz.

Bir Fonksiyona 2B Dizi Geçirmek

İşte herkesi tökezleten kural: fonksiyonun parametresi sütun sayısını bildirmelidir.

Sebep dönüşümdür. grid'i geçirmek onu ilk elemanına bir göstericiye dönüştürür - ve elemanları satırlardır, dolayısıyla tip int (*)[4]'tür: 4 int'lik bir diziye gösterici. grid[i][j]'nin bir anlam ifade etmesi için derleyicinin bir satırın ne kadar uzun olduğunu bilmesi gerekir ve o 4'tür. Satır sayısı tipte gerçekten yoktur; ayrı bir argüman olarak yolculuk etmesinin sebebi budur.

int (*grid)[COLS] ile int grid[][COLS]'un iki farklı şekilde yazılmış aynı parametre olduğuna dikkat edin - parantezler zorunludur, çünkü int *grid[COLS] bunun yerine bir gösterici dizisi olurdu. Bu ayrım göstericiler ve diziler sayfasında ele alınıyor.

Sütun sayısı yalnızca çalışma zamanında biliniyorsa, C99'un değişken değiştirilmiş parametreleri onu önce geçirmenizi sağlar:

void print_any(int rows, int cols, int grid[rows][cols]);

rows ve cols, onları kullanan dizi parametresinden önce bildirilmelidir. Bunun mevcut olmadığı yerlerde yaygın alternatif, düz bir 1B dizi artı elle indeks aritmetiğidir:

data[i * cols + j], sabit boyutlu durumda derleyicinin sizin için yazdığı şeyin tam olarak kendisidir. Bunu elle yapmak bir satıra mal olur ve çalışma zamanında kararlaştırılan her biçim için çalışır.

Bir Matris Örneği

Matris çarpımı tüm sayfayı bir araya getirir - satır-öncelikli depolama üzerinde üç iç içe döngü.

Kopyalamaya değer iki ayrıntı. İçteki k döngüsü a[i][k] ile b[k][j]'yi eşler - bir indeks bir satırı, diğeri bir sütunu dolaşır. Ve devrik, iç döngüsüne j = i + 1'den başlar: 0'dan başlamak her çifti iki kez takas eder ve matrisi değişmemiş bırakır.

Üç Boyut ve Ötesi

Kalıp genişler, fonksiyon parametreleriyle ilgili kural da öyle - ilki dışındaki her boyut bildirilmelidir.

Pratikte üç boyut, sabit boyutlu dizilerin hantallaşmaya başladığı yerdir ve kodun çoğu, hesaplanmış indeksli düz bir bloğa ya da her eksenin ne anlama geldiğini adlandıran bir yapı dizisine geçer.

Sık Yapılan Hatalar

  • grid[i, j] yazmak. Virgül operatörü i'yi değerlendirir, atar ve j ile indeksler. Derlenir. Yanlıştır. grid[i][j] kullanın.
  • İndeksleri yer değiştirmek. grid[col][row] yanlış yerden gerçek bir eleman okur, dolayısıyla bunu yakalayacak bir hata yoktur. Her yerde [row][col] sırasını koruyun.
  • Bir parametrede sütun boyutunu atlamak. void f(int grid[][]) derlenmez ve bu, derleyicinin sizi kurtarmasıdır.
  • Sınır dışına çıkmak. Her dizide olduğu gibi sınır denetimi yoktur. [3][4] bir ızgarada grid[0][5], sessizce grid[1][1]'i okur, çünkü yerleşim bitişiktir ve aritmetik umursamaz.

Sıkça Sorulan Sorular

C'de bir 2B dizi nasıl tanımlanır?

Köşeli parantez içinde iki boyut verin: int grid[3][4];, 4 sütunluk 3 satır - toplam 12 int - tanımlar. Bunu "her biri 4 int'lik bir dizi olan 3 şeyden oluşan bir dizi" diye okuyun; C onu tam olarak böyle saklar.

C'de bir 2B dizi bellekte nasıl saklanır?

Satır-öncelikli sırada: önce 0. satırın tüm elemanları, sonra 1. satırınkiler vb., tek bir bitişik blokta. grid[i][j], başlangıçtan i * sutun + j eleman kadar ötededir; derleyicinin ihtiyaç duyduğu sayının sütun sayısı olmasının sebebi budur.

C'de bir fonksiyona 2B dizi nasıl geçirilir?

Parametre sütun sayısını bildirmelidir: void print(int grid[][4], int rows) ya da eşdeğer olarak void print(int (*grid)[4], int rows). Satır sayısı atlanabilir, çünkü dizi bir satıra göstericiye dönüşür - ama sütun boyutu olmadan derleyici bir satırın nerede başladığını hesaplayamaz.

Bir 2B diziye tümü sıfır olacak şekilde ilk değer verilebilir mi?

Evet: int grid[3][4] = {0}; her elemanı sıfırlar, çünkü listelemediğiniz her elemana sıfır atanır. int grid[3][4] = {{1, 2}};, 0. satırın ilk iki girdisini ayarlar ve diğer onunu sıfırda bırakır.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA