strcpy-strncpy-strlcpy

本文探讨了C语言中的strncpy函数行为,包括其是否支持重叠区域拷贝,是否会导致越界错误,以及如何处理src长度小于目标长度的情况。同时介绍了strlcpy的安全特性,如自动添加结束符和返回值的含义。最后,对比了两者在字符串处理上的优劣及应用场景。
摘要由CSDN通过智能技术生成

char strcpy(char dest, const char *src);
头文件:#include <string.h> 和 #include <stdio.h>
功能:把从src地址开始且含有’\0’结束符的字符串复制到以dest开始的地址空间
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
标签:危险

char *strncpy(char *destinin, char *source, int maxlen);
strncpy : 拷贝源地址固定个数的字符给目的地址
标签:较危险
这里为了更好理解,将字符串的结束符’\0’也当做一个普通字符。

示例:

#include <stdio.h>
#include <string.h>

char src[5];
char dst[5];

void dump(char *c, int n) {
  int i = 0;
  for(;i<n;i++) {
    printf("c[%d]=%c ", i, c[i]);
  }
  printf("\n");
}

void init(char *c, int n, char d) {
  int i = 0;
  for(;i<n;i++) {
    c[i] = d;
  }
}

int main(){
  int len = sizeof(src);
  printf("len = %d\n", len);
  init(src, len, 's');
  init(dst, len, 't');
  dump(src, len);
  dump(dst, len);
  printf("\n");
  strncpy(dst, src, 3);
  dump(dst,5);
  init(dst, len , 't');
    
  strncpy(dst, src, 4);
  dump(dst,5);
  init(dst, len , 't');

  strncpy(dst, src, 5);
  dump(dst,5);
  init(dst, len , 't');
  return 0;
}
#./main 
len = 5
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=s 
c[0]=t c[1]=t c[2]=t c[3]=t c[4]=t

c[0]=s c[1]=s c[2]=s c[3]=t c[4]=t 
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=t 
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=s 

两种做法:
(1) dst先memset为’\0’,拷贝src的strlen(src)个字符给dst,这样dst就是一个完整字符串
(2)直接拷贝src的strlen(src)+1个字符给dst,这样dst也是一个完整字符串。

问题1:
strncpy支不支持重叠区域的拷贝?
支持

#include <stdio.h>
#include <string.h>

char src[5];

void dump(char *c, int n) {
  int i = 0;
  for(;i<n;i++) {
    printf("c[%d]=%c ", i, c[i]);
  }
  printf("\n");
}

int main(){
  int len = sizeof(src);
  printf("len = %d\n", len);
  src[0] = src[1] = 's';
  src[2] = src[3] = 't';
  src[4] = 'k';
  strncpy(src+2, src, 2);
  dump(src,5);
    
  return 0;
}
#./main1 
len = 5
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=k 

问题2:
strncpy越界会报错不?
不会报错

#include <stdio.h>
#include <string.h>

char src[5];
char dst[4];

void dump(char *c, int n) {
  int i = 0;
  for(;i<n;i++) {
    printf("c[%d]=%c ", i, c[i]);
  }
  printf("\n");
}

void init(char *c, int n, char d) {
  int i = 0;
  for(;i<n;i++) {
    c[i] = d;
  }
}

int main(){
  int len = sizeof(src);
  printf("len = %d\n", len);
  init(src, len, 's');
  init(dst, len-1, 't');
  dump(src, len);
  dump(dst, len-1);
  strncpy(dst, src, len);
  dump(dst,len);
  return 0;
}
#./main2 
len = 5
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=s 
c[0]=t c[1]=t c[2]=t c[3]=t 
c[0]=s c[1]=s c[2]=s c[3]=s c[4]=s 

问题3:
如果src的长度比拷贝的n值小很多,那该怎么拷贝呢?
默认填充’\0’

#include <stdio.h>
#include <string.h>

char src[2];
char dst[4];

void dump(char *c, int n) {
  int i = 0;
  for(;i<n;i++) {
    printf("c[%d]=%c ", i, c[i]);
  }
  printf("\n");
}

void init(char *c, int n, char d) {
  int i = 0;
  for(;i<n;i++) {
    c[i] = d;
  }
}

int main(){
  int len = sizeof(dst);
  printf("len = %d\n", len);
  src[0] = 's';
  src[1] = '\0';
  init(dst, len, 't');
  dump(src, sizeof(src));
  dump(dst, len);
  strncpy(dst, src, len);
  dump(dst,len);
  return 0;
}
#./main3 
len = 4
c[0]=s c[1]= 
c[0]=t c[1]=t c[2]=t c[3]=t 
c[0]=s c[1]= c[2]= c[3]= 

strlcpy
size_t strlcpy(char *dst, const char *src, size_t dsize)
将src按字符拷贝到dst中,最多拷贝dszie-1个字符,拷贝结束后在dst末尾添加’\0’
返回值:src的长度
一般将dsize置为dst的大小。

相较于strncpy,strlcpy有两个优点:
(1)当strlen(src)大于等于dsize时自动在dst末尾添加结束符;
(2)返回值大于等于dsize时确定发生字符串截断。
以上两点帮助程序员进行判断,方便后续处理。

#include <string.h>
#include <stdio.h>
int main()
{
    char buf[5];
    char src[10] = "12345678";
    strlcpy(buf, src, sizeof(buf));
    printf("%s\n",buf);
    return 0;
}

上述示例中buf字符串最终结果是“1234\0”。
也就是拷贝完后不管有没有截断,都要进行\0的拷贝。

缺点:
strlcpy和strlcat是BSD的C库函数,glibc维护者一直拒绝将其加入,所以需要安装额外的包。
apt-get install libbsd-dev

strlcpy安全吗?
留后续验证。睡觉

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值