C++中char *和char []的区别
例如如下代码:
#include<iostream>
using namespace std;
int main()
{
char *p1 = "abcd";
char p2[] = "1234";
return 0;
}
这二者的区别还在于:
- p1是一个指针变量,有一块内存存储它,它的内容是字符串的地址,那么我们要访问字符串就先要取出p1中存储的地址,然后计算偏移量,进行访问
- 不同于p1,p2直接是字符串的地址,直接访问就行了
“abcd”是文字常量区分配了内存存储的,栈上分配一地址给p1并指向“abcd”,那么如果在后面的代码中改变了“abcd”,自然崩溃。所以,需要加上const限定
但是说到底,为什么改变p1就是危险的,字符数组的内容就能随意改变呢?这是因为“abcd”是在编译时刻就确定的,而“1234”是在运行时刻赋值的。所以,编译器在编译时就已经知道p1指向的是常量,他并不希望你改变,但是数组不同,可以说他只是个存储的工具,编译器编译时并不知道它里面是什么。
但在往后的存取中,在栈上的数组比指针所指向的字符串是要快的。
还网上找到如下代码,很详细
int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[]="abc"; //栈
char *p2; //栈
char *p3="123456"; //123456\0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p3,"123456"); //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
}
#include<iostream>
using namespace std;
int main()
{
char *p1 = "abcd";
char p2[] = "1234";
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
printf("p1:0x%p", p1);
cout << (str1 == str2) << endl;
cout << (str3 == str4) << endl;
cout << (str5 == str6) << endl;
cout << (str7 == str8) << endl;
return 0;
}
c++内存被分为5个区,分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。现在来分析一下所以数据的内存分布:
分析一
char str1[] = "abc";
char str2[] = "abc";
这里的"abc"是一个常量,首先会在常量存储区里存储"abc"这个常量,然后会因为"abc"被赋值给str1[],所以在栈中开辟一段内存,内存大小为4个节点(char数组后会自动加一个’\0’),然后又有一个"abc"被保存在栈中。
同理,str2[]中的"abc"也是保存在栈中,地址不同。
到此,有三个"abc"被保存起来,一个在常量存储区,另外两个在栈中。
str1:0x0x7ffea5a7c543
str2:0x0x7ffea5a7c547
分析二
const char str3[] = "abc";
const char str4[] = "abc";
str3:0x0x7ffea5a7c54b
str4:0x0x7ffea5a7c54f
对于这种被const修饰起来的变量,一般也是被保存在常量存储区,但是,但是对于const数组来讲,系统不确定符号表是否有足够的空间来存放const数组,所以还是为const数组分配内存的。所以str3指向的是栈上的"abc"。
同理,str4[]也是保存在栈中,地址不同。
分析三
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
str5:0x0x7fc215ec100a
str6:0x0x7fc215ec100a
str7:0x0x7fc215ec100a
str8:0x0x7fc215ec100a
因为"abc"在常量存储区中保存有一份(即使没保存,这样的操作也会新建一份),这里str5定义的时候,str5就直接指向"abc"所在的常量区的地址。同理str6,str7,str8。与const没有半毛钱关系,const只是使得str5和str6无法指向新的字符串常量(也就是新的地址)。
分析四
__attribute ((packed)) 是为了强制不进行4字节对齐,这样比较容易说明问题。
struct tag {
int a;
char *p1;
}__attribute ((packed));
struct tag1 {
int a;
char p1[];
}__attribute ((packed));
printf("int-sltren:%d\n", sizeof(int));
printf("tag-sltren:%d\n", sizeof(tag));
printf("tag1-sltren:%d\n", sizeof(tag1));
char *-sltren:8
int-sltren:4
tag-sltren:12
tag1-sltren:4
#include<iostream>
using namespace std;
struct tag {
int a;
char *p1;
};
struct tag1 {
int a;
char p1[];
};
int main()
{
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
printf("char *-sltren:%d\n", sizeof(char *));
printf("int-sltren:%d\n", sizeof(int));
printf("tag-sltren:%d\n", sizeof(tag));
printf("tag1-sltren:%d\n", sizeof(tag1));
cout << (str1 == str2) << endl;
cout << (str3 == str4) << endl;
cout << (str5 == str6) << endl;
cout << (str7 == str8) << endl;
return 0;
}
char *-sltren:8
int-sltren:4
tag-sltren:16
tag1-sltren:4
分析五
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct tag1
{
int a;
int b;
}__attribute ((packed));
struct tag2
{
int a;
int b;
char *c;
}__attribute ((packed));
struct tag3
{
int a;
int b;
char c[0];
}__attribute ((packed));
struct tag4
{
int a;
int b;
char c[1];
}__attribute ((packed));
int main()
{
struct tag2 l_tag2;
struct tag3 l_tag3;
struct tag4 l_tag4;
memset(&l_tag2,0,sizeof(struct tag2));
memset(&l_tag3,0,sizeof(struct tag3));
memset(&l_tag4,0,sizeof(struct tag4));
printf("size of tag1 = %d\n",sizeof(struct tag1));
printf("size of tag2 = %d\n",sizeof(struct tag2));
printf("size of tag3 = %d\n",sizeof(struct tag3));
printf("size of tag4 = %d\n",sizeof(struct tag4));
printf("l_tag2 = %p,&l_tag2.c = %p,l_tag2.c = %p\n",&l_tag2,&l_tag2.c,l_tag2.c);
printf("l_tag3 = %p,l_tag3.c = %p\n",&l_tag3,l_tag3.c);
printf("l_tag4 = %p,l_tag4.c = %p\n",&l_tag4,l_tag4.c);
return 0;
}
size of tag1 = 8
size of tag2 = 16
size of tag3 = 8
size of tag4 = 9
l_tag2 = 0x7ffdd3e11450,&l_tag2.c = 0x7ffdd3e11458,l_tag2.c = (nil)vivi
l_tag3 = 0x7ffdd3e11467,l_tag3.c = 0x7ffdd3e1146f
l_tag4 = 0x7ffdd3e1146f,l_tag4.c = 0x7ffdd3e11477
说明在结构体里面, char* buf 和char buf[1]的效果差不多(这个是在对齐的情况下,不对齐的话也是不一样的),占4个字节;char buf[0] 和char buf[]是一样的,不占内存。
从上面程序和运行结果可以看出:tag1本身包括两个32位整数,所以占了8个字节的空间。tag2包括了两个32位的整数,外加一个char *的指针,所以占了12个字节。tag3才是真正看出char c[0]和char *c的区别,char c[0]中的c并不是指针,是一个偏移量,这个偏移量指向的是a、b后面紧接着的空间,所以它其实并不占用任何空间。tag4更加补充说明了这一点。