我想采取一个结构,"垃圾"它,并重新分配其元素的值。我编写了以下示例代码,它似乎工作。但我想知道是否有人认为它有任何问题。我主要担心的是" name"和" 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);
}
答案 0 :(得分:2)
嗯,这是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
答案 1 :(得分:1)
我真的很幸运吗?
嗯,你很幸运,但不是因为你可能在想的原因......
您对stuct分配和重新分配的处理是正确的,但不完整。您应该 验证 所有内存分配,以确保您不会通过尝试访问不属于您的内存来调用未定义行为分配失败。
例如,当您分配d.ip
- 检查返回以确保calloc
成功时,例如
if (!(d.ip = calloc (d.dsize, sizeof *d.ip))) {
perror ("calloc d.ip failed");
exit (EXIT_FAILURE);
}
你应该为每次分配都这样做。
void
的{{1}}类型不足。它没有提供有意义的方法来确定函数中的分配是否成功。每次分配内存,打开文件,接受输入等时,请确保使用能够提供有意义的返回的类型声明您的函数,以指示成功或失败。返回有效地址或reassign
的指针,以及返回可指示成功/失败的值的整数类型。
不要在代码中使用魔术数字。如果您需要常量来表示NULL
成员的大小,那么name
一个或使用#define
。例如,如果您希望enum
的大小为name
字节,则定义一个名称大小常量,例如
32
#define NMSZ 32
功能继续使用魔术数字和魔术字符串进行reassign
,.n
和{{ 1}}。 (我知道这很可能是为了快速测试)。通过将.dsize
,.dname
和reassign
的值作为函数参数传递,使n
变得有用。
您不需要name
。您需要做的就是分配dsize
。现在你可能有点幸运,因为你将结构的指针成员限制为单指针。由于包含的值是每个内存块的起始地址,因此只需要一个赋值。但是,如果使用指向的指针的指针作为成员(例如双指针),则赋值(或memcpy (s, &snew, sizeof (stuff));
)不再足够,并且深层副本将是必需的。
注意:除非您需要*s = snew;
可修改(例如,您需要更改单个字符),否则可以使用字符串文字并消除需要动态分配。
将memcpy
选择更改为name
作为计数器以消除int n;
和int i;
等之间的混淆也可能是明智之举。
将这些部分放在一起,您可以使n
更加健壮,如下所示:
s.n
虽然您可以在声明之后自由分配结构的每个成员,但从C99开始,使用named-initializers可以在声明时初始化非动态成员,例如。
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;
}
输出单个字符。这就是 stuff d = { .n = 1, .dsize = 10 };
的用途。当然,一个不错的编译器会为您进行优化,但这不是开始使用正确工具的借口。
将所有部分组合在一起,您可以通过添加验证并利用printf ("\n");
的有意义回报来使您的代码更加健壮并防止未定义行为,如下所示:< / p>
putchar ('\n');
示例使用/输出
reassign
如果您有任何其他问题,请告诉我们。