在C中复制具有字符串成员的结构
我有一个简单的结构,其中包含一个定义为字符数组的字符串。我认为使用赋值运算符将结构的一个实例复制到另一个实例只需复制存储在char指针中的内存地址。相反,字符串内容似乎被复制了。我举了一个非常简单的例子:在C中复制具有字符串成员的结构,c,arrays,string,struct,C,Arrays,String,Struct,我有一个简单的结构,其中包含一个定义为字符数组的字符串。我认为使用赋值运算符将结构的一个实例复制到另一个实例只需复制存储在char指针中的内存地址。相反,字符串内容似乎被复制了。我举了一个非常简单的例子: #include <stdio.h> #include <string.h> struct Test{ char str[20]; }; int main(){ struct Test t1, t2; strcpy(t1.str, "Hello");
#include <stdio.h>
#include <string.h>
struct Test{
char str[20];
};
int main(){
struct Test t1, t2;
strcpy(t1.str, "Hello");
strcpy(t2.str, "world");
printf("t1: %s %p\n", t1.str, (char*)(t1.str));
printf("t2: %s %p\n", t2.str, (char*)(t2.str));
t2 = t1;
printf("t2: %s %p\n", t2.str, (char*)(t2.str));
return 0;
}
据我所知,在t2=t1
t2.str之后,它指向赋值之前指向的同一内存地址,但现在在该地址中,在t1.str中找到了相同的字符串。所以在我看来,字符串内容已经自动从一个内存位置复制到另一个内存位置,这是我认为C不会做的。我认为触发这种行为的原因是我将str声明为char[]
,而不是char*
。实际上,如果试图用t2.str=t1.str
将一个字符串直接分配给另一个字符串,则会出现以下错误:
Test.c: In function ‘main’:
Test.c:17:10: error: assignment to expression with array type
t2.str = t1.str;
^
这让我觉得数组在某些情况下与指针的处理方式是不同的。但我仍然不知道数组赋值的规则是什么,换句话说,当我将一个结构复制到另一个结构中时,为什么要复制结构中的数组,但我不能直接将一个数组复制到另一个结构中。该结构没有指针,但包含20个字符。
在
t2=t1
之后,t1
的20个字符被复制到t2
中,在C中struct
是编译器知道如何构造内存区域的一种方法。struct
是一种模板或模板,C编译器使用它来计算到结构的各个成员的偏移量
最初的C编译器不允许struct
赋值,因此人们不得不使用memcpy()
函数来赋值结构,而后来的编译器却这样做了。C编译器将通过复制内存中struct
区域的字节数来执行struct
赋值,包括为从一个地址到另一个地址的地址对齐而添加的填充字节。无论源内存区域中发生什么,都会复制到目标区域。这本书一点用也没有。它只是将这么多字节的数据从一个内存位置复制到另一个内存位置
如果在结构中有一个字符串数组或任何类型的数组,那么整个数组都将被复制,因为它是结构的一部分
如果struct
包含指针变量,那么这些指针变量也将从一个区域复制到另一个区域。这样做的结果是,您将有两个具有相同数据的结构。这些结构中的每个指针变量都具有相似的地址值,这两个区域是彼此的副本,因此一个结构中的特定指针将与另一个结构中的相应指针具有相同的地址,并且两者都指向相同的位置
请记住,结构赋值只是将字节数据从一个内存区域复制到另一个内存区域。例如,如果我们有一个简单的struct
,它带有一个char
数组,C源代码如下所示:
typedef struct {
char tt[50];
} tt_struct;
void test (tt_struct *p)
{
tt_struct jj = *p;
tt_struct kk;
kk = jj;
}
汇编程序在VisualStudio 2005 C++编译器中调试输出,用于分配<代码> KK= JJ;<代码>看起来像:
; 10 : tt_struct kk;
; 11 :
; 12 : kk = jj;
00037 b9 0c 00 00 00 mov ecx, 12 ; 0000000cH
0003c 8d 75 c4 lea esi, DWORD PTR _jj$[ebp]
0003f 8d 7d 88 lea edi, DWORD PTR _kk$[ebp]
00042 f3 a5 rep movsd
00044 66 a5 movsw
这段代码将数据从内存中的一个位置逐4字节逐字复制到另一个位置。使用较小的char
数组大小,编译器可以选择使用不同的指令序列来复制内存,因为这样更有效
在C语言中,数组并不是真正以智能的方式处理的。数组不像Java那样被视为数据结构。在Java中,数组是一种由对象数组组成的对象类型。在C语言中,数组只是一个内存区域,数组名实际上被视为常量指针或无法更改的指针。结果是,在C中可以有一个数组sayintmyints[5]
哪个Java会看到一个包含五个int的数组,但是对于C来说,它实际上是一个带有myint
标签的常量指针。在Java中,如果试图访问超出范围的数组元素,比如myInts[i],其中i的值为8,则会出现运行时错误。在C语言中,如果您试图访问超出范围的数组元素,比如myInts[i],其中i的值为8,则不会出现运行时错误,除非您使用一个调试构建,并且使用一个优秀的C编译器执行运行时检查。然而,有经验的C程序员倾向于将数组和指针视为类似的结构,尽管数组作为指针有一些限制,因为它们是常量指针的一种形式,不完全是指针,但具有一些类似于指针的特性
这种缓冲区溢出错误在C中很容易通过访问超过其元素数的数组来实现。经典的例子是将一个字符数组的字符串复制到另一个字符数组中,而源字符数组中没有零终止字符,因此当您期望10个或15个字符时,会产生几百个字节的字符串副本。在您的示例中实际上有20个字符,这与将结构声明为struct Test相同{char c1,char c2,…}
如果只想复制指向字符串的指针,可以如下更改结构声明,并通过函数Test\u init
和Test\u delete
手动管理字符串的内存
struct Test{
char* str;
};
void Test_init(struct Test* test, size_t len) {
test->str = malloc(len);
}
void Test_delete(struct Test* test) {
free(test->str);
}
如果您运行以下简单程序
#include <stdio.h>
int main( void )
{
{
struct Test
{
char str[20];
};
printf( "%zu\n", sizeof( Test ) );
}
{
struct Test
{
char *str;
};
printf( "%zu\n", sizeof( Test ) );
}
return 0;
}
因此,第一个结构包含一个由20个元素组成的字符数组,而第二个结构只包含一个char*
类型的指针
当一个结构分配给另一个结构时,会复制其数据成员。因此,对于第一个结构,数组的所有内容都会复制到另一个结构中。对于第二个结构,只复制指针的值(它包含的地址)。指针指向的内存不会被复制,因为它不是
#include <stdio.h>
int main( void )
{
{
struct Test
{
char str[20];
};
printf( "%zu\n", sizeof( Test ) );
}
{
struct Test
{
char *str;
};
printf( "%zu\n", sizeof( Test ) );
}
return 0;
}
20
4