C++字符数组

好长时间没记点东西了,今天晚上就写一点。

其实这个问题也是之前比较早遇到的过的问题。当时觉得没什么,可是,今天在写点东西的时候再次越到,却花了短时间找不到错误!这就有点不应该了。索性今天就写一下吧;

事情是这样的,今天下午在写一段小代码。主要的功能是这样的:


函数将字符串中的字符'*'移到串的前部分,

前面的非'*'字符后移,但不能改变非'*'字符的先后顺序,函数返回串中字符'*'的数量。
如原始串为:ab**cd**e*12,
处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间)

问题看起来是非常简单的,代码也很快就写了出来》》


#include <iostream>

using namespace std;


void fun(char *src)
{
    char *pFast = src;
    char *pSlow = src;
    
    while(*pFast != '\0')
    {
           if(*pFast != '*')
           {
               *pSlow = *pFast;
               pSlow++;
           }
           pFast++;
    }    
    while(*pSlow != '\0')
    {
        *pSlow++ = '*';
    }    
}    
int main(int argc, char *argv[])
{
  char *src = "a*b*c*d*e";
  fun(src);
  while(*src != '\0')
  {
      cout << *src++;
      }
   cout << endl;      
  system("PAUSE");	
  return 0;
}


程序简单到写起来行云流水,可是没想到,在编译的运行的时候,跪了。。。程序崩溃了。

没关系,崩溃了咱们就调试下,看是在哪崩溃的呗~,于是下断点,跟进去发现是在函数Fun中的


*pSlow = *pFast;
赋值时崩溃的,这令我感到非常的奇葩。怎么回事?回来的一路上都在思考这个问题,回到宿舍的时候,突然想到了应该是字符串常量的问题。。。


这里把字符串常量方面的东西,给加深下吧:

(这段直接从网上引用过来的

http://blog.163.com/bbluesnow@126/blog/static/27784545201251205247130/)

   在程序中一般是通过变量名来对内存单位进行存取操作的。程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。

      

1、 常量字符串

在代码里直接出现的”abcdef”这种字符串,在程序执行的时候,系统会将它们放在常量区,所谓常量区就是一直存在的,只读的,不可更改的数据区域,并且一个字符串只会有一份。假设你在程序里有两行代码

char* p1 = “agcd” ;

char* p2 = “agcd” ;

无论你这两个行代码隔了多远,如果你想知道p1p2所指向的字符串在内存中是不是同一个,那答案是肯定的,p1p2的值完全一样。”agcd”这是一个存在于内存中的常量字符串,它从程序一开始就在那里,一直到程序结束读不会改变。在内存中,”agcd”是以如下方式存储的

‘a’

’g’

‘c’

‘d’

‘\0’

它的最后肯定有一个字符串结束标志’\0’。在种字符串的名字叫“以空字符为结束标志的字符串”。

char* p1 = “agcd” ;

如果你这时候想改变第一个字符的值,用p[0] =’b’,系统会报一个错,常量字符不能更改(这里为什么指针可以当数组用,下面再解释)

 

2、 字符数组

如果你定义一个char a[10],那么系统会“只分配”10char这么长的内存区域,一个char是一个字节,那么系统会分配十个字节的内存空间,并且将这一片连续的内存空间的首地址赋值给a。也就是说“数组名的值是数组所在内存区域的首地址”换句话说“数组名是一个指针,指向数组第一个值的地址”。

如果你定义一个char a[] = “abcdefg”;这句代码就复杂点了。定义一个数组,数组长度未知,那么系统会根据等号后面的值来“初始化”这个数组,等号后面是什么?前面说过,它是一个常量字符串。在内存中占8个字节,7个字符加上一个结束标志。这时候在内存中就有两个”abcdefg”的字符串了,一个是常量区域的,另一个是根据前者复制了一份的。这句代码的意思就是复制一个常量区域的字符串,将复制后的字符串的首字母的地址赋值给a

也就是说,最后a所指向的内存区域,已经不是常量里的”abcdefg”了,这里为什么要复制一份呢?原因是因为常量是不允许更改的,而数组一般都意味着需要修改,所以就复制了一份数据,放在非常量区域,就可以更改了。下面的测试程序可证明上述结论:

           char* p1 = "abcdef" ;

    char* p2 = "abcdef" ;

    char a[]= "abcdef" ;

 

    unsigned long dwP1 = (unsigned long)p1 ;

//32位系统里的指针就是4个字节的整数,这样可以具体查看指针的值。

    unsigned long dwP2 = (unsigned long)p2 ;

    unsigned long dwA = (unsigned long)a ;

 

    printf("p1的值(32位地址) = 0x%X\n",dwP1);

    //%X是打印十六进制,X是大写,x是小写

    printf("p2的值(32位地址) = 0x%X\n",dwP2);

    printf("a的值(32位地址) = 0x%X\n",dwA);

  

       从上面的结果可看出,常量字符串在内存中只有一份。而赋值给数组的时候,系统会拷贝一份。如果我们打印a[6]会是什么结果呢,请注意,字符串只有6个,最高索引是5

         printf("a[6]的值=%d",a[6]);//以整数打印

   

    虽然数组的可用长度是6,但是它第7个位置还是存在的,那就是空字符结束标志。空字符结束标志是必须的,因为很多时候系统并不知道你的字符串有多长。

 

3、 应用

当明白了这些本质后,我们怎么来灵活使用字符数组呢。在实际的编码中,比如从文件里读一段文字出来,假设当前文件中的字符串有20个字母。

第一步读取文件大小。

int fileSize = file.getSize();

第二步,分配缓存。这是动态分配数组。这种用法和java相似。

char* p = new char[fileSize+1] ;//1是为了后面放’\0’.

第三步,读取

file.read(p,fileSize);//意思是从文件里读取filesize个字节,并且放在p所指向的缓存中

第四步,标志结尾

P[fileSize] = ‘\0’; //由于指针指向的是字符串首字母地址,而数组名也是一样的,所以C/C++里指针和数组几乎用法一样。其他语言里不一样。这也是C/C++的魅力所在,够灵活。

 

这时候的p所指向的就是一片连续的内存空间,它的内容就是文件里的字符串,并且它的最后有一个结束标志(这样就可以在系统里灵活使用了)

看过之后应该就能明白了吧,后来改了下程序。直接就妥妥的了。

#include <iostream>

using namespace std;

void fun(char *src)
{
	char *pFast = src;
	char *pSlow = src;

	while(*pFast != '\0')
	{
		if(*pFast != '*')
		{
			*pSlow = *pFast;
			pSlow++;
		}
		pFast++;
	}    
	while(*pSlow != '\0')
	{
		*pSlow++ = '*';
	}    
}    

int main()
{
	char src[] = "*da*peng*king*";//修改src为数组
	fun(src);
	int i = 0;
	while(src[i] != '\0') //采用数组的方式遍历
	{
		cout << src[i++];
	}
	cout << endl;    
	
	return 1;
}
从今天这件事情上来看,可是看出上面的代码中char src[] = "afadf";其中src是一个 常量。。。

感觉这个问题,还是比较不容易发现的,今天记录下~



你可能感兴趣的:(C++字符数组)