[英]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)
因此,没有内存错误。
一些风格点
sizeof(char)
-它定义为1 dsize
应该是无符号的,并且实际上应该是size_t
,它大于int
我只是幸运吗?
好吧,您很幸运,但并非出于您可能正在思考的原因...
您对结构分配和重新分配的处理是正确的,但不完整。 您应该验证所有内存分配,以确保在分配失败的情况下尝试访问不属于您的内存,从而确保您不会调用未定义行为 。
例如,当您分配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
。 (当然,我知道可以进行快速测试)。 通过将n
, name
和dsize
的值作为函数参数传递,使reassign
很有用。
您不需要进行memcpy (s, &snew, sizeof (stuff));
。 您需要做的就是分配*s = snew;
。 现在您可能在这里有点幸运,因为您将结构的指针成员限制为单个指针。 由于包含的值是每个存储块开始的地址,因此只需要分配即可。 但是,如果使用指向对象的成员的指针(例如,双指针),分配(或memcpy
)将不再足够,并且将需要深拷贝。
注意:除非您需要name
是可修改的(例如,您需要更改单个字符),否则可以使用字符串文字 ,而无需完全动态分配。
更改int n;
的选择可能也是明智的int n;
int i;
作为消除n
和sn
等之间混淆的计数器。
将这些部分放在一起,您可以使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.