char数组和指针问题

这个问题是C++基础问题中相当折腾人的一个,死记硬背解决不了根本问题,记住还是要忘,需要仔细研究其本质。

这两种方式就是数组和指针的方式:

1
2
char a[6] = "abcde";
char *b = "abcde";

第一行声明了并初始化了一个char数组,第二行是声明char指针b,指向了常量字符串。其中a是数组的首地址,a和b的地址一定不同。

千万不能说数组名是指针,可以用sizeof来否定:

1
2
char a[]="abcde";
cout<<sizeof(a)<<endl;

如果a是个指针,那么结果是4,但结果是6.

数组不能被直接复制,所以当数组名作为函数参数的时候,要么就是数组的引用,要么就是指向第一个元素的指针,他们的值是相等的。当你对一个数组做&的时候,他提取的是指向数组的指针,然后仍然可以隐式转换成指向第一个元素的指针,而且它们的值是相等的。

这样的代码是错的:

1
2
char a[6] = "abcde";
a[6] = "asdfge";

只有声明里才能用a[6],这就好比int a[6]={1,2,3,4,5,6};,但不能再用a[6]={5,6,7,8,9,0};。应该用a[0]='A';

这样的代码是正确的:

1
2
3
char a[]="abcde";
char *b;
b = a;

b是指针变量,指向了数组的首地址。

这样是错的:

1
2
3
char *a="abcde";  或者  char a[]="abcde";
char b[6];
b = a;

实际上是上一种情况的相反,报错error: incompatible types in assignment of 'char*' to 'char [6]'因为不存在一个隐式转换使得 char 被转换成 char[]。这个问题比较关键,我们可以把数组名b理解成一个常量指针,它不能指向其他地址,但指向的字符串可以改变。但是注意只是这么理解而已,数组名并不真的是常量指针。同样的,b++;也是错的。
从另一个角度来看,*数组名做函数参数时会退化为指针
,这里没有退化为指针的条件,所以b不能当指针变量用。

这样也是错的:

1
2
char a[6];
a = "abcde";

a是数组的首地址,怎么把字符串常量赋给它?

再看这种情况:

1
2
3
char *a, *b;
a = "abcde";
b = "abcde";

a和b的值不一定相同,也就是不一定是同一个地址,这取决于编译器的行为。

对于char指针和数组,以下操作都是可行的。

1
2
3
4
5
6
7
8
9
10
11
const char* p="abcd";	//在常量区,应当加const,否则编译器会报警
// char p[]="abcd"; // 在stack
cout<<p<<endl; // abcd
cout<<&(*p)<<endl; //abcd
cout<<*p<<endl; // a
cout<<&p<<endl; // 0x62fe9c

cout<<p+2<<endl; //cd
cout<<*(p+2)<<endl; //c
cout<<*p+2<<endl; //99
cout<<p[2]<<endl; //c

但下面操作仅适用于char数组,不能用于指针,指针指向的是常量:

1
//p[0]='A';

对于数组char p[]="abcd";,可以使用p[2],这是因为数组名在这里退化为指针,p转为指向数组首元素的char*类型。也就是说指针本身就可以用[],反而是数组名需要先转换为指针才能用[]。看下面的例子:
1
2
3
4
5
6
int b[5] = {1,2,3,4,5};
int *f=b;
cout<<f[2]<<endl; // 3
f[2]=0;
cout<<f[2]<<endl; // 0
cout<<b[2]<<endl; // 0

对于常量字符串,都可以用数组下标,这种做法比较少见:

1
cout<<"abcd"[2];     //c