前言

C标准库源码可通过下列两个网站进行查看:The GNU C LibraryWelcome to uClibc-ng! - Embedded C library

以下学习记录也是以这两个网站提供的库函数源码进行学习的。

字符串相关

strcpy()函数

头文件:#include <string.h>

函数原型:char *strcpy(char *dest, const char *src);

函数描述:将src指向的字符串拷贝到dest,包括结束符'\0'。字符串不能重叠,并且dest有足够的空间接收拷贝的字符串。

返回值:返回指向dest存放字符串的指针。

函数原型:

char *strcpy(char *dest, const char *src)
{
char *dst = dest; while ((*dst = *src) != '\0') {
src++;
dst++;
} return dest;
}

可以看出,函数中并不会检查dest的空间大小,只会拷贝字符串直到遇到src字符串的结束符'\0',因此dest的空间大小需要用户保证。

测试用例一:dest空间大于src指向的字符串个数。

#include <stdio.h>
#include <string.h> int main(void)
{
char *str_original = "0123456789";
char buf_dest[12] = {0};
char *ret = NULL;
int i = 0; for (i = 0; i < sizeof(buf_dest); i++)
buf_dest[i] = 1; printf("dest:0x%x\n", buf_dest);
ret = strcpy(buf_dest, str_original);
printf(" ret:0x%x\n", ret);
for (i = 0; i < sizeof(buf_dest); i++)
printf("%d ", buf_dest[i]);
printf("\n"); return 0;
}

编译,运行结果:

$ ./a.out
dest:0xca8e26c0
ret:0xca8e26c0
48 49 50 51 52 53 54 55 56 57 0 1

可以看出,字符串拷贝的时候会拷贝字符串结束符'\0'。

测试用例二:dest空间小于src指向的字符串个数(错误用法)。

#include <stdio.h>
#include <string.h> int main(void)
{
char *str_original = "0123456789";
char buf_dest[5] = {0};
char *ret = NULL;
int i = 0; for (i = 0; i < sizeof(buf_dest); i++)
buf_dest[i] = 1; printf("dest:0x%x\n", buf_dest);
ret = strcpy(buf_dest, str_original);
printf(" ret:0x%x\n", ret);
for (i = 0; i < sizeof(buf_dest); i++)
printf("%d ", buf_dest[i]);
printf("\n"); return 0;
}

编译,运行:

由于dest没有以'\0'结尾,因此printf打印的信息是错误的,访问的内容已经超过了dest的空间。

测试用例三:内存重叠

参考博客:strcpy函数的实现 - Norcy - 博客园 (cnblogs.com)

#include <stdio.h>
#include <string.h> int main(void)
{
char str[12] = "hello";
strcpy(str + 1, str); //存在段错误
printf("%s\n", str); return 0;
}
#include <stdio.h>
#include <string.h> int main(void)
{
char str[12] = "hello"; strcpy(str, str+1);
printf("%s\n", str); //打印输出ello return 0;
}

第一种情况:strcpy(str + 1, str),这种情况下dest指向'e',而src第一个字符为'h',当拷贝的时候,'\0'结束符会被覆盖掉,导致一直拷贝,陷入死循环。

第二种情况:strcpy(str, str+1),这种情况下,仅拷贝"ello"。

strncpy()函数

头文件:#include <string.h>

函数原型:char *strncpy(char *dest, const char *src, size_t n);

函数描述:功能和strcpy函数类似,但仅拷贝src的n字节给dest。另外如果n字节中没有结束符'\0',那么拷贝完后,dest中是不会有结束符的,这个需要注意。如果src的长度小于n字节,将会在拷贝src字符串之后继续拷贝结束符给dest,直到满足n字节。

函数原型:

char *strncpy (char *s1, const char *s2, size_t n)
{
reg_char c;
char *s = s1; --s1; if (n >= 4)
{
size_t n4 = n >> 2;
for ( ; ; )
{
c = *s2++;
*++s1 = c;
if (c == '\0')
break;
c = *s2++;
*++s1 = c;
if (c == '\0')
break;
c = *s2++;
*++s1 = c;
if (c == '\0')
break;
c = *s2++;
*++s1 = c;
if (c == '\0')
break;
if (--n4 == 0)
goto last_chars;
}
n = n - (s1 - s) - 1;
if (n == 0)
return s;
goto zero_fill;
} last_chars:
n &= 3;
if (n == 0)
return s; do
{
c = *s2++;
*++s1 = c;
if (--n == 0)
return s;
} while (c != '\0'); zero_fill:
do
*++s1 = '\0';
while (--n > 0); return s;
}

测试用例一:src字符串长度大于n,且前n个字符中存在结束符,则只会拷贝到第一个结束符时就结束拷贝。

#include <stdio.h>
#include <string.h> int main(void)
{
char str_original[] = {'a', '\0', 'b', '\0', 'c', 'd', 'e', '\0'};
char str_dest[8] = {0};
int i = 0; strncpy(str_dest, str_original, 8);
for (i = 0; i < 12; i++)
printf("%d ", str_dest[i]);
printf("\n"); return 0;
}

编译,运行:

$ ./a.out
97 0 0 0 0 0 0 0

测试用例二:src字符串长度大于n,且前n个字符中不存在结束符,则会拷贝n个字符。处理这种情况,一般需要自己去在dest末尾添加一个结束符。

#include <stdio.h>
#include <string.h> int main(void)
{
char str_original[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'};
char str_dest[5] = {0};
int i = 0; strncpy(str_dest, str_original, 5);
for (i = 0; i < 5; i++)
printf("%d ", str_dest[i]);
printf("\n"); return 0;
}

编译,运行:

$ ./a.out
97 98 99 100 101

测试用例三:src字符串长度小于n,将会在拷贝src字符串之后继续拷贝结束符给dest,直到满足n字节。

#include <stdio.h>
#include <string.h> int main(void)
{
char str_original[] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'};
char str_dest[12] = {0};
int i = 0; for (i = 0; i < 12; i++)
str_dest[i] = 1; strncpy(str_dest, str_original, 12);
for (i = 0; i < 12; i++)
printf("%d ", str_dest[i]);
printf("\n"); return 0;
}

编译,运行:

$ ./a.out
97 98 99 100 101 102 0 0 0 0 0 0

最新文章

  1. [转]MySQL日志——Undo | Redo
  2. 转:CentOS/Debian/Ubuntu一键安装LAMP(Apache/MySQL/PHP)环境
  3. 简述reflow和repaint
  4. 使用javascript实现贪吃蛇游戏
  5. php进阶函数
  6. iOS开发中乱用hook可能导致灾难
  7. 2015GitWebRTC编译实录10
  8. Qt Creator 黑色主题配置
  9. Object C语法学习笔记(一)
  10. weblogic远程调试
  11. UpdatePanel与$.function()同时使用问题
  12. HL7及PIX相关的测试工具
  13. [Ecmall]ECMALL目录结构设置与数据库表
  14. Java 原始模型(Prototype)模式
  15. Docker 启动,进入容器,查看log命令
  16. 51单片机学习笔记(清翔版)(21)——ADDA数模转换
  17. Java ee第四周作业
  18. 使用UTL_HTTP时遭遇ORA-29273
  19. 函数指针与typedef
  20. mpvue两小时,产出一个《点钞辅助工具》小程序

热门文章

  1. 适用于AMD ROC GPU的Numba概述
  2. 10: java数据结构和算法: 构建哈夫曼树, 获取哈夫曼编码, 使用哈夫曼编码原理对文件压缩和解压
  3. Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer
  4. 【NX二次开发】体消参,移除体参数UF_MODL_delete_body_parms()
  5. SpringCloud Alibaba实战(6:nacos-server服务搭建)
  6. 【codeforces841A】Generous Kefa
  7. 回顾Games101图形学(一)几何变换中一些公式的推导
  8. vue环境搭建以及使用vue-cli创建项目
  9. 大型情感类技术连续剧-徒手撸一个 uTools(二)
  10. 三剑客-sed