字符常量区和strtok

strtok函数分解字符串为一组标记串,原型为:

 

extern char *strtok(char *s, char *delim); s为要分解的字符串,delim为分隔符字符串。首次调用时,s必须指向要分解的字符串,随后调用要把s设成NULLstrtoks中查找包含在delim中的字符并用NULL(‘/0’)来替换,直到找遍整个字符串。返回指向目前找到的最后一个标记串,当没有标记串时则返回空字符NULL。

 

看下面代码:

 

 

 

编译没有问题,但运行时会出现Segmentation fault的错误。

 

有些人就会百思不得其解,认为是strtok的问题,其实不然。如果将s的定义由char *s改成char s[]char s[20],则运行时就不会出现段错误而是想要的结果。可见问题不是strtok而是字符串常量的问题。看strtok的原型,s不是const的,说明在strtok里会修改s的值,而如果定义char *s="Golden Global View",则s指向只读区域不能修改。

 

可以看下面的程序:

 

#include<string.h

 

int main()

{

       char *s=(char *)malloc(sizeof(char)*10);

       s="abcdef";

       *(s+1)='g';

}

运行时会出现段错误。

      

下面的程序:

 #include<string.h>

 

int main()

{

       char *s=(char *)malloc(sizeof(char)*10);

       strcpy(s,"abcdef");

       *(s+1)='g';

}

 

则会正确运行。

 

 出现这些不同的结果,究其原因,是“非常量指针指向了常量字符串”。”abcdef”本身是一个常量字符串,系统把它放在了只读权限的内存空间,用可读写的指针指向这块区域本身就是错误的(逻辑上),而对只读内存区域进行写操作则造成了实际的错误[2]char s[]=”abc”则不然,首先声明了一个字符数组,然后将数组的大小定义成刚好能存下”abc”字符串,并且将”abc”存入该数组,这时数组存放在数据区并且已经在数据区分配了相应的空间。其实如果想改写指针指向的空间,就要确保指针指向可写的空间,想强制改写只读区域的内容是不鼓励的而且是另一个问题,再此不讨论。

 

 

 

 

 

还有一个规则就是,非常量指针可以隐式转换成常量指针,而反之则需要显示转换。如:

char a[10];

const char *p=a; 这是没有问题的。反过来:

const char *p=”abc”;

char *a=p; 这是不允许的,需要进行强制转换:char *a=(char *)p[3]

 

常量字符串问题的提出!

 

问题提出:
void main()
{
    char *const p1="show me the money/n";
    cout<<p1;
    *(p1+3)='a';
    cout<<p1;
}

可以通过编译,但是运行出错,为什么?

原因很简单,"show me the money/n"本身是一个常量字符串,在VC6.0下,系统把它放在了只读权限的内存空间。
用可读写的指针指向这块区域本身就是错误的(逻辑上),而对只读内存区域进行写操作则造成了实际的错误。

C++语法对char * ,char *const 类型指针允许指向常量字符串的宽容源自于对旧的C代码的兼容。但是这种兼容造成的代码的隐患和对开发者的困扰却是不容忽视的。
我没有查阅新的C/C++标准对这一行为的描述和处理,个人认为,对于这种代码,编译器应该至少提出一个类似:“非常量指针指向了常量”的警告,或者用一个编译开关选择是否兼容旧的C语法。
对于新的代码,还是建议使用:
const char * p= "Test";
这种形式,如果写入不可避免,建议:char p[] = "Test";
可是很遗憾的是,编译器不提供对数组越界的检测,这种检测在运行时也没有进行,除了代价太高之外,指针等一些特殊的手法(比如结构体的最后一个成员用 unsigned char a[0]以实现变长结构)也无法有效运作。
因此,为了代码的安全和效率,只能在必要的时候检查数组的越界与否,或者运用ASSERT等调试手法勉强应付。

补充一点:系统是否把一个字符串常量放在只读内存空间由选项:
/GF,/Gf决定,这个选项的详细信息请参考msdn中
/Gf, /GF   (Eliminate Duplicate Strings)
部分的说明。
表面上看,VC在_DEBUG选项下是默认开启了/GF选项。而在release下则是/Gf.


 

 

 

使用strtok函数分割。

原型:char *strtok(char *s, char *delim);
strtok在s中查找包含在delim中的字符并用NULL('/0')来替换,直到找遍整个字符串。 
功能:分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
说明:首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。
           strtok在s中查找包含在delim中的字符并用NULL('/0')来替换,直到找遍整个字符串。
返回值:从s开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。
           所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

 使用例:

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


int main(int argc,char **argv)
#include 
<stdio.h>
#include 
<string.h>


int main(int argc,char **argv)
{


    
char * buf1="aaa, ,a, ,,,bbb-c,,,ee|abc";

    
/* Establish string and get the first token: */
    
char* token = strtok( buf1, ",-|");
    
while( token != NULL )
    {
        
/* While there are tokens in "string" */
        printf( 
"%s ", token );
        
/* Get next token: */
        token 
= strtok( NULL, ",-|");
    }
    
return 0;

}

 

 

 

 

 

 

OUT 值:
aaa
 
a
 
bbb
c
ee
abc

 

 

使用strstr函数分割。

原型:extern char *strstr(char *haystack, char *needle);
        
 用法:#include <string.h>
  
 功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。
  
 说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。

使用例:


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

int main(int argc,char **argv)
{
char *haystack="aaa||a||bbb||c||ee||";
char *needle="||";
char* buf = strstr( haystack, needle);
while( buf != NULL )
{
    buf[0]='/0';
    printf( "%s/n ", haystack);
    haystack = buf + strlen(needle);
    /* Get next token: */
    buf = strstr( haystack, needle);
}
   return 0;
}

 

 OUT 值:
aaa
 a
 bbb
 c
 ee

strtok比较适合多个字符作分隔符的场合,而strstr适合用字符串作分隔符的场合

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值