簡體   English   中英

當我嘗試索引除零以外的任何內容時,指向越界索引的結構指針(?)

[英]Pointer to pointer of structs indexing out of bounds(?) when I try to index anything other than zero

我試圖通過用我初始化的新結構填充默認值來編輯結構數組(作為指針)。 這樣做似乎會導致一些非常奇怪的問題。 我正在學習如何使用帶有指針的結構,因此感謝您提供任何幫助。

主函數的片段(玩家只保存 startLoc 而不更改它)

Location** near;
    startLoc = initLocation("Base", 5);
    player = initPlayer(startLoc);
    near = &(startLoc->near);
    *near = initLocation("Zero", 0);
    *(near + 1) = initLocation("Two", 0);
    printf("Start near: %i\n", startLoc->nearCount);

整個位置.c

#include <stdlib.h>

typedef struct Location {
    char isValid;
    char* name;
    struct Location* near;
    int nearCount;
} Location;

Location* initLocation(char* name, int nearCount) {
    Location* l = malloc(sizeof(Location));
    l->name = name;
    l->near = calloc(sizeof(Location) * nearCount, 1);
    l->nearCount = nearCount;
    l->isValid = 1;
    return l;
}

讓我們從關於指針指針到指針的基本討論開始。 指針只是一個變量,它將其他事物的地址作為其值。 當你聲明一個指向某物的指針時,就像你在結構中使用你的namenear成員所做的那樣,你聲明了一個變量,該變量將保存該類型對象存儲在內存中的內存地址(例如,指針將指向該對象的位置被儲存了)

當你聲明一個指向類型的指針(例如Location **near )時,你有一個指針,它保存另一個指針的地址作為它的值。 這在兩個方面很有用。 (1) 它可以允許您將指針的地址作為參數傳遞,以便函數能夠對該地址處的原始指針進行操作,或者 (2) 它可以允許該單個指針指向一組指針在內存中,例如

 pointer
    |      pointers      allocated struct
   near --> +----+     +-------------------+
            | p1 | --> | struct Location 1 |
            +----+     +-------------------+
            | p2 | --> | struct Location 2 |
            +----+     +-------------------+
            | p3 | --> | struct Location 3 |
            +----+     +-------------------+
            | .. |     |        ...        |

    (a pointer-to-pointer to type struct Location)

在第二種情況下,為什么選擇指向指針的指針作為您的類型,而不是僅僅分配該類型的集合? 好問題。 有兩個主要原因,一個是您分配的內容的大小是否不同。 例如:

 char**
    |      pointers      allocated strings
  words --> +----+     +-----+
            | p1 | --> | cat |
            +----+     +-----+--------------------------------------+
            | p2 | --> | Four score and seven years ago our fathers |
            +----+     +-------------+------------------------------+
            | p3 | --> | programming |
            +----+     +-------------------+
            | .. |     |        ...        |

或 (2) 您想要分配的偶數對象集合(例如將上面的char**更改為int** )可以使用二維數組索引(例如array[2][7]

分配指針對象的集合會增加復雜性,因為您負責維護兩個已分配的集合、指針和對象本身。 在釋放已分配的指針塊之前,您必須跟蹤和重新分配指針集合(和對象——如果需要),然后free()對象集合。

這可以大大簡化,如果您只需要一些相同類型的對象,例如N - struct Location 這為這些對象本身提供了單一分配、單一重新分配和單一自由(當然,每個對象也可以依次包含已分配的對象)。 根據你的情況為near這將是類似於:

 pointer
    |
   near --> +-------------------+
            | struct Location 1 |
            +-------------------+
            | struct Location 2 |
            +-------------------+
            | struct Location 3 |
            +-------------------+
            |        ...        |

       (a pointer to type struct Location)

在您的情況下,您正在處理需要嵌套struct Location分配塊。 從這個意義上說,在需要的地方,您只需要N - struct Location ,它們的大小都相同,並且沒有對 2D 數組索引的迫切需求。 從這個角度來看,看看你想要做什么(最好的猜測),簡單地分配struct Location塊而不是處理指向單獨分配的struct Location的指針塊似乎更有意義。

實現一個簡短的例子

雖然使用initLocation()設置單個struct Location沒有任何問題,但您可能會發現,每次調用它時,只需編寫一個addLocation()函數以將新的struct Location添加到您的集合中會更有意義。 如果您在調用者中初始化指向集合NULL指針,您可以簡單地使用realloc()來處理您的初始分配和后續重新分配。

在下面的例子中,我們只是為列表中的每個名稱創建一個新的struct Location並為 3- near對象分配。 您可以自由地將addLocation()與每個對象中的near struct Location ,就像您使用初始集合一樣,但該實現留給您,因為它只是在嵌套的基礎上做同樣的事情。

以一種看起來像您正在嘗試的方式將addLocation()函數放在一起,您可以執行以下操作:

Location *addLocation (Location *l, size_t *nmemb, char *name, int nearCount)
{
    /* realloc using temporary pointer adding 1 Location */
    void *tmp = realloc (l, (*nmemb + 1) * sizeof *l);  /* validate EVERY allocation */
    if (!tmp) {     /* on failure */
        perror ("error: realloc-l");
        return NULL;    /* original data good, that's why you realloc to a tmp */
    }

    /* on successful allocation */
    l = tmp;                                /* assign reallocated block to l */
    l[*nmemb].isValid = 1;                  /* assign remaining values and */
    l[*nmemb].name = name;                  /* allocate for near */
    l[*nmemb].near = calloc(nearCount, sizeof(Location));
    if (!l[*nmemb].near) {
        perror ("calloc-l[*nmemb].near");
        return NULL;
    }
    l[*nmemb].nearCount = nearCount;        /* set nearCount */
    (*nmemb)++;                             /* increment nmemb */

    return l;  /* return pointer to allocated block of Location */
}

然后,您可以循環填充每個類似於以下內容的內容:

    for (size_t i = 0; i < nmemb;)  /* loop adding 1st nmemb names */
        if (!(l = addLocation (l, &i, names[i], nearCount)))
            break;

注意: i正在addLocation中更新,因此循環定義中不需要i++

一個完整的例子可以寫成如下。 我還添加了一個打印功能和一個刪除所有分配內存的功能。 在下面對addLocation的調用中,您將看到使用了names[i%nnames]而不是names[i]並且使用計數器取我列表中的名稱總數只是確保提供列表中的名稱,無論如何大i得到。

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

typedef struct Location {
    char isValid;
    char *name;
    struct Location *near;
    int nearCount;
} Location;

Location *addLocation (Location *l, size_t *nmemb, char *name, int nearCount)
{
    /* realloc using temporary pointer adding 1 Location */
    void *tmp = realloc (l, (*nmemb + 1) * sizeof *l);  /* validate EVERY allocation */
    if (!tmp) {     /* on failure */
        perror ("error: realloc-l");
        return NULL;    /* original data good, that's why you realloc to a tmp */
    }

    /* on successful allocation */
    l = tmp;                                /* assign reallocated block to l */
    l[*nmemb].isValid = 1;                  /* assign remaining values and */
    l[*nmemb].name = name;                  /* allocate for near */
    l[*nmemb].near = calloc(nearCount, sizeof(Location));
    if (!l[*nmemb].near) {
        perror ("calloc-l[*nmemb].near");
        return NULL;
    }
    l[*nmemb].nearCount = nearCount;        /* set nearCount */
    (*nmemb)++;                             /* increment nmemb */

    return l;  /* return pointer to allocated block of Location */
}

void prn_locations (Location *l, size_t nmemb)
{
    for (size_t i = 0; i < nmemb; i++)
        if (l[i].isValid)
            printf ("%-12s    nearCount: %d\n", l[i].name, l[i].nearCount);
}

void del_all (Location *l, size_t nmemb)
{
    for (size_t i = 0; i < nmemb; i++)
        free (l[i].near);   /* free each structs allocated near member */

    free (l);   /* free all struct */
}

int main (int argc, char **argv) {

    char *endptr,   /* use with strtoul conversion, names below */
        *names[] = { "Mary", "Sarah", "Tom", "Jerry", "Clay", "Bruce" };
    size_t  nmemb = argc > 1 ? strtoul (argv[1], &endptr, 0) : 4,
            nnames = sizeof names / sizeof *names;
    int nearCount = 3;      /* set nearCourt */
    Location *l = NULL;     /* pointer to allocated object */

    if (errno || (nmemb == 0 && endptr == argv[1])) {   /* validate converstion */
        fputs ("error: nmemb conversion failed.\n", stderr);
        return 1;
    }

    for (size_t i = 0; i < nmemb;)  /* loop adding 1st nmemb names */
        if (!(l = addLocation (l, &i, names[i%nnames], nearCount)))
            break;

    prn_locations (l, nmemb);
    del_all (l, nmemb);
}

示例使用/輸出

$ ./bin/locationalloc
Mary            nearCount: 3
Sarah           nearCount: 3
Tom             nearCount: 3
Jerry           nearCount: 3

或者,例如,如果您想為其中的10分配,則:

$ ./bin/locationalloc 10
Mary            nearCount: 3
Sarah           nearCount: 3
Tom             nearCount: 3
Jerry           nearCount: 3
Clay            nearCount: 3
Bruce           nearCount: 3
Mary            nearCount: 3
Sarah           nearCount: 3
Tom             nearCount: 3
Jerry           nearCount: 3

內存使用/錯誤檢查

在你寫的,可動態分配內存的任何代碼,您有任何關於分配的內存任何塊2個職責:(1)始終保持一個指針的起始地址的存儲器中,以便塊,(2),當它是沒有它可以被釋放不再需要。

您必須使用內存錯誤檢查程序來確保您不會嘗試訪問內存或寫入超出/超出分配塊的范圍,嘗試讀取或基於未初始化值的條件跳轉,最后確認你釋放了你分配的所有內存。

對於 Linux valgrind是正常的選擇。 每個平台都有類似的內存檢查器。 它們都易於使用,只需通過它運行您的程序即可。

$ valgrind ./bin/locationalloc
==13644== Memcheck, a memory error detector
==13644== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13644== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13644== Command: ./bin/locationalloc
==13644==
Mary            nearCount: 3
Sarah           nearCount: 3
Tom             nearCount: 3
Jerry           nearCount: 3
==13644==
==13644== HEAP SUMMARY:
==13644==     in use at exit: 0 bytes in 0 blocks
==13644==   total heap usage: 9 allocs, 9 frees, 1,728 bytes allocated
==13644==
==13644== All heap blocks were freed -- no leaks are possible
==13644==
==13644== For counts of detected and suppressed errors, rerun with: -v
==13644== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始終確認您已釋放所有分配的內存並且沒有內存錯誤。

讓我知道這是否符合您的意圖以及您是否有任何其他問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM