c库函数strncat出现乱字符

25 Nov 2016 c库函数strncat出现乱字符

最近项目接近尾声,基本都是在fix bug,今天说说一个bug,是由于strncat函数使用不当导致命令行输出有乱字符。虽说只是一个很简单的小问题,但是不弄明白就是大问题。

1 strncat声明

char *strncat(char *dest, const char *src, size_t n)

- dest: 目标字符串,需保证其容量能容纳连接后字符串
- src: 源字符串
- n: 追加的字符数,如果超过src大小,只拷贝src所有字符

这个函数的主要功能是将src指向的字符串追加到dest指向的字符串,最多追加n个字符,包括最后一个字符串结尾符NUL,最后返回指向dest字符串的指针。

2 strncat误用

引起bug的代码大致如下,主要功能是将一个字符串拷贝到一个空的字符数组中。

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

int main ()
{
   char dest[10];
   strncat(dest, "Hello World!", 100);
   printf("Final destination string: %s\n", dest);
   return (0);
}

3 代码输出

root@leo:demo# crun strncat.c
Final destination string: Hello World!
root@leo:demo# crun strncat.c
Final destination string: τ:Hello World!
root@leo:demo# crun strncat.c
Final destination string: ᰷Hello World!

如果从第一次运行结果来看,看似没有什么问题。但是再运行2次,发现结果有乱字符。

广告时间: 这里使用的crun是我封装的一个命令,只是为了在终端运行c代码时方便,如果你觉得实用的话拷贝到你的bash配置文件,命令定义如下:

crun(){
    gcc $1 -g;
    if [ $? -ne 0 ]; then
        echo "failed build $1"
    else
        ./a.out
        rm -rf ./a.out
    fi
}

4 出现乱字符的原因

输出有乱字符,是因为dest声明后,没有初始化,其内存空间是一些随机数据。不是一个合法的字符串,所以没有字符串结束符NUL,但是strncat函数在追加字符串时需要知道目的字符串dest的结尾,因为找不到字符串结束符NUL,所以出现未定义行为。

5 strncat正确使用

所以正确使用方式是给dest第一个字符赋值为NUL,即字符\0。

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

int main ()
{
   char dest[10];
   dest[0] = '\0';
   strncat(dest, "Hello World!", 100);
   printf("Final destination string: %s\n", dest);
   return (0);
}

6 总结

在c语言中,如果忽略了字符串结束符,会导致很多依赖字符串结束符的函数发生未定义行为。比如以下函数:

  • strlen
  • strcat
  • strcpy
  • strncpy

再强调下c字符串定义:

字符串是以ASCII字符NUL结尾的字符序列,ASCII字符NUL表示为\0。

所以不要把字符数组和字符串混淆,字符串是一个字符数组,但是该字符数组最后一个字符必须是\0,但是字符数组不一定是字符串。上面说到的bug就是因为混淆了字符数组和字符串定义导致。

本次荐书:代码的未来

LEo at 22:09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端小吉米

Promise 串行调用面试题

20530
来自专栏鸿的学习笔记

Python漫谈

列表切片的标准是lst[a:b:c] a是列表切片的开头,b是切片的结尾,c是step。 这里的表示从第一个元素开始,按照每隔一步,到第三个元素结束

10330
来自专栏C语言及其他语言

【编程经验】优秀题解

1) 定义一个字符指针数组,用来存放将要输入的各个字符串的指针(用动态内存分配开辟空间存放数组)

8550
来自专栏python3

python3--元组(tuple),列表(list),字典dict,其它(for,enumerate,range)

元组被称为只读列表,即数据可以被查询,但不能被修改,所以,字符串的切片操作同样适用于元组

26610
来自专栏HTML5学堂

伪数组

HTML5学堂:熟悉JavaScript的人对document.getElementsByTagName再熟悉不过,对arguments也多有耳闻,我们时常针对...

28740
来自专栏杂文共赏

Go中没有按引用传递

在Go中两个变量共享同一块内存区域是不可能的。但是两个变量指向的实际存储位置是可以一样的,但这不同于两个变量共享相同的存储区域。

20010
来自专栏我和我大前端的故事

初探 TypeScript函数基本类型泛型接口类内置对象

前段时间有朋友和我推荐 TypeScript ,他说写起来特别爽,让我去试一试,那时候我还在那是啥高深莫测的东西。刚好那段时间忙,一直没有时间看。最近也很忙,还...

66020
来自专栏社区的朋友们

sizeof 知多少? (下)

稍熟悉 C/C++ 的朋友,对于 sizeof 肯定不陌生,通过它我们可以知晓某个类型或者实例的内存大小( 以字节计 ),网上关于这个话题的信息其实挺多的,但是...

11900
来自专栏java学习

java每日一练(2017/9/16)

本期题目 (单选题) 1、代码String str=”123456a”;int i=Integer.parseInt(str);会报异常的是() A java...

38980
来自专栏finleyMa

说下js中的bind

bind的受体是对象,返回的是个新的函数。 我们知道this总是指向调用他的对象。但是有时候我们希望‘固化’这个this。 也就是无论怎么调用这个返回的函数...

70710

扫码关注云+社区

领取腾讯云代金券