C++中char *和char []的区别

本文详细探讨了C++中char*和char[]的区别,涉及内存分配、常量存储区、栈和堆的使用。通过实例代码分析了两者的不同,包括它们在内存中的布局和访问效率。同时,文章还讲解了C++内存的五个分区,并举例说明了结构体中不同类型成员的内存占用情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++中char *和char []的区别

例如如下代码:

#include<iostream>
using namespace std;
int main()
{
    char *p1 = "abcd";
    char p2[] = "1234";
    return 0;
}

这二者的区别还在于:

  1. p1是一个指针变量,有一块内存存储它,它的内容是字符串的地址,那么我们要访问字符串就先要取出p1中存储的地址,然后计算偏移量,进行访问
  2. 不同于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更加补充说明了这一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值