字符串赋值给字符指针(char *a="hello")的正确理解方式
对于语句 char *a="hello";
对于这个声明方式,会造成的误解是:声明了一个字符指针(它会指向一个位置),将“字符串”赋值给 指针表达式"*a"所指向的地址。但正解是:声明了一个字符指针后,并用字符串常量的第一个字符的地址赋值给指针变量a。
即正确顺序是:
- 1.分配内存给字符指针;
- 2.分配内存给字符串;
- 3.将字符串首地址赋值给字符指针;
这里有两点需要考虑清楚的地方:
①*a只是指向一个字符。举例如下:
#include <iostream>
#include <string>
using namespace std;
int main() {
char *a ="abcdefg";
cout << "输出字符: " << *a << endl;
cout << "第二次输出字符: " << *(a+1) << endl;
cout << "输出字符串: " << a << endl;
return 0;
}
结果如下
输出字符: a
第二次输出字符: b
输出字符串: abcdefg
② 若字符串常量出现在在表达式中,代表的值为该字符串常量的第一个字符的地址。所以”hello”仅仅代表的是其地址。
原声明方式相当于以下声明方式:
char *a;
a="hello";/*这里字符串"hello"仅仅代表其第一个字符的地址*/
1.但还是不明白为什么字符串可以赋值给字符指针变量
char *p,a='5';
p=&a; //显然是正确的,
p="abcd"; //但为什么也可以这样赋值??
双引号做了3件事:
1.申请了空间(在常量区),存放了字符串
2. 在字符串尾加上了'/0'
3.返回地址
返回的地址,赋值给了指针变量p
2.以char *p = “hello”为例,把p换成数组,然后再赋值就不行了
字符串常量"hello"出现在一个表达式中时,"hello"表达式使用的值就是这些字符所存储的地址(在常量区),而不是这些字符本身。
所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。
char a[10] = “hello”; //这样可以,这种情况是c语言初始化所支持的
如果写成char a[10]
然后 a = “hello” 这样就错误了。
因为同样是a数组,char a[10] = “hello”;这种是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理
但是换成char a [10],然后a = “hello”是不行的,“hello”赋值的值是一个地址,而a虽然也有地址,与指针不同,指针的值是地址,而数组的值虽然同为地址,却是一个常量,不能给常量赋值
代码测试
#include <iostream>
#include <string>
using namespace std;
int main() {
char *p = "hello";
cout << p << endl;
char a[10];
a = "hello";
return 0;
}
报错 error C3863: array type 'char [10]' is not assignable
而修改后,正常运行
#include <iostream>
#include <string>
using namespace std;
int main() {
char *p = "hello";
cout << p << endl;
char a[10] = "hello"; //数组初始化
return 0;
}
字符数组,字符指针,字符串常量 知识回顾
1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写 "abc",那么编译器帮你存储的是"abc\0"
2."abc"是常量吗?
1.当作为字符数组初始值的时候,"abc"不是常量
char str[] = "abc";
因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
char str[4] = {'a','b','c','\0'};
2.当赋值给 字符指针变量的时候:如char* ptr = "abc" 此时是常量
char* ptr = "abc" //规范不允许这种c的写法
规范写法
const char* ptr = "abc";
因为定义的是一个普通字符指针,并没有定义空间来存放"abc",所以编译器得帮我们 找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器 最合适的选择。简言之,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc\0"被 放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr(指针)是被放在栈上的, 只不过是它所指向的东西(值)被放在常量区罢了
3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的
如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4], 也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的
4.字符串常量的类型可以理解为相应字符常量数组的类型
如"abcdef"的类型可以看成是const char[7]
5.sizeof是用来求类型的字节数的。
如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4,因为sizeof(a)其实就是sizeof(type of a)
6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通 的指针类型
void func(char sa[100],int ia[20],char *p) 则sa的类型为char* ia的类型为int* p的类型为char*
7.根据上面的总结,来实战一下
1)对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7] str本身可变
2)也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7] 字符串常量
3)对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*,即
#include <iostream>
#include <string>
using namespace std;
int main() { char *p = "hello"; cout << sizeof(p) << endl; //4 cout << sizeof(char *) << endl; //4 return 0; }
4)对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]
5)对于void func(char sa[100],int ia[20],char *p);
sizeof(sa) == sizeof(ia) == sizeof(p) == 4,
因为前面有说过编译器把数组类型的书写的形参,解释为普通的指针类型
sa的类型是char*, ia的类型是int*,p的类型是char*
对于C/C++中的 字符指针和字符数组,总是在碰到的时候无法确定而不得不现场测试,来确定末尾是否包含'\0',函数到底如何使用等等。真是劳民伤财,现在总结一下:
字符指针的赋值
(1)指向一个字符串常量
char *src = "abcded"; //这种方式由系统自动给该字符指针指定了一个内存中的位置,并且该位置的字符数组为{'a', 'b', 'c', 'd', 'e', 'd', '\0'};
如果此时再次对 src赋值,src = "mmmt", 则src指向另外一个由系统指定的内存块(由"mmmt"本身返回)。这种方式赋值的src为一个指向字符串常量指针,不能对src指向的位置的内容做改变的操作,即不能执行 *src = 'a', *(src+1) = 't' 等操作;但是可以改变src指向的位置,即像之前的 src = "mmmt";
(2)指向一个字符数组
char tmp[4] = {'a', 'c', 'e', 'f'};
char* src = tmp;
(3)使用 new,然后可以像字符数组一样赋值,即指向一个字符数组
char* src = new char[10]; //这种方式由程序在堆内存上开辟了一个数组,并将地址赋值给src
字符串常量和字符数组比较
(1)字符串常量由系统自动分配一个内存区域,且该区域中的内容不能改变(即无法通过指向该字符串的指针进行修改);
(2)字符数组或者为系统自动分配的全局数据区或栈上的内存,或者通过new操作来分配的堆上的内存,字符数组中的内容可变(即可以通过指向该字符数组的指针进行修改)。
(3)字符数组中不默认含有'\0',除非明确赋值,而字符串常量在末尾自动含有 '\0'.
strcpy的使用
(1)用strcpy时候, 如果源字符串是一个字符指针,则没有问题,因为字符指针自动带'\0',在'\0'位置复制结束;
而如果源是一个字符数组(即将字符数组转换为字符指针来使用),则将会从字符数组的首地址开始复制,如果字符数组中明确指定了'\0'元素,则会在'\0'处停止,而若没有'\0'元素,则程序可能会不停的复制,直到在程序的内存中碰到'\0',这样可能会得到不希望的结果。