复制功能通常以C语言使用. 本文记录了strcpy,strncpy和memset函数的使用.
功能: 将一个字符串复制到另一个. 将src指向的以null终止的字符串复制到dest指向的内存空间. dest是char *,而src是const char *. 注意,src指向的存储空间只能在函数中读取,不能修改. 该函数的返回值是char *类型,它返回修改后的字符串的地址.
注意: strcpy函数将src指向的字符串(包括终止符(\ 0))复制到dest指向的区域. 因此,可以确保目标以“ \ 0”结尾. strcpy只知道src字符串的第一个地址,它将始终复制到'\ 0'位置. dest指向的区域必须足够大才能完成复制.
strncpy函数类似于,但它将n个字节复制到dest区域. 如果src的长度小于n,则目标区域将填充空白字符.
使用strcpy时需要注意两点: 1)src字符串以'\ 0'结尾2)dest区域足够大,可以复制所有dest,否则将导致缓冲区溢出.
错误示例:
//
// main.cpp
// strcpyExample
//
// Created by mini on 12/24/13.
// Copyright (c) 2013 mini. All rights reserved.
//
#include
#include
using namespace std;
int main(int argc, const char * argv[])
{
char buf1[10];
char buf2[10];
//char *s = "hello world";
strcpy( buf1, "hello world" );
strcpy( buf2, "holly shit!");
cout << &buf1 << endl;
cout << &buf2 << endl;
cout << buf1 << endl;
cout << buf2 << endl;
}
输出:
0x7fff5fbffa5e
0x7fff5fbffa54
!
holly shit!
Program ended with exit code: 0
分析: 函数堆栈向下增长. 从内存的高地址->低地址增长. 即,堆栈顶部的地址低于堆栈底部的地址. 因此,buf1的地址为高(堆栈的底部),而buf2的地址为低(堆栈的顶部). 分配的两个字符数组均为10,并且要复制的内容将超出边界. 复制两个字符串的内容后,如下所示:
因此,使用strcpy时请注意跨境问题. 该函数的实现者无法知道src字符串的长度和目标存储空间的大小. 因此,“确保您不会超出范围”是函数调用者的责任. 调用者提供的dest参数应指向足够大的存储空间. 调用者还有责任“确保不会超出范围”. 调用者提供的src参数所指向的内存必须确保'\ 0'结束.
那时候写出边界可能没有错,但是函数返回时会发生错误,因为写出边界会覆盖存储在堆栈帧中的返回地址,并且当出现以下情况时,该函数会跳转到非法地址该函数返回,因此发生错误. 这称为分段错误. 如果不仅仅是分割错误,那就更严重了. 缓冲区溢出错误通常由恶意用户使用. 函数返回到预先设计的地址,然后执行设计的指令. 如果设计巧妙,则可以启动shell并根据需要执行命令. 如果在具有root特权的可执行程序中存在此类错误,后果将非常严重. 该技术目前也是非常流行的饼干.
技术.
此外: src和dest指向的存储空间不能重叠. 通常,带有指针参数的C标准库函数基本上有此要求,并且每个指针参数指向的内存空间不能相互重叠.
strncpy参数n指定最多将n个字节从src复制到dest,即,如果将其复制到'\ 0',则结束. 如果复制n个字节没有遇到'\ 0',则结束相同. 调用者负责提供适当的n值,例如使n的值等于dest指向的存储空间的大小,例如:
char buf1[10] = "abcdefghi";
strncpy( buf1, "hello world", sizeof( buf1 ) );
此代码可以完全填充buf1的空间,但这不能保证dest以'\ 0'结尾. 因此,读取时发生越界错误. 调用方必须确保dest以'\ 0'结尾. 您可以手动添加这样的句子.
char buf1[10] = "abcdefghi";
strncpy( buf1, "hello world", sizeof( buf1 ) );
buf1[ sizeof(buf1) - 1 ] = '\0';
strncpy也有一个功能. 如果在所有副本完成后src字符串不如n个字节那么好,则缺少多少个字节将完成'\ 0'.
strcpy和strncpy的返回值:
这两个函数都返回一个dest指针,并且dest最初是作为调用者传递的,那么为什么要返回它呢?这样做是为了使函数调用可以用作指针类型的表达式. 例如,printf(“%s \ n”,strcpy(buf,“ hello”)),如果strcpy返回void,则不能直接使用它.
strcpy和strncpy摘要:
使用strcpy时,最好确保您不会写越界或读越界. 当strncpy时strncpy函数,您可以手动添加结束符号. 例如:
strncpy(buf,str,n);
if( n > 0)
buf[n - 1] = '\0';
功能原型:
void * memset ( void * ptr, int value, size_t num );
功能:
memset函数用值填充ptr指向的内存地址的前num个字节. 通常用于清除内存区域. 例如,您将一个int数组定义为一个[10]. 如果a是全局数组或静态变量,则会自动生成为0. 如果它是局部变量,则可以使用memset(a,0,10)清除为0,适合在循环函数中进行初始化或更新.
示例:
int a[10];
for( int s: a )
cout << s << " ";
memset( a, 0, sizeof( a ) );
cout << endl;
for( int s: a )
cout << s << " ";
cout << endl;
输出:
0 1 1606417120 32767 0 1 0 0 1606417136 32767
0 0 0 0 0 0 0 0 0 0
memset可以轻松清除结构类型变量或数组.
示例:
如果我们定义一个结构数组:
struct student {
char name[20];
int age;
int number;
};
通常,有一个结构变量Student. 如何清除s的值?您可以使用以下方法:
s.name[0] = '\0';
s.age = 0;
s.number = 0;
此方法实际上效率低下,需要大量代码. 我们可以使用memset来执行:
struct student s ;
strcpy( s.name, "hehe" );
s.age = 10;
s.number = 10;
cout << s.name << " " << s.age << " " << s.number << endl;
memset( &s, 0, sizeof( s ) );
cout << s.name << " " << s.age << " " << s.number << endl;
输出:
hehe 10 10
0 0
如果它是结构数组,则可以这样使用它:
student s[10];
memset( s, 0, sizeof( student ) * 10 );
功能原型:
void *memcpy(void *dest, const void *src, size_t n); void *memmove(void *dest, const void *src, size_t n);
功能:
memcpy函数将src指向的内存地址中的n个字节复制到dest指向的内存地址中.
memmove还将n个字节从src指向的内存地址复制到dest指向的内存地址. 尽管它称为移动,但实际上是副本. 它与memcpy有一点不同. 如果memcpy的两个参数src和dest指向的存储区重叠,则不能保证正确的复制,但是memmove可以正确复制. 如果定义数组char data [20] =“ hello world \ n”strncpy函数,则要将字符串后移一个字节到“ hhello
world \ n“,您可以使用memmove(buf + 1,buf,13)实现此功能,但是memcpy(buf + 1,buf,13)无法完成复制:
这两个函数以及strcpy和strncpy之间的区别:
strcpy函数遇到“ \ 0”时结束. memcpy遇到“ \ 0”时不会结束. 相反,它将肯定复制n个字节. 复制函数的命名规则是,以str开头的函数将输出以'\ 0'结尾的字符串,而以mem开头的函数不关心字符串'\ 0'. 以mem开头的函数不会将参数视为字符串,因此参数的指针类型为void *而不是char *
错误示例:
int main(int argc, const char * argv[])
{
char data[20] = "Dont give up!";
memcpy( data + 1, data, 13 );
char data2[20] = "Dont give up!";
memmove( data2 + 1, data2, 13 );
cout << data << endl;
cout << data2 << endl;
}
输出:
DDont givv upp
DDont give up!
第一个函数将出现乱码,第二个函数将正常执行. 这是两者之间的区别.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-145494-1.html