簡體   English   中英

在結構中重新分配元素(尤其是指針)

[英]Reassigning elements (pointers in particular) in a struct

我想要一個結構,對其進行“刪除”,然后重新分配其元素的值。 我編寫了以下示例代碼,它似乎可以工作。 但是我想知道是否有人認為這有什么問題。 我主要擔心“名稱”和“ ip”指針。 他們出來看起來不錯,但我是否很幸運?

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


typedef struct stuff_s{

   int n;
   char *name;
   int dsize;
   int *ip;

} stuff;


void output_stuff(stuff *s){

   int n;

   printf("%d\n", s->n);
   printf("%s\n", s->name);
   printf("%d\n", s->dsize);

   for (n=0; n<s->dsize; n++) {
      printf("%d ",s->ip[n]);
   }

   printf("\n");

}


void reassign(stuff *s) {

   stuff snew;
   int n;

   snew.n = 2;
   snew.name = calloc(32, sizeof(char));
   strcpy(snew.name, "Snoopy");
   snew.dsize = 12;
   snew.ip = calloc(snew.dsize, sizeof(int));
   for (n=0; n<snew.dsize; n++) { snew.ip[n] = n; }

   free(s->name);
   free(s->ip);

   memcpy(s, &snew, sizeof(stuff));

}


int main() {

   stuff d;
   int n;

   d.n = 1;
   d.dsize = 10;
   d.ip = calloc(d.dsize, sizeof(int));
   for (n=0; n<d.dsize; n++) {
      d.ip[n] = n;
   } 
   d.name = calloc(32, sizeof(char));
   strcpy(d.name,"Charlie Brown");

   output_stuff(&d);
   printf("\n");

   reassign(&d);

   output_stuff(&d);

   free(d.ip);
   free(d.name);

}

好吧,這是valgrind所說的:

1
--2097-- REDIR: 0x4ebf9c0 (strlen) redirected to 0x4c2e0e0 (strlen)
Charlie Brown
10
0 1 2 3 4 5 6 7 8 9

--2097-- REDIR: 0x4eb9d10 (free) redirected to 0x4c2bd80 (free)
--2097-- REDIR: 0x4ec8630 (memcpy@@GLIBC_2.14) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--2097-- REDIR: 0x4ed1620 (__memcpy_sse2_unaligned) redirected to 0x4c2f6b0 (memcpy@@GLIBC_2.14)
2
Snoopy
12
0 1 2 3 4 5 6 7 8 9 10 11
==2097==
==2097== HEAP SUMMARY:
==2097==     in use at exit: 0 bytes in 0 blocks
==2097==   total heap usage: 4 allocs, 4 frees, 152 bytes allocated
==2097==
==2097== All heap blocks were freed -- no leaks are possible
==2097==
==2097== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==2097== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

因此,沒有內存錯誤。

一些風格點

  1. 您不需要使用sizeof(char) -它定義為1
  2. dsize應該是無符號的,並且實際上應該是size_t ,它大於int
  3. 正如SoronellHaetir指出的那樣,您不需要使用其他結構-您可以修改傳入的結構

我只是幸運嗎?

好吧,您很幸運,但並非出於您可能正在思考的原因...

您對結構分配和重新分配的處理是正確的,但不完整。 您應該驗證所有內存分配,以確保在分配失敗的情況下嘗試訪問不屬於您的內存,從而確保您不會調用未定義行為

例如,當您分配d.ip - 檢查返回值以確保calloc成功,例如

    if (!(d.ip = calloc (d.dsize, sizeof *d.ip))) {
        perror ("calloc d.ip failed");
        exit (EXIT_FAILURE);
    }

您應該為每個分配執行此操作。

輸入void進行reassign是不夠的。 它沒有提供有意義的方法來確定函數中的分配是否成功。 每當您分配內存,打開文件,輸入內容等時,請確保使用可以提供有意義的返回值來指示成功失敗的類型聲明函數。 返回有效地址或NULL指針可以工作,返回值可以指示成功/失敗的整數類型也可以。

不要在代碼中使用幻數。 例如,如果您需要一個常量作為name成員的大小,則#define一個或使用enum 例如,如果您希望name的大小為32個字節,則定義一個name大小常量,例如

#define NMSZ 32

reassign功能繼續與幻數魔法字符串進行分配,以.n.dsize.dname (當然,我知道可以進行快速測試)。 通過將nnamedsize的值作為函數參數傳遞,使reassign很有用。

您不需要進行memcpy (s, &snew, sizeof (stuff)); 您需要做的就是分配*s = snew; 現在您可能在這里有點幸運,因為您將結構的指針成員限制為單個指針。 由於包含的值是每個存儲塊開始的地址,因此只需要分配即可。 但是,如果使用指向對象的成員的指針(例如,雙指針),分配(或memcpy )將不再足夠,並且將需要深拷貝。

注意:除非您需要name是可修改的(例如,您需要更改單個字符),否則可以使用字符串文字 ,而無需完全動態分配。

更改int n;的選擇可能也是明智的int n; int i; 作為消除nsn等之間混淆的計數器。

將這些部分放在一起,您可以使reassign更加穩健,如下所示:

stuff *reassign (stuff *s, int n, const char *name, int dsize) {

    stuff snew;
    int i;

    snew.n = n; /* validate all memory allocations */
    if (!(snew.name = calloc (NMSZ, sizeof *snew.name))) {
        perror ("calloc snew.name");
        return NULL;
    }
    strcpy (snew.name, name);

    snew.dsize = dsize; /* ditto */
    if (!(snew.ip = calloc (snew.dsize, sizeof *snew.ip))) {
        perror ("calloc snew.ip");
        return NULL;
    }

    for (i = 0; i < snew.dsize; i++)
        snew.ip[i] = i + 5;

    free (s->name);
    free (s->ip);

    *s = snew;  /* you can simply assign snew */

    return s;
}

盡管您可以在聲明之后自由分配結構的每個成員,但從C99開始,使用named-initializers可以在聲明時初始化非動態成員的簡單易懂的方式,例如

    stuff d = { .n = 1, .dsize = 10 };

最后,如注釋中所述,無需調用printf ("\\n"); 輸出單個字符。 那就是putchar ('\\n'); 是為了。 誠然,一個不錯的編譯器會為您進行優化,但這並不是沒有為開始使用正確的工具的借口。

將所有部分放在一起,可以通過添加驗證並利用reassign的有意義的收益來使代碼更健壯,並防止未定義行為 ,如下所示:

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

#define NMSZ 32

typedef struct stuff_s {
    int n,
        dsize,
        *ip;
    char *name;
} stuff;


void output_stuff (stuff *s){

    int n;

    printf ("%d\n%s\n%d\n", s->n, s->name, s->dsize);

    for (n = 0; n < s->dsize; n++)
        printf ("%d ",s->ip[n]);

    putchar ('\n');
}


stuff *reassign (stuff *s, int n, const char *name, int dsize) {

    stuff snew;
    int i;

    snew.n = n; /* validate all memory allocations */
    if (!(snew.name = calloc (NMSZ, sizeof *snew.name))) {
        perror ("calloc snew.name");
        return NULL;
    }
    strcpy (snew.name, name);

    snew.dsize = dsize; /* ditto */
    if (!(snew.ip = calloc (snew.dsize, sizeof *snew.ip))) {
        perror ("calloc snew.ip");
        return NULL;
    }

    for (i = 0; i < snew.dsize; i++)
        snew.ip[i] = i + 5;

    free (s->name);
    free (s->ip);

    *s = snew;  /* you can simply assign snew */

    return s;
}

int main() {

    stuff d = { .n = 1, .dsize = 10 };
    int i;

    if (!(d.ip = calloc (d.dsize, sizeof *d.ip))) {
        perror ("calloc d.ip failed");
        exit (EXIT_FAILURE);
    }
    for (i = 0; i < d.dsize; i++)
        d.ip[i] = i;

    if (!(d.name = calloc (NMSZ, sizeof *d.name))) {
        perror ("calloc d.name failed");
        exit (EXIT_FAILURE);
    }
    strcpy (d.name, "Charlie Brown");

    output_stuff (&d);
    putchar ('\n');

    if (reassign (&d, 2, "Linus", 12)) {

        output_stuff (&d);

        free (d.ip);
        free (d.name);
    }

    return 0;
}

使用/輸出示例

$ ./bin/structcpy
1
Charlie Brown
10
0 1 2 3 4 5 6 7 8 9

2
Linus
12
5 6 7 8 9 10 11 12 13 14 15 16

如果您有其他疑問,請隨時告訴我。

暫無
暫無

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

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