[英]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.
我主要担心“名称”和“ ip”指针。 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: 好吧,这是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)
So there are no memory errors. 因此,没有内存错误。
Some stylistic points 一些风格点
sizeof(char)
- it's defined to be 1 sizeof(char)
-它定义为1 dsize
should be unsigned, and should really be a size_t
, which is bigger than an int
dsize
应该是无符号的,并且实际上应该是size_t
,它大于int
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 例如,当您分配
d.ip
- 检查返回值以确保calloc
成功,例如
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. 输入
void
进行reassign
是不够的。 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. 返回有效地址或
NULL
指针可以工作,返回值可以指示成功/失败的整数类型也可以。
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
. 例如,如果您需要一个常量作为
name
成员的大小,则#define
一个或使用enum
。 For example if you want the size of name
to be 32
bytes, then define a namesize constant, eg 例如,如果您希望
name
的大小为32
个字节,则定义一个name
大小常量,例如
#define NMSZ 32
Your reassign
function continues with magic numbers and magic strings in making assignments to .n
, .dsize
, and .dname
. 您
reassign
功能继续与幻数和魔法字符串进行分配,以.n
, .dsize
和.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. 通过将
n
, name
和dsize
的值作为函数参数传递,使reassign
很有用。
You don't need to memcpy (s, &snew, sizeof (stuff));
您不需要进行
memcpy (s, &snew, sizeof (stuff));
. 。 All you need to do is assign
*s = snew;
您需要做的就是分配
*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. 但是,如果使用指向对象的成员的指针(例如,双指针),分配(或
memcpy
)将不再足够,并且将需要深拷贝。
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. 注意:除非您需要
name
是可修改的(例如,您需要更改单个字符),否则可以使用字符串文字 ,而无需完全动态分配。
It may also be wise to change your choice of int n;
更改
int n;
的选择可能也是明智的int n;
to int i;
int i;
as the counter to eliminate confusion between n
and sn
, etc.. 作为消除
n
和sn
等之间混淆的计数器。
Putting those pieces together, you could make reassign
a bit more robust as follows: 将这些部分放在一起,您可以使
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;
}
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 尽管您可以在声明之后自由分配结构的每个成员,但从C99开始,使用named-initializers可以在声明时初始化非动态成员的简单易懂的方式,例如
stuff d = { .n = 1, .dsize = 10 };
Finally, as noted in the comments, there is no need to call printf ("\\n");
最后,如注释中所述,无需调用
printf ("\\n");
to output a single character. 输出单个字符。 That is what
putchar ('\\n');
那就是
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: 将所有部分放在一起,可以通过添加验证并利用
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;
}
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. 如果您有其他疑问,请随时告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.