c/c++中字符串若干问题总结

在c++程序中字符串大都使用string标准库,所以自己忽略了c风格字符串的一些用法。虽然c风格的字符串使用起来不太方便,但还是有必要搞懂,以便兼容一些旧的代码。string标准库不再介绍,使用起来非常便捷。本文主要总结c语言风格的字符串 char[]和char* 中自己以前理解不到位的地方,特此记录与大家分享交流。

第一个问题,也是最早开始看c++时候的疑问

数组类型的对象是使用一个指向该数组首元素的指针。那么数组变量的名字应该代表的是一个指针,为什么字符数组用cout输出时得到的结果是字符串呢?如:

int a[] = { 1,2,3 };
char c[] = "test";
cout << a << endl;
cout << c << endl;

输出结果如下:

你可能会想为什么第一个输出地址,第二个输出的是字符串呢?

通过查阅资料发现,C++标准库中I/O类对输出操作符<<重载,在遇到字符型指针时会将其当做字符串名来处理,输出指针所指的字符串。

第二个问题:

c++中的字符串常量为什么可以赋值给char*? 比如char*s="test";其中的"test"不是const char*类型吗?为什么可以给char*赋值?在C中,字符串文字的类型是char[],根据类型它不是const,C中"test"类型是char[5],所以允许char* str="hello";
C++中"test"的类型是const char[5],不过为了兼容C代码做了特殊处理,所以也允许赋值给char*指针。虽然编译可以通过但是最好加上const,顺便提一下string转char*,注意别忘了加上const

string s = "hello word";
const char *str = s.c_str();

第三个问题,也是重点要提到的问题,char[]和char*的异同点。

首先看一段代码

char s1[] = "wang";
char s2[] = "wang";
const char *s3 = "wang";
const char *s4 = "wang";
cout << (s1 == s2) << endl;
cout << (s3 == s4) << endl;

结果输出如下所示:

为什么会是这样的输出结果呢?我们需要从程序内存分配的角度来解释

一般而言内存中的数据区可分为:

  1. 静态数据区: 在编译器进行编译的时候就为该变量分配的内存,即全局变量和静态变量(用static声明的变量),存放在这个区的数据程序全部执行结束后系统自动释放,声明周期贯穿于整个程序执行过程。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss)。
  2. 堆区:这部分存储空间完全由程序员自己负责管理,它的分配和释放都由程序员自己负责。这个区是唯一一个可以由程序员自己决定变量生存 期的区间。可以用malloc,new申请对内存,并通过free和delete释放空间。如果程序员自己在堆区申请了空间,又忘记将这片内存释放掉,就 会造成内存泄露的问题,导致后面一直无法访问这片存储区域。但程序退出后,系统自动回收资源。分配方式倒是类似于链表。
  3. 栈区:存放函数的形式参数和局部变量,由编译器分配和自动释放,函数执行完后,局部变量和形参占用的空间会自动被释放。效率比较高,但是分配的容量很有限。
  4. 常量区: 存放常量的区间,如字符串常量等,注意在常量区存放的数据一旦经初始化后就不能被修改。 程序结束后由系统释放。

下面以一个例子来说明问题:

//main.cpp  
int a = 0; //全局初始化区
char *p1;// 全局未初始化区
main()
{
    int b; //栈
    char s[] = "wang"; //栈
    char *p2; //栈
    char *p = "wang"; //"wang\0"在常量区,p3在栈上。
    static int c = 0;// 全局(静态)初
}

因此p指向的字符串是位于常量区的,该处的数据是不能修改的(只能读取),比如 p[0]='s',这种赋值是不允许的,但是s存储在栈中,s[0]='s'的操作是完全可以的。

下面看下char*与char[]初始化的步骤:

比如 char *p="wang",首先需要声明一个char*变量,在内存的常量区开辟一个空间存储字符串常量,返回这个区域的地址作为值赋给这个字符指针(注意如果此时再执行char *c="wang",则系统不会开辟新的空间,这也是为什么上面那个程序s3==s4是true的原因)。

char s[]="wang",首先声明一个char数组,然后为该数组赋值,即将每一个字符分别赋值给数组的每一个元素,存储在栈中。因此即便是定义两个相同的字符数组,它们的地址也是不同的,回到上面的程序也就是(s1==s2)为false的原因了。

接下来用接下来的两个图片来说明char s[]和char*p在内存中的分配情况:

在这里需要特别强调一下 :


s本身就是一个字符数组,由之前数组的概念可知,s=&s=&s[0]。

p是一个内存单元,里面存有一个地址,这个地址就是字符串的地址。对p取地址&p,意味着得到了存有字符串指针的内存单元的地址,而不是字符串的地址,这个不要搞混了。c++中要想输出字符串的地址需要进行一个void*类型的转换。


	int main()
	{
	  char *s = "test";
	  cout << s << endl//这个是字符串"string"也是第一个字符's'的地址,但输出的是字符串
          cout<< *s << endl//这个是取得a指向的位置的值,即字符's',输出是s
	  cout<< &s << endl//这个是a的地址,即指针的地址        
          cout<< (void*)s << endl;//这个是输出a的值,即's'和"string"的地址
	  return 0;
	}

程序输出如下:

现在再回到原始的问题:

char s1[] = "wang";
char s2[] = "wang";
const char *s3 = "wang";
const char *s4 = "wang";
cout << (s1 == s2) << endl;
cout << (s3 == s4) << endl;
cout << &s1 << endl;//字符数组首元素的地址
cout << &s2 << endl; //字符数组首元素的地址
cout << &s3 << endl; //保存指向字符串常量的指针的地址,并不是字符串常量的地址
cout << &s4 << endl;//同上
cout << (void*)s1<< endl;//同&s1
cout << (void*)s2 << endl;//同&s2
cout << (void*)s3 << endl;//真正字符串常量的地址
cout << (void*)s4 << endl;//真正字符串常量的地址

结果如下所示:

这下看起来就比较明显了,s1并不等于s2,但是s3等于s4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值