补遗篇之C字符串

    C并没有字符串类型,C字符串是一个以null('\0')结尾的字符数组,用null标识字符串结束。如{'a','b','c','d','\0'},只有包含这个'\0’才算C字符串。注意null也好,'\0'也罢,都只是0的不同表达,如:

    char a[] = “ABCDEF”;

    printf(“a=%s\n”,a);

    a[2]=0;

    printf(“a=%s\n”, a);

    运行看看,明白什么是null以及它的作用了吧!

字符串与字符

    要注意区分'a'和"ABCDEF"这两种形式。单引号修饰的单字符实际代表一个整数,其值为该字符在编译器默认字符集中(一般为ascii)的映射值。如char x =’a’;ascii表,等价于char x = 0x61。而用双引号修饰的字符序列,即字符串常量,可看作无名字符数组,该数组被双引号中的字符集和一个隐含的'\0’初始化。

字符串与字符数组

    结尾是否有'\0'是字符串和一般字符数组的唯一区别,{'a','b','c','d’,'\0'}是字符串,而{'a','b','c','d’}是字符数组。这两种形式很好区分,但定义太繁琐,于是C为了简便,引入另一种字符串定义形式,即"abcd”,它等价于{'a’,'b’,'c’,'d’,'\0’}。这种定义更简单,但外表看不到null(尽管编译器知道),需要不断自我提醒"abcd”是字符串,结尾隐含'\0’。可看不见摸不到总是容易混淆。如:

    char a[] = “ABCDEF”;    

    char b[] = {'A','B','C','D','E’,'F'};

    叙述正确的是:A)ab完全相同  B)ab长度相同  C)ab都是字符串 D)a数组比b数组长

    只有D。因为"ABCDEF"等价{'A','B’,'C’,'D’,'E’,'F’,'\0'},因此ab并不相同,a长度为7b长度为6a为字符串,b只是普通字符数组。

编程中几乎所有和C字符串相关的错误都和这个隐含的'\0’相关。

strxxxmemxxx

    除标准字符串处理函数外,还有一些函数也能操作字符串,如memcpy代替strcpymemcmp代替strcmp等。这两套怎么选择?

    a. 首先,非字符串操作,不能用strxxx,如:

    char a[4],b[4],c[4];

    a[0]=0; a[1]=2; a[2]=0; a[3]=1;

    memcpy(b, a, 4);

    strncpy(c, a, 4);

    源数据a[4]中间有0strncpy会把0当成字符串结束符而提前终止拷贝。而memcpy执行内存拷贝,不受内容影响。

    b. 对于字符串操作,都可以用。strxxx专门针对字符串特性,遇到null自动结束,使用起来更方便。

'\0’的代价

    字符串默认以'\0’结尾,标识字符流结束,区分字符串和一般字符数组,表达形式更自然。但作为代价,'\0’也引出很多问题:

    a. 字符串中除结尾外不能包含任何0,所以它不能保存二进制数据。

    b. 要搞清哪些strxxx结尾自动补'\0’,哪些不补。如strncpy(dst, src, n)不自动补0,如果dst在操作前未清零,而nsrc只填充部分dst,那dst就可能因缺少结束符而成为非法字符串。

    c. 而strcpy(dst,src)中,如果src是未置'\0’的字符数组,strcpy就找不到结束符而死循环。两害择其轻,最好strnxxx()代替strxxx()

    d. strxxx每次都判断是否null,性能较差且随字串长度变化(油漆工的故事)。如下strcat:

    void strcat( char* dest, char* src )

    {

     while (*dest) dest++;

     while (*dest++ = *src++);

    }

    char longstr[1000];

    longstr[0] = '\0';

    strcat(longstr,"John, ");

    strcat(longstr,"Paul, ");

    strcat每次都要扫描整个dst字符串,寻找尾部'\0’,字符串越长,性能越差。需要注意字符串操作的性能不确定性。

避免字符串硬编码

    字符串一般不是程序的关键角色,程序员往往直接把它们随意写到代码里,如:

    if (..) {  MessageBox(_T("1.0.0.1, xxx Company");  }

    但是,这种字符串硬编码会带来如下问题:

    a. 可读性不好且无法集中修改:裸字符串和奇异数一样,含义不明确,如果分散写在代码里,难以统一维护。最好用define或全局const变量来表示,即反边统一修改维护,也能明确显示含义,如:

#define SOFTWARE_VERSION_INFO ("1.0.0.1, xxx Company ")或const char VersionInfo[] = "1.0.0.1, xxx Company ";

    b. 难以统计资源占用:字符串常量占用静态只读内存,如果某程序中字符串很多,需要统计资源占用,把它们提取出来专门放置,比分散在源码里更方便统计和优化。

    c. 配置多语言版本:如果字符串硬编码,要出多语言版,必须一个个去修改字符串,再重新编译,导致一种语言一份代码。可事先把所有与显示相关的字符串放到资源配置文件中(自定义的保存资源的配置文件),配合编译开关选择切换不同语言的字符串。

油漆工的笑话:

    某人得到一份油漆工作,负责在马路中间喷涂画线。第一天,他带着一罐漆来到他负责的路段,喷涂了300码长的线。“干得不错!”老板称赞道,“真是一位麻利的工匠”,然后赏给他一个硬币。

    第二天,他只喷涂了150码。“虽然不如昨天,但仍然算得上一位麻利的工匠!150码也不错,”老板又赏给他一个硬币。

    然而第三天,他只喷涂了30码长的马路。“才30码!”老板吼道。“这太令人难以接受了!第一天你的工作量是今天的10倍!怎么回事?”

   “我尽力了,”油漆工哭丧着脸。“可一天一天下来,我离油漆罐越来越远!”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值