2011-08-28 12 views
9

PHP'ye alışkınım ama C öğrenmeye başlıyorum. Dosya satırını okuyan ve her satırı bir diziye kaydeden bir program oluşturmaya çalışıyorum.C'de dinamik boyutlu bir dizi nasıl oluşturulur?

Şimdiye kadar, dosya satırını okuyan ve hatta her satırı gittikçe yazdırdığı bir programa sahibim, ancak şimdi her satırı bir diziye eklemem gerekiyor.

Dün gece arkadaşım bana biraz bahsetti. C de çok boyutlu bir dizi kullanmak zorunda kalacağım, yani temelde array[x][y]. [y] parçasının kendisi kolaydır, çünkü her satırın olacağı maksimum bayt miktarını biliyorum. Ancak, dosyanın ne kadar satırında olduğunu bilmiyorum.

Dosya boyunca döngü yapabildiğimi ve her seferinde bir tam sayıyı artırabileceğimi ve kullanabileceğimi düşünüyorum, ancak bunu yapmanın daha basit bir yolu olabileceğini hissediyorum.

Doğru yönde herhangi bir fikir veya ipucu? Herhangi bir yardım için minnettarım.

+0

Sen işlevini 'kullanabilirsiniz sonradan dizinin boyutunu değiştirmek için realloc'. – Jonathon

+0

Bu işleve bakıp nasıl uygulayabileceğimi düşünmeye çalışacağım ve size geri döneceğim, teşekkürler – Rob

cevap

9

dinamik 2B dizi tahsis için:

char **p; 
int i, dim1, dim2; 


/* Allocate the first dimension, which is actually a pointer to pointer to char */ 
p = malloc (sizeof (char *) * dim1); 

/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars 
* within each of these arrays are chars 
*/ 
for (i = 0; i < dim1; i++) 
    { 
    *(p + i) = malloc (sizeof (char) * dim2); 
    /* or p[i] = malloc (sizeof (char) * dim2); */ 
    } 

/* Do work */ 

/* Deallocate the allocated array. Start deallocation from the lowest level. 
* that is in the reverse order of which we did the allocation 
*/ 
for (i = 0; i < dim1; i++) 
{ 
    free (p[i]); 
} 
free (p); 

yukarıdaki yöntemi değiştirin. Eklenecek başka bir satıra ihtiyacınız olduğunda *(p + i) = malloc (sizeof (char) * dim2); yapın ve i'u güncelleyin. Bu durumda, p dizisini ilk kez tahsis ettiğimiz dim1 değişkeniyle belirtilen dosyadaki maksimum satır sayısını tahmin etmelisiniz. Bu, (sizeof (int *) * dim1) baytları yalnızca char p[dim1][dim2]'dan (c99'da) çok daha iyi bir seçenek olarak ayıracaktır.

Sanırım başka bir yolu var. Dizileri bloklara ayırın ve taşma olduğunda bunları zincirleyin.

struct _lines { 
    char **line; 
    int n; 
    struct _lines *next; 
} *file; 

file = malloc (sizeof (struct _lines)); 
file->line = malloc (sizeof (char *) * LINE_MAX); 
file->n = 0; 
head = file; 

Bundan sonra ilk blok kullanıma hazırdır. Bir çizgi eklemek gerektiğinde sadece yapın:

n LINE_MAX başka bir blok tahsis ve buna bağlamak edilir
/* get line into buffer */ 
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1)); 
n++; 

.

struct _lines *temp; 

temp = malloc (sizeof (struct _lines)); 
temp->line = malloc (sizeof (char *) * LINE_MAX); 
temp->n = 0; 
file->next = temp; 
file = file->next; 

Böyle bir şey. bir blok en n0 olduğunda

, bunu ayırması ve bir öncekine geçerli blok işaretçisi file güncelleyin. Tek bağlantılı listeden başlayıp, en baştan hareket edebilir ya da çift linkleri kullanabilirsiniz.

+0

İkinci yaklaşım, GNU'nun “tail (1)” uygulamasının, giriş dosyası arandığında (boru veya stdin gibi) işe yaradığına inanıyorum. Bu durumda giriş dosyasından yalnızca bir geçiş yapabildiği için, dosyayı bellek blob'larının bağlantılı bir listesinde saklar ve dosya sonuna ulaştığında, son 'N' satırlarını yazdırmak için geriye doğru arar. –

+0

@Adam Rosenfield: Bunu bilmiyordum, bilgi için teşekkürler. Bunu uzun zaman önce hafızada uzun bir kelime listesi saklamak için kullandım ve oldukça kullanışlıdır. – phoxis

1

Eğer C kullanıyorsanız, dizinin yeniden boyutlandırmasını kendiniz uygulamanız gerekir. C++ ve SDL bu işlemi sizin için yaptı. Bu, vector olarak adlandırılır. http://www.cplusplus.com/reference/stl/vector/

+0

Maalesef, C'ye bağlı kalmaya çalışıyorum ama teşekkürler. – Rob

5

C.'de standart bir yeniden boyutlandırılabilir dizi türü yok. Bunu kendiniz uygulamanız ya da üçüncü taraf bir kitaplık kullanmanız gerekiyor. Burada basit çıplak kemikleri örnek:

typedef struct int_array 
{ 
    int *array; 
    size_t length; 
    size_t capacity; 
} int_array; 

void int_array_init(int_array *array) 
{ 
    array->array = NULL; 
    array->length = 0; 
    array->capacity = 0; 
} 

void int_array_free(int_array *array) 
{ 
    free(array->array); 
    array->array = NULL; 
    array->length = 0; 
    array->capacity = 0; 
} 

void int_array_push_back(int_array *array, int value) 
{ 
    if(array->length == array->capacity) 
    { 
     // Not enough space, reallocate. Also, watch out for overflow. 
     int new_capacity = array->capacity * 2; 
     if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX/sizeof(int)) 
     { 
      int *new_array = realloc(array->array, new_capacity * sizeof(int)); 
      if(new_array != NULL) 
      { 
       array->array = new_array; 
       array->capacity = new_capacity; 
      } 
      else 
       ; // Handle out-of-memory 
     } 
     else 
      ; // Handle overflow error 
    } 

    // Now that we have space, add the value to the array 
    array->array[array->length] = value; 
    array->length++; 
} 

böyle kullanın: Elbette

int_array a; 
int_array_init(&a); 

int i; 
for(i = 0; i < 10; i++) 
    int_array_push_back(&a, i); 
for(i = 0; i < a.length; i++) 
    printf("a[%d] = %d\n", i, a.array[i]); 

int_array_free(&a); 

, bu sadece int s bir dizi içindir.C'nin şablonları yoksa, bu kodun tümünü her bir farklı dizi için bir makroya koymanız gerekir (veya GNU m4 gibi farklı bir önişlemci kullanın). Ya da, void* işaretçilerinin (tüm dizi öğelerinin malloc 'ed olması gerekir) veya opak bellek lekelerinin kullanılmasını sağlayan genel bir dizi kapsayıcı kullanabilirsiniz; bu, her öğe erişiminde bir döküm gerektirecek ve her öğe için bir set/set için bir memcpy olmalıdır.

Her durumda, hoş değil. İki boyutlu diziler daha da çirkin.

0

Burada bir dizinin yerine, bağlantılı bir liste de kullanabilirsiniz, Kod daha basittir, ancak ayırma daha sık ve parçalanmadan zarar görebilir.

Çok rasgele erişim yapmayı planladığınız sürece (Burada O (n) burada), yineleme normal bir dizi kadar basittir. çok boyutlu dizi bu sorunu çözmek mümkün olmakla birlikte

typedef struct Line Line; 
struct Line{ 
    char text[LINE_MAX]; 
    Line *next; 
}; 

Line *mkline() 
{ 
    Line *l = malloc(sizeof(Line)); 
    if(!l) 
     error(); 
    return l; 
} 

main() 
{ 
    Line *lines = mkline(); 
    Line *lp = lines; 
    while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){ 
     lp->next = mkline(); 
     lp = lp->next; 
    } 
    lp->next = NULL; 
} 
0

, dikdörtgen 2D dizi gerçekten doğal C çözüm olmaz.

Başlangıçta dosyayı bağlantılı bir listeye okur ve sonra doğru boyuttaki bir işaretçi vektörünü ayırır. Her bir karakter array[line][col] olarak görünür, ancak aslında her satır sadece olması gerektiği kadardır. <err.h> dışında C99.

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

typedef struct strnode { 
    char *s; 
    struct strnode *next; 
} strnode; 

strnode *list_head; 
strnode *list_last; 

strnode *read1line(void) { 
    char space[1024]; 
    if(fgets(space, sizeof space, stdin) == NULL) 
    return NULL; 
    strnode *node = malloc(sizeof(strnode)); 
    if(node && (node->s = malloc(strlen(space) + 1))) { 
    strcpy(node->s, space); 
    node->next = NULL; 
    if (list_head == NULL) 
     list_head = node; 
    else 
     list_last->next = node; 
    list_last = node; 
    return node; 
    } 
    err(1, NULL); 
} 

int main(int ac, char **av) { 
    int n; 
    strnode *s; 

    for(n = 0; (s = read1line()) != NULL; ++n) 
    continue; 
    if(n > 0) { 
    int i; 
    strnode *b; 
    char **a = malloc(n * sizeof(char *)); 
    printf("There were %d lines\n", n); 
    for(b = list_head, i = 0; b; b = b->next, ++i) 
     a[i] = b->s; 
    printf("Near the middle is: %s", a[n/2]); 
    } 
    return 0; 
} 
0

dinamik tahsis ve char için işaretçiler dizisi boyutlandırmak için malloc ve realloc fonksiyonlarını kullanabilir ve dizinin her elemanı dize 'muhafazası da tahsis edilir dosyası (okunan bir dizeye işaret edecektir dinamik olarak). Basitlik için, her satırın maksimum uzunluğunun M karakterinden (yeni satırı saymaktan) daha az olduğunu varsayıyoruz, bu nedenle tek tek satırların dinamik yeniden boyutlandırması yapmak zorunda değiliz.

Her genişletme işleminizde dizi boyutunu manuel olarak izlemeniz gerekir. Yaygın bir teknik, sabit bir boyutta uzatmak yerine, genişletme işleminiz boyunca dizi boyutunu ikiye katlamaktır; Bu, potansiyel olarak pahalı olan realloc numaralı çağrıların sayısını en aza indirir. Tabi ki bu iki miktarın kaydını tutmak zorunda kalacaksınız demektir; dizinin toplam boyutu ve şu anda okunan öğelerin sayısı.

Örnek:

#define INITIAL_SIZE ... // some size large enough to cover most cases 

char **loadFile(FILE *stream, size_t *linesRead) 
{ 
    size_t arraySize = 0; 
    char **lines = NULL; 
    char *nextLine = NULL; 

    *linesRead = 0; 

    lines = malloc(INITIAL_SIZE * sizeof *lines); 
    if (!lines) 
    { 
    fprintf(stderr, "Could not allocate array\n"); 
    return NULL; 
    } 

    arraySize = INITIAL_SIZE; 

    /** 
    * Read the next input line from the stream. We're abstracting this 
    * out to keep the code simple. 
    */ 
    while ((nextLine = getNextLine(stream))) 
    { 
    if (arraySize <= *linesRead) 
    { 
     char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp); 
     if (tmp) 
     { 
     lines = tmp; 
     arraySize *= 2; 
     } 
    } 
    lines[(*linesRead)++] = nextLine; 
) 

    return lines; 
}