简体   繁体   中英

Reassigning elements (pointers in particular) in a struct

I want to take a struct, "trash" it, and reassign the values of its elements. I wrote the following example code, and it seems to work. But I am wondering if anyone sees anything wrong with it. I am mainly worried about the "name" and "ip" pointers. They came out looking right, but did I just get lucky?

#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);

}

Well, here's what valgrind says:

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)

So there are no memory errors.

Some stylistic points

  1. You don't need to use sizeof(char) - it's defined to be 1
  2. dsize should be unsigned, and should really be a size_t , which is bigger than an int
  3. As SoronellHaetir points out, you don't need to use another struct - you can modify the passed-in one

did I just get lucky?

Well, you got lucky, but not for the reasons you may be thinking...

Your handling of the stuct allocation and reassignment was correct, but incomplete. You should validate all memory allocations to insure you do not invoke Undefined Behavior by attempting to access memory that does not belong to you in the event of an allocation failure.

For example, when you allocate d.ip -- check the return to insure calloc succeeded, eg

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

You should do that for every allocation.

Type void for reassign is insufficient. It provides no meaningful way to determine whether the allocations in the function succeeded or not. Any time you are allocating memory, opening a file, taking input, etc.. make sure you declare your function with a type that can provide a meaningful return to indicate success or failure . A pointer returning a valid address or NULL works, as does an integer type returning a value that can indicate success/failure.

Don't use magic numbers in your code. If you need a constant for instance for the size of the name member, then #define one or use an enum . For example if you want the size of name to be 32 bytes, then define a namesize constant, eg

#define NMSZ 32

Your reassign function continues with magic numbers and magic strings in making assignments to .n , .dsize , and .dname . (granted, I understand that is was likely for a quick test). Make reassign useful by passing the values of n , name and dsize as function parameters.

You don't need to memcpy (s, &snew, sizeof (stuff)); . All you need to do is assign *s = snew; . Now you may be a bit lucky here since you limited the pointer members of your struct to single pointers. Since the value contained is the address of the start of each memory block, an assignment is all that is required. However, if you use a pointer to pointer to type as a member (eg a double-pointer) an assignment (or a memcpy ) is no longer sufficient, and a deep copy will be required.

note: unless you need name to be modifiable (eg you need to change individual characters), then a string literal can be used and eliminate the need to dynamically allocate altogether.

It may also be wise to change your choice of int n; to int i; as the counter to eliminate confusion between n and sn , etc..

Putting those pieces together, you could make reassign a bit more robust as follows:

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;
}

While you are free to assign each member of your struct after declaration, beginning with C99, use of named-initializers make a simple and readable way to initialize non-dynamic members at the time of declaration, eg

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

Finally, as noted in the comments, there is no need to call printf ("\\n"); to output a single character. That is what putchar ('\\n'); is for. Granted, a decent compiler will make the optimization for you, but that isn't an excuse for not using the correct tool for the job to begin with.

Putting all the pieces together, you could make your code a bit more robust and protect against Undefined Behavior by adding validations and making use of the meaningful return from reassign as follows:

#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;
}

Example Use/Output

$ ./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

Look thins over an let me know if you have any further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM