next_permutation

从邹博那里的算法:
next_permutation:
如果一个数后面有比他大的数,就应该有下一个字典序大的permutation了,下一个应当是与后面比他大的数里 最接近他的那个交换,因为这个才是紧接着他的permutation

  1. 找最后一个数a[i],使得后面存在比他大的数
  2. 找该数a[i]与后面比!!他大的数!!里最小的数a[maxmini]//一开始没想清楚,本以为1 2点两个循环可以合并,后来发现不行,必须是比a[i]大的数里找,a[i]必须循环结束了才能确定,然后maxmini 初始可以选i+1,因为至少a[i+1]>a[i]的至于是不是最小则遍历一遍打擂台即可,
    我确实不太喜欢定义一个MAXNUM,总觉得这样程序适用性会有局限性,除非万不得已,例如Dijstra算法,其实也可以去掉,但是来麻烦了,而且也没有这种做法= =
  3. swap(a[i], a[maxmini])
  4. 将该数[i]后面的数排序

    void nextpermutation(int *a, int n)
    {

     int i=n-1;
     while(i>=1 && a[i-1]>a[i])//property
     {
         i--;
     }
    
     if(i<1)
         return;
     //i-1 is to be swapped
    
     int min=a[i],mini=i;
     for(int j=n-1;j>=i;j--)
     {
         if(a[j]<min && a[j]>a[i-1])
         {
             mini=j;min=a[j];
         }
     }
    
     swap(a[i-1],a[mini]);
     sort(a+i,a+n);//sort [begin, end)
    

    }

这里面有个bug,如果数字里出现重复的就挂了,因为while的目的是为了找第一次出现a[i-1]<a[i]的,=都不行的,因为等于的话,交换可能是无用交换,导致后面的还是和当前的permutation一样。因此while改为

while(i>=1 && a[i-1]>=a[i])//property

另外里面找不到这样的i的时候,允许循环pemutation需要手动调整到第一个ascending顺序的permutation,因此代码改为:

if(i<1)
{
    sort(a,a+n);
    return;
}

试了下库函数,next_permutation是可以直接跳过重复的permutation的,所以如果重复数的话,n!次nextpermutaion就不是到起点了,而是过头了。
另外这里面本来是 找到i=max k (a[k]b 可以确定一定后面没有比a大的数么?

答案是可以!因为前面如果能过来,说明逐个都是从后往前递减的,如果a>b 的话, 由于b>c,所以a>c 也即a只要和最右判断大小即可确定后面是否有比a大的数了!

另外还有一点性质,就是swap之后,i+1…n-1之间一定是递减的,因为a[k]是大于a[i]里最小的,a[i]比大于后面的,因为后面的肯定都小于a[i],而前面也肯定都是大于a[i],因为前面的数比a[k]大(之前是选出最小),于是我意识
到自己不了解这个性质导致性能降低,于是里面的sort全部换成reverse。。。。

但是看到邹博的代码好像挺复杂的。

Posted by richard爱闹 - 7月 23 2014
如需转载,请注明: 本文来自 Richard

Median of two array

两个数组的中位数,这道是11CS的DS最后一题,考研的童鞋一定不会陌生,都已经做烂了,但是想想看第一次做的时候,是否能写出正确的处理所有case的代码了。
我也不记得当时有没有写出来了。。。。

这道题目很多帖子,也是经典题目
http://wenku.baidu.com/view/114e577a27284b73f242506b
http://liubangchuan.iteye.com/blog/1870650
http://my.oschina.net/mustang/blog/58047
http://blog.csdn.net/hackbuteer1/article/details/7584838

先来个简单的,假设两个数组等长,且sorted,假设median定义为 两个数组中 中间两个数较小的一个
思路大家基本都知道,就是两个数组同时二分,然后比较, a[mid1]b[mid2],那么a往低处走,b往高处走,相等的话直接返回结果。
思想是基于这个:中位数必>=至少”一半”的数,n奇,(n-1)/2 此处写的除都是数学的除,不是C++的除,避免混淆,如果偶数,必>=(n-2)/2),对于等长数组,n比为偶数

(中位数:如果长度为奇,就是排序后中间的数,如果偶,就是中间的两个数的任意一个(leetcode定义为均值)),那么接下来代码AC(如果面试官算法非常强的话,应该是知道你的代码是否可以AC的)之前需要回答几个问题:

  1. 返回的出口在哪几个地方?
  2. 奇偶性处理

对于第一个问题,首先如果相等的话,就找到了,因为<=a[mid] 的数至少(n-2)/2, >=a[mid]也一样,b[mid]也一样,所以直接返回中位数了,如果a[mid1]<b[mid1],a[0…mid1-1] b[0..mid1-1]最多这么多数小于。。。。
突然发现好像看的答案好像只能返回偶数时两个中位数中靠前的一个。。。。。呜呜呜。。。。。。

大致思想是如果<=a[mid]的数都不到一半了,往上移的时候可以多移一位,否则要保留mid,b[]也是一样的。

现附上这个两个等长数组,返回中位数(如果偶数个数时返回中间两个任意都可以的话)代码(由于没有OJ测所以未必保证正确性):

int findMedianSortedArrays_EqualSize(int A[], int B[], int n)
{
    int low1=0,high1=n-1,low2=0,high2=n-1;
    while(low1<high1 || low2<high2)
    {
        int mid1=(low1+high1)/2;
        int mid2=(low2+high2)/2;
        if(A[mid1]<B[mid2])
        {

            if(n%2==0)//a[mid1] can not be median of a[]+b[], analyze...
                low1=mid1+1,high2=mid2;
            else
                low1=mid1,high2=mid2;
        }
        else if(A[mid1]>B[mid2])//对称分析
        {
            if(n%2==0)
                low2=mid2,high2=mid2-1;
            else
                low2=mid2,high2=mid2;
        }
        else
        {
            return A[mid1];
        }
    }
    return A[low1]<B[low2]?A[low1]:B[low2];

}

这个等长的情况已经比较麻烦了,包括最后返回的这两个low1 low2的解释我还没有看到,而不等长的里面还嵌套了一个外部函数的递归,我本打算基于上述这个弄个不等长的,始终搞不出来o(╯□╰)o
不等长的就暂时放一放吧,这个从昨天一直搞到几天,断断续续弄,太浪费时间了,先暂时搁置吧,我用merge方向也过了leetcode,leetcode其实时间很松的。

Posted by richard爱闹 - 7月 23 2014
如需转载,请注明: 本文来自 Richard

scanf eof cin

今天发现遇到一个奇怪的问题,群里的童鞋说POJ这样写TLE

while(~scanf("%s%s", s,e))

但是这样写就AC了

while(scanf("%s%s", s,e)!=eof)

后来发现,G++两个都可以,但是C++(MSVC)第一个就会TLE了

看了官方的scanf资料,http://www.cplusplus.com/reference/cstdio/scanf/?kw=scanf
发现还是比较模糊,尤其是eof之前就有阴影,感觉有时候会很奇怪。看过一篇详细的eof博客,还是没理清头绪

while(cin>>s>>e)

这种就不需要管eof的事情,OJ后台可能还是重定向到文件里,然后去读文件的

Posted by richard爱闹 - 7月 23 2014
如需转载,请注明: 本文来自 Richard

BFS-Knight

看到群里童鞋说这个scanf会挂,于是我试了下,poj已提交网页就挂了,不知道为啥。

朴素的bfs,之前dfs练得多一些,图的dfs相比树的因为更复杂,需要visit来避免重复访问。poj2243, 这道就是基本的bfs了,好像也没有剪枝的策略。

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <cstring>
#include <string>
#include <algorithm>
#include <iomanip>
//#include <unordered_set>
#define Min(a,b) (((a) < (b)) ? (a) : (b))
#define Max(a,b) (((a) > (b)) ? (a) : (b))
#define read freopen("in.txt","r",stdin)
#define write freopen("out.txt","w",stdout)
using namespace std;

struct node
{
    int row;
    int col;
    int path;

};

int main()
{
    //read;
    //write;
    char s[3],e[3];
    while(~scanf("%s%s",s,e))
    {
        //cin>>e;

        node start,end;
        start.col=s[0]-'a';
        start.row=s[1]-'1';
        start.path=0;
        end.col=e[0]-'a';
        end.row=e[1]-'1';

        queue<node> q;

        q.push(start);
        node p;
        while(!q.empty())
        {
            p=q.front();
            q.pop();
            if(p.row==end.row && p.col== end.col)
            {
                break;
            }

            if(p.row-2 >=0 && p.col+1<=7)
            {
                node nd;
                nd.row=p.row-2;nd.col=p.col+1;
                nd.path=p.path+1;
                q.push(nd);
            }

            if(p.row-1 >=0 && p.col+2<=7)
            {
                node nd;
                nd.row=p.row-1;nd.col=p.col+2;
                nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row+1 <=7 && p.col+2<=7)
            {
                node nd;
                nd.row=p.row+1;nd.col=p.col+2;
                nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row+2 <=7 && p.col+1<=7)
            {
                node nd;
                nd.row=p.row+2;nd.col=p.col+1;
                nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row+2 <=7 && p.col-1>=0)
            {
                node nd;
                nd.row=p.row+2;nd.col=p.col-1;
                nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row+1 <=7 && p.col-2>=0)
            {
                node nd;
                nd.row=p.row+1;nd.col=p.col-2;nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row-1 >=0 && p.col-2>=0)
            {
                node nd;
                nd.row=p.row-1;nd.col=p.col-2;nd.path=p.path+1;
                q.push(nd);
            }
            if(p.row-2 >=0 && p.col-1>=0)
            {
                node nd;
                nd.row=p.row-2;nd.col=p.col-1;nd.path=p.path+1;
                q.push(nd);
            }
        }

        cout<<"To get from "<<s<<" to "<<e<<" takes "<<p.path<<" knight moves."<<endl;
    }

    return 0;
}

Posted by richard爱闹 - 7月 23 2014
如需转载,请注明: 本文来自 Richard

Recode SubsetsSnum

看到邹博写的permutation,发现参数比较有意思,是from to这种,后来想想,确实这种比较make sense,之前写的的什么selectk,k,selectn,n都非常容易在边界之类的出错,递归出口处的逻辑等等,这种非常清晰,于是
学习邹博的coding风格重写了subsetsnum 问题。

先写了一个朴素的,遍历完2^n搜索空间的朴素,from是起点,to是终点,所以这么调用Sum(a, 0,n-1,sumx);表示从a[from…to]里面找到子集和为sumx的集合,
后来发现,这样每次都去想到底是n还是n-1,出口是k>n还是k==n呢,每次都先写一个,然后拿个例子去测,还是邹博这种写法比较规范。

void Sum(int *a, int from, int to, int sumx)
{
    if(from>to)
    {
        int sum=0;
        for(int i=0;i<=to;i++)
            sum+=a[i]*select[i];
        if(sum==sumx)
        {
            for(int i=0;i<=to;i++)
                if(select[i])
                    cout<<a[i]<<" ";
            cout<<endl;
        }
    }
    else
    {
        select[from]=false;
        Sum(a,from+1,to, sumx);
        select[from]=true;
        Sum(a,from+1,to,sumx);
    }
}

一直都考虑到这种很多无用的搜索,例如之前和已经超了,还要去遍历,显然浪费了时间,于是将和<0的剪枝,但是之前写总会出问题,改成这种写法决定试试:

void Cut_Sum(int from, int to, int sumx)
{


    if(sumx<0) return;
    else if(sumx==0)
    {
        for(int i=0;i>=to;i--)
        {
            if(selectn[i])
                cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    else if(from>to)
        return;
    else
    {
        selectn[from]=false;
        Cut_Sum(from+1,to,sumx);
        selectn[from]=true;
        Cut_Sum(from+1,to,sumx-a[from]);
    }
}

后来和上面朴素的结果比一比发现又错了,递归程序改改又挂了o(╯□╰)o

1,2,3,4,5,6 sum=6
又来了阴影,打算重新模拟一遍,发现了问题,其实不是完全乱了,而是在回溯到2 4的时候,我把5 6也打印出来了,发现这时候5 6 的select都为true,难怪print出来,怎么会出这个问题,为啥原来的没有?
后来分析一遍,发现区别了,原来每次找到解都要遍历完6个数,因此后面的自然变为false了,而我现在剪枝的话,前面一开始从false到true后是没有复原到false的,然后又从0->to print出来当然误以为5 6都选上了,所以解决方案
是只把0->from-1的根据select print出来,注意是from-1,因为sum=0 是处理0->from之后得出的和,from+1还没看,所以把上面sumx=0的for改为

for(int i=from-1;i>=0;i--)

改成后面到前面是因为一道题目要求从大到小排序,我已开始把数组升序排了。
因此这是递归版剪枝的子集和问题代码,但是OJ由于时间掐的严,还是TLE了,因为递归还是很多重复调用,要改成非递归的,不过我都忘记了。。。

Posted by richard爱闹 - 7月 23 2014
如需转载,请注明: 本文来自 Richard

quick and right coding

昨天看了下邹博的PPT,邹博是负责July团队负责字符串一块的,包括Manachester算法,KMP,BP,字符串排列(其实是回溯法了)等等,
有一个不太重要的地方,就是邹博的代码比较短,而且看起来逻辑比较漂漂,而我的经常逻辑复杂,还写半天,还经常出bug,于是写点总结。

先拿最长回文子串朴素O(n^2)代码这个case来看,遍历回文串中心,我代码可能有问题,以为不需要首尾两个字符了,因为肯定是1了,但是忘记考虑偶数长回文子串了,如果后面Index=1(0-base)的没有考虑01为中心的偶长传可能就会有问题了,
末尾也是一样的。自己漏考虑。

int maxdrome_centre(string str)
{
    if(str.size()==0) return 0;
    int maxlen=1,max;
    for(int i=1;i<str.size()-1;i++)
    {
        max=1;
        for(int j=1;j<=(str.size()-1)/2;j++)//two
        {
            if(i-j>=0 && i+j <=str.size()-1)
            {
                if(str[i-j] == str[i+j])
                    max+=2;
                else
                    break;
            }
            else
                break;
        }
        if(max>maxlen)
            maxlen=max;
        max=1;
        if(str[i]==str[i+1])
        {
            max++;
            for(int j=1;j<=(str.size()-2)/2;j++)
            {
                if(i-j>=0 && i+1+j<=str.size()-1 )
                {
                    if(str[i-j]==str[i+1+j])
                        max+=2;
                    else
                        break;
                }
                else
                    break;
            }
            if(max>maxlen)
                maxlen=max;
        }


    }
    return maxlen;
}

看看这个代码里面,还嵌套了break,for条件判断之后,里面又判断了越界问题,其实是有重复的,for里面的条件其实没有必要,完全可以用if里面的替换掉,因为只要头尾都每越界,就可以一直两边extend,于是如果越界了,还break,如果写到for
条件里,刚好不用break了,里面如果长度相等就extend,没有的话break,这个是比较合理的,也需要的。而且这个max没必要每次加2,因为完全可以根据j计算出来。

因此代码修改后变成如下的:

int maxdrome_centre(string str)
{
    if(str.size()==0) return 0;
    int maxlen=1;//total max, at least 1
    for(int i=0;i<=str.size()-1;i++)
    {
        for(int j=1;i-j>=0 && i+j <=str.size()-1;j++)//odd
        {
            if(str[i-j] != str[i+j])
                break;
        }
        max=2*j+1;
        if(max>maxlen)
            maxlen=max;

        for(int j=0;(i-j>=0) && (i+j+1) <=str.size()-1;j++)//odd
        {
            if(str[i-j] != str[i+j+1])
                break;
        }
        max=2*j++2;
        if(max>maxlen)
            maxlen=max;
    }
    return maxlen;
}

这个逻辑几乎和邹博一样了,所以总结一下自己coding的一些不好的思维和设计逻辑的习惯
1.不要去根据不等式去计算i或者j的范围,这是计算机做的事情,例如i-j>=0 i+j<=str.size()-1 这个我有时候可能回想着推出i j范围,然后写在条件语句,这个其实直接写会比较好,计算机会处理好的
2.写逻辑语句要想清楚是否两个条件有重叠有包含关系,只要选那个更严格的条件就可以了,否则逻辑更复杂,例如我for里面先去限制j最大只能extend (n-1)/2长度,再在里面加if限制两边越界,其实越界包含了前者,
只需要这个条件,因此直接放到for条件里,这样少了break,还有计算长度直接有j决定,所以不需要里面每次max+2,当然这个问题会轻一点,但是没想到不应该。
3.另外后面偶数长度还先加if判断是否第一个成立,完全可以放到循环里面,不然逻辑复杂,易出bug,还有一个内部的max没必要,因为每次都是计算当期的与总的比较,不是两次max比较,也没想清楚。

总之代码写的还是naive,还是要多读别人的代码,例如fawkes,adhoc,邹博等,现在要求代码要写的快,逻辑简单清晰,精炼,越垄长的容易bug

Posted by richard爱闹 - 7月 22 2014
如需转载,请注明: 本文来自 Richard

Dijstra proof

看到09CS 考研DS第一道大题,是对Dijstra的变形,问是否正确,于是回顾这一经典的greedy算法。

算法思想和代码之前已经联系过了,但是这道题目涉及到了正确性证明,如果变形了是否保证正确性,尽管答案只是举了一个反例来说明,但是我还是打算回顾下
当时的proof。其实一直都不算真正意义的理解了。

利用数学归纳的思想:

集合分为close-set表示,已经算出最短路径的点击,open-set为剩余的点击。

1(初始条件). 一开始肯定是source点u了,因为dist为0,不可能有比他更短的了。假设第二个点p,现在证明p一定是u->p为p的最短路径。
假设不是,假设存在open-set点x,使得u->x x->p为最短路径,那么u->x一定比u->p短(边>0的性质),x应该比p先加入close-set,与一直矛盾

2(递推证明).加入当前close-set有些点,那么现在加入p,那么u->p的最短路径的点一定都在close-set里。
假设不是,假设路径出现了一个点x在opens-set(假设只是路径最后一个点在open-set,其他情况是转化为这个的吧),那么u->x x->p比u->p(中间全是close-set中点)要短,那么u->x比u->p短,x应该比p先加入close-set,
产生矛盾。

思路来源这里
http://blog.csdn.net/dog250/article/details/5303310

另外还附上一个童鞋另一种思维方式的理解正确性 http://my.oschina.net/mustang/blog/56216

当年算法书的证明感觉不make sense,所以弃掉了。

另外今天看到了一个priority_queue 也是在里面的,都是单向队列,只是加了优先级排序。

则是双向队列,两边都可以插和删,

STL与堆相关的主要是 make_heap push_heap, pop_heap 这样一些algorithm里的函数,用vector容器来装,可以实现heap

Posted by richard爱闹 - 7月 22 2014
如需转载,请注明: 本文来自 Richard

CockTailSort

今天看到鸡尾酒排序,看名字就像了解下,之前好像有稍微听过,但是具体不知道是啥。

是冒泡排序的改进版,原来每次冒最大(小)到最后面,现在两端来回冒,这样有啥好处呢?
例如 23451这样的 只要一来一回就排好了,但是bubblesort却要很多次。

所以写算法,感觉是n/2个来回,n为偶正好冒完所有,n为奇,除掉中间的元素,最后肯定也是定好位置的,因为其他n-1个元素都订好位置了。
另外和bubblesort一样,加swap flag,如果一来或者一回没有交换,那么就排好序了,调用C++11的swap函数。

后来看了下wikipedia的,感觉实现不太一样,他是直接用swap就来判断外部循环,思路也是一致,每个来回长度都要缩减2,

void CockTailSort(int*a, int n)
{
    bool isswap;
    for(int i=1;i<=n/2;i++)
    {
        isswap=false;
        for(int j=i-1;j<=n-i-1;j++)
        {
            if(a[j]>a[j+1])
                swap(a[j],a[j+1]),isswap=true;

        }
        if(isswap==false) break;
        isswap=false;
        for(int j=n-i-1;j>=i;j--)
        {
            if(a[j-1]>a[j])
                swap(a[j],a[j-1]),isswap=true;
        }
        if(isswap==false) break;
    }
}

Posted by richard爱闹 - 7月 22 2014
如需转载,请注明: 本文来自 Richard

trim variant c cpp string

实现trim的variant,将多个空格变为1个。但是逻辑似乎对我来说短时间不简单啊。

我开始尝试的是C++字符串,一个istringstream搞定,但是其实也会有问题,后来发现要试试C的,不然有点像调用了一个库或者class的感觉。

char * trim(char* str)
{
    int i=0;
    int len=strlen(str),startpos;
    char* a=new char[len+1];

    int offset=0;
    while(i<len)
    {
        startpos=i;
        while(i<len&&str[i]!=' ')
            i++;
        if(i==len) break;
        for(j=startpos;j<i;j++)
        {
            a[offset]=str[j];
            offset++;
        }
        a[offset]=' ';
        offset++;

        while(i<len&&str[i]==' ')
            i++;
    }
    a[offset]='\0';
    strcpy(str,a);
    delete[] a;
    return str;
}

上面代码有点问题的,break早了,最后一个非空格的串还没copy过去,另外改成strncpy,提高可读性

char * trim_new(char* str)
{
    int i=0,j;
    int len=strlen(str),startpos;
    char* a=new char[len+1];

    int offset=0;
    while(i<len)
    {
        startpos=i;
        while(i<len&&str[i]!=' ')
            i++;
        strncpy(a+offset,str+startpos,i-startpos);
        /*
        for(j=startpos;j<i;j++)
        {
            a[offset]=str[j];
            offset++;
        }
        */
        offset+=i-startpos;
        if(i==len) break;
        a[offset]=' ';
        offset++;

        while(i<len&&str[i]==' ')
            i++;
    }
    a[offset]='\0';
    strcpy(str,a);
    delete[] a;
    return str;
}    

我已开始犯了一个严重,但是我一直因为没弄清楚字符数组,字符串的区别导致的,上一篇总结好了。我用char a[]=”” 最后return a, 发现其实返回之后这块地址因为是栈的被销毁了,所以挂了。。。。
于是改成堆,但是又要释放空间这样比较好,然后只能拷贝回str,而且逻辑似乎不好,好不如直接在源串上改。

于是有了下面两个指针的代码,i指向当前处理好的字符串后一个位置,j指向后面待处理的字符串,记得开始记录startpos,所以是[startpos,j) copy回 i开始的位置,里面有个地方容易忽视,就是如果最后面是非空格的一串,后面要避免被赋上空格,或者直接break返回
也可以的,不break也可以因为后面condition控制好了越界的情况。

char *trim(char* str)
{
    int len=strlen(str);

    int i=0,j=0,startpos;
    while(j<len)
    {
        startpos=j;
        while(j<len && str[j]!=' ')
            j++;
        strncpy(str+i,str+startpos, j-startpos);

        i+=j-startpos;

        if(j<len)//no more space add
        {
            str[i]=' ';
            i++;
        }


        while(j<len && str[j]==' ')
            j++;
    }
    str[i]='\0';

    return str;
}

另外还有一个简单的C++ istringstream版的,但是好像有问题,例如开头一串空格的一个空格就丢了,因为里面封装了太多了,改起来不易。。。

char *trim(char *str)
{
    string strcpp=str;
    istringstream istr(str);
    istr>>outstr;
    while(istr>>split)
        outstr+=" "+split;


    strcpy(str,outstr.c_str());

    return str;
}

今天再次写这个程设题,总结了一些经验,如果先找非空格,后找空格似乎控制麻烦,就换成先找空格后找费空格,代码很好控制,而且如果能一个一个copy就尽量这种,不要通过计算坐标
使得代码还麻烦,尽量不要把break出去的处理,在里面各个分支处理好就可以了。

更简洁的就地双指针遍历版本如下:

char* trimspace(char* cstr)
{
    int cstrlen=strlen(cstr);
    int i=0,j=0,start;
    while(i<cstrlen)
    {
        //start=i;
        bool hasspace=false;
        while(i<cstrlen && cstr[i]==' ')
            i++, hasspace=true;

        if(hasspace==true)
            cstr[j]=' ',j++;
        if(i==cstrlen)
        {
            cstr[j]='\0';
            break;
        }
        while(i<cstrlen&& cstr[i]!=' ')
            cstr[j++]=cstr[i++];
        if(i==cstrlen)
            cstr[j]='\0';
    }
    return cstr;
}

Posted by richard爱闹 - 7月 21 2014
如需转载,请注明: 本文来自 Richard

c cpp convert, char char a

对于这一块,fawks大神毫无疑问已经到了一个境界了,我还是个学习的菜鸟。

我当时学的时候,就觉得里面的语法太复杂了,而且各种陷阱,一不小心就掉到坑里去了。。。

先来两个case:

char* a="fawks"
char a[]="fawks"

下面本文从几个方面来比较区别

  1. 两个的是前者a是字符串指针,后者a是字符数组。具体的来说,两者都会在常量区(常量区,堆,栈(通常说的堆栈), 全局\静态存储区)申请一个空间存放fawks这个字符串,
    然后前者在栈申请一个空间存放字符串指针a,*86 占4B,值为这块常量区的首地址,但是如果cout<<a的时候又会输出这个字符串,而不会输出a的地址,之前李青老师的书记得写过,这是
    C++的智能处理,包括输出字符数组首地址也是自动输出字符串内容。后者则是将常量区字符串copy到一个栈,然后首地址就是a的地址,这个是字符数组。并且要注意前者最好是写成

    const char*a =”fawks”

因为不能通过a来修改字符串所在常量区的值,所以const会比较安全,但是后面修改a的指向又是可以的。

  1. 后面继续赋值:

    a=”zrc”

这句话对于前者是可以的,因为把栈中指针a的值改为zrc的地址,a指向了常量区另一个区域zrc的首地址,而后者是不可以的,因为a已经固定下来了指向一个栈中fawks的首地址,字符数组只能初始化不能赋值。

  1. 另外还有一个区别要注意
    sizeof(a);

这句话对于前者输出4(*86),因为这个指针占得空间是4个字节,因为32bit地址,8bit占一个Byte,如果字节寻址的话(一般这么理解),所以占4个存储单元。而后者则是输出5,注意与strlen(a)的区别,
因为sizeof()是看占得内存空间大小,所以包含结束符\0(ASCII 0), strlen是字符串长度,不包含结束符。

4.所以如果函数返回一个char, 如果你定义的char a[]=””, 然后return a就会挂,虽然过程是先返回了a指向的地址,然后再结束函数,但是这块是栈空间,函数结束就销毁了这块栈,所以位置是野值。。。
而后者是字符串存在常量去,随着程序开始就在那直到程序结束,所以char
a =””, return a完全没问题,而且函数先返回,后销毁栈变量也保证了他的正确性,a是栈中的一个指针变量。

  1. 数组(char a[])可以转化为指针(char* a),指向字符串的指针,但是指针未必可以转为数组,因为如果指向字符串常量的话,那么就不能作为数组了,因为没法修改它们的值。

另外要注意字符串常量如果赋值(不是初始化)给char* 可能有警告,例如

char *s;
s="hello";

但是char* s=”hello” 是可以的,其实还是加个const安全些。附上

string str=(char*)s;
char *s=str.c_str();

上面一句可以,二句不可以的,因为c_str()返回const char ,不可以const char 赋给char*, 但是反过来可以,我付给一个保证不修改内容的指针的嘛

const char* s=(char*)cstr;

引用 http://www.zhihu.com/question/20779337
http://blog.csdn.net/hackbuteer1/article/details/6706562

Posted by richard爱闹 - 7月 21 2014
如需转载,请注明: 本文来自 Richard