C++指针和数组以及字符串常量的类型

指针和数组名

参考:《c专家编程》

程序中所有的变量名或者数组名在重定位之后都只是一个地址值而已,所有引用这些变量的地方都会被替换成一个地址值。

  • 数组名仅仅是一个标号而已,没有额外的内存存储这个值,我们应当把一个数组看作是一个含有多个重复类型元素的struct。数组名就是这个struct的名字,它仅仅是一个label,声明在函数内的数组空间是在栈上,这时候的数组名就是一个地址(指向栈)。这也是为什么数组名不能被赋值的原因(数组名是右值)。经过重定位之后,数组名已经完全是地址值了,程序中对a访问就等价于访问a的地址,而对a[0]的访问等价于对a指向的内存单元第0个元素进行访问
  • 指针本身占用额外的空间,这个空间存储的是某个变量的地址,同时指针变量本身也是一个标号,这个标号的值存在符号表中。存储指针变量是的内存空间,这个空间被修改就以为值指针指向的地方被更改了
char a [] = "shakalaka";
a = 0x1111cc;//非法
char * p = a;//定义了一个指针变量,这个指针变量占据8个字节的空间(64位的系统)并且使用a的值初始化p。p也可以指向其他地址
p = 0x74cc;//合法
  • a[1]的方式访问数据过程
    • 把1和a的值相加(在链接和重定位之后,所有引用a的地方都是一个地址值)
    • 将得到的值作为地址取出该地址所存储的内容
  • p[1]的方式访问数据过程(程序重定位之后p也是一个地址值)
    • 根据p这个符号的值定位p这个变量所在的地址
    • 从p所在的地址取出的值和1相加
    • 将得到的值作为地址取出该地址所存储的内容
  • 从上面可以看出来使用指针的方式引用内容会多出取地址这一步。有点类似间接寻址的味道

##定义成数组,声明成指针

extern char * p;//some.h

char p[] = "abcdef" ;// private.c

//some.c
#include"some.h"
p[1]='s';

这时候,对p[1]的解释方法和声明时的类型保持一致,即间接的形式

  • 获取p这个符号的值作为地址
  • 从该地址取出的值(即字符’a’ 对应十进制值97)和1相加得到98(16进制0x62)
  • 将得到的值作为地址取出该地址所存储的内容(地址0x62的内容)

显然,这不是我们期待的

const char *和字面字符串常量

const char* a = "hello";//c/c++都能编译成功
char *na = "hello";//c编译成功,c++产生warning
char b[] = "hello";//c/c++都能编译通过
a[1] = 'a';//语法上是正确的,但是会报错
b[0]='a';//正确执行
"blingbling"[0] = 'a';//错误
  • 字面字符串是数组不是指针:在c中一个字面字符串常量的类型是char[]类型的,之所以定义成数组类型而不是char *

    • 可以使用sizeof()操作符取得字符串常量的长度
    • 为了兼容函数传递,
    void foo(char a[]);
    void bar(char *a);
    
    int main(){
      foo("literal");//编译通过, 数组传值给数组
      bar("literal");//编译通过, 数组名传值给指针
     	//如果字面字符串常量是指针类型的
      {
        foo("fail");//编译错误
        bar("quit");//编译通过
      }
    }
    
    • c++指针版本的声明中必须要加上修饰符号const(否则产生warining)
    char *text = "test";//c可以编译通过,c++会产生warning
    
  • 因此,下面两种定义方式都能编译成功,但是背后原理不一样

char a[] = "abcdr";// 编译器开开辟一块空间将其初始化,相当于调用拷贝构造函数之后a是一个可以修改的数组

char * b = "yes";// b是一个指向rodata的指针!!!在c++中会报错的
  • 显然字面字符串常量是左值!!不是右值!!
  • c++把"hello"这种常量叫做string constant
void foo(char a[]){
  a[0] = 's';
}
void foo_const(const char a[]){
	
}
}
int main(){
 	const char a[] = "Iloveeating";
  foo("Iamathief");//c编译通过,允许const char[]传值给char*
  foo(a);//c++编译报错
  foo_const("I need money");//都编译通过,const char[]传值给const char*
}
  • 总之,如果想使用字面字符串常量而且之后不改变它,一定要加上const~,如果只是想初始化一个字符数组,用正常的char[]即可

cout接收char * 默认输出字符串而不是地址

const char * a = "I love China";

cout<<a;//会输出I love China而不是a的地址

cout<<(void*)a;//输出a的地址
cout<<staitc_cast<void*>(a);//c++ stytle
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值