strcpy引发的疑问(二)

1.库函数strcpy的工作方式
char * strcpy( char *strDest, const char *strSrc )
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != ‘/0’ );
  return address;
}

2.找错题

试题1:

void test1()
{
 char string[10];
 char* str1 = "0123456789";
 strcpy( string, str1 );
}
试题1字符串str1需要11个字节才能存放下(包括末尾的’/0’),而string只有10个字节的空间,strcpy会导致数组越界;

试题2:

void test2()
{
 char string[10], str1[10];
 int i;
 for(i=0; i<10; i++)
 {
  str1[i] = 'a';
 }
 strcpy( string, str1 );
}

对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分(结尾必须是/0);如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分.

void test3(char* str1)
{
 char string[10];
 if( strlen( str1 ) <= 10 )
 {
  strcpy( string, str1 );
 }
}
对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’/0’所占用的1个字节。

对strlen的掌握,它没有包括字符串末尾的'/0'。

读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为: int strlen( const char *str ) //输入参数const

{
 assert( strt != NULL ); //断言字符串地址非0
 int len;
 while( (*str++) != '/0' )
 {
  len++;
 }
 return len;
}

试题4:

void GetMemory( char *p )
{
 p = (char *) malloc( 100 );
}

void Test( void )
{
 char *str = NULL;
 GetMemory( str );
 strcpy( str, "hello world" );
 printf( str );
}

试题4,str=NILL赋值给*p,则*p也是NULL,所以两个指针都还没有分配地址,形参和实参并没有结合!函数执行结束后的str仍然为NULL;经典的一题!
试题5:

char *GetMemory( void )
{
 char p[] = "hello world";
 return p;
}

void Test( void )
{
 char *str = NULL;
 str = GetMemory();
 printf( str );
}

char p[] = "hello world";
return p;

p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。

试题6:

void GetMemory( char **p, int num )
{
 *p = (char *) malloc( num );
}

void Test( void )
{
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, "hello" );
 printf( str );
}

试题6的GetMemory避免了试题4的问题,&str是指针的地址,str为指针指向的地位,要弄清楚这两个。这样就能达到形参和实参的结合了。

*p = (char *) malloc( num );

后未判断内存是否申请成功,应加上:

if ( *p == NULL )
{
 ...//进行申请内存失败处理
}

试题7:

void Test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str );
 ... //省略的其它语句
}
试题7存在与试题6同样的问题,在执行

char *str = (char *) malloc(100);

后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:

str = NULL;

(提示: //delete一个指针的时候 一定要检查是否为null,否则是个严重的应用程序错误.
              //由于m_pName是用new 产生的空间 所以需要 delete来释放掉, new的东西都不会自己消除,需 要我们在适合的使用调用delete
             if(m_pName!=NULL) {
             delete []m_pName;    //因为它是一个数组所以需要用delete []
            m_pName=NULL;       //将它设置为null 防止再次使用这个指针去访问原来的地址,原来的地址已经被我们delete了.但编译器不会帮我们自动清它为null 所以我们自己清.

<think>好的,我现在需要回答用户关于C语言memcpy函数的使用规范和定义的问题。首先,我得回想一下memcpy的基本功能,它用于内存块的拷贝,用户可能想知道它的参数、返回值以及注意事项。 根据引用[2],memcpy的函数原型是void *memcpy(void *str1, const void *str2, size_t n),需要包含头文件<string.h>。参数str1是目标地址,str2是源地址,n是拷贝的字节数。返回值是目标地址的指针,这样可以链式调用。 接下来要注意的是内存重叠的问题。引用[5]提到,当源和目标内存有重叠时,memcpy可能会导致未定义行为,这时候应该用memmove。用户可能没有提到这点,但作为规范的一部分,必须指出这一点以确保正确使用。 关于参数校验,引用[3]中的实现代码检查了目标地址和源地址是否为NULL,但根据C标准库的文档,实际memcpy可能不进行这些检查,用户需要自行确保指针有效,否则会导致未定义行为。所以需要提醒用户传递有效指针和正确的size值。 使用示例方面,引用[4]给出了一个例子,将字符串拷贝到字符数组,并在末尾添加了终止符。这说明memcpy不会自动添加终止符,用户需要手动处理,特别是处理字符串时,这点很重要。 另外,引用[5]的代码实现展示了逐字节拷贝的方法,使用char*指针,因为char在C中占1字节,适合处理任意类型的数据。这也说明了memcpy是按字节复制,不考虑数据类型,这也是为什么它可以处理任何类型的内存块。 用户可能需要知道如何正确使用memcpy,比如确保目标缓冲区足够大,避免溢出。同时,性能方面,虽然标准库的实现可能优化过,但在处理大块数据时还是需要注意效率。 最后,生成相关问题时要覆盖常见疑问,比如与strcpy的区别、内存重叠处理、参数校验等,帮助用户深入理解。</think>### C语言memcpy函数使用规范与定义解析 #### 一、官方定义与语法规范 `memcpy`是C标准库中的内存拷贝函数,其**官方原型**为: $$void\ *memcpy(void\ *dest,\ const\ void\ *src,\ size\_t\ n)$$ 需包含头文件`<string.h>`,功能是将从`src`地址开始的`n`个字节复制到`dest`地址中[^2][^5]。 #### 、核心参数说明 1. **`dest`**:目标内存地址(需非空且可写) 2. **`src`**:源内存地址(需非空且可读) 3. **`n`**:复制的字节数(需保证不超过源和目标的可用空间) #### 三、关键使用规范 1. **内存重叠处理** - 当源与目标内存区域**存在重叠**时,应使用`memmove`而非`memcpy`,否则会导致未定义行为[^5]。 - 示例:若`src`在`dest`之后且两区域重叠,`memcpy`可能覆盖未复制的数据。 2. **参数有效性要求** - 函数**不校验指针有效性**,若传入`NULL`指针会引发程序崩溃[^3]。 - 需确保`dest`缓冲区足够容纳`n`字节数据,否则产生缓冲区溢出。 3. **数据类型无关性** - 支持任意类型数据的进制拷贝(如结构体、数组等) - 示例:拷贝整型数组 ```c int src[5] = {1,2,3,4,5}; int dest[5]; memcpy(dest, src, sizeof(src)); // 正确计算字节数 ``` 4. **返回值特性** - 返回目标指针`dest`,支持链式调用: ```c strcpy((char*)memcpy(buffer, data, 100) + 100, "append"); ``` #### 四、典型应用场景 1. 结构体深拷贝 ```c struct Student s1, s2; memcpy(&s2, &s1, sizeof(struct Student)); ``` 2. 网络数据传输前的数据打包 3. 图像处理中的像素块复制 #### 五、与strcpy的对比 | 特性 | memcpy | strcpy | |-------------|------------------|-----------------| | 终止条件 | 按字节数复制 | 遇到'\0'停止 | | 安全性 | 需手动控制长度 | 易导致溢出 | | 数据类型 | 任意进制数据 | 仅字符串 | #### 六、注意事项 1. **字符串拷贝需手动补'\0'** ```c char src[] = "Hello"; char dest[10]; memcpy(dest, src, 5); dest[5] = '\0'; // 必须显式添加终止符[^4] ``` 2. **性能优化** 标准库实现可能使用SIMD指令加速大块内存复制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值