今天带着疑问去看了看字符指针,就一直在想为什么输出一个指向字符的指针,它输出的不是地址,而是字符,结果挖到了一个宝藏博客,然后结合了自身的想法,摘抄了大部分内容,最后我也把我自己的疑问给解决了,外加再做了一点点补充,凑出今天收获,谢谢这位博主https://blog.csdn.net/yichu5074

一、

C语言中,为什么字符串可以赋值给字符指针变量

char *p,a=‘5’;
p=&a; //显然是正确的,
p=“abcd”; //但为什么也可以这样赋值??

问:一直理解不了为什么可以将字串常量赋值给字符指针变量,请各位指点!

答:

双引号做了3件事:
1.申请了空间(在常量区),存放了字符串
2. 在字符串尾加上了’/0’
3.返回地址

你这里就是 返回的地址 赋值给了 p

二、
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 <stdio.h> int main(){char *p = "hello";printf("%s",p);char a[10];a = "hello";return 0;}

error C2440: ‘=’ : cannot convert from ‘char [6]’ to ‘char [10]’

    There is no context in which this conversion is possible

看到这样的错误提示,你是否会想到把char a[10]改成char a[6]呢

试一下,

error C2106: ‘=’ : left operand must be l-value

运算符的左边应该是一个“左值”。所谓“左值”就是指在程序中占用内存空间、可以被修改的量,比如各种变量。

继续扩展问题:

在使用指针的时候,指针可以自增,而数组不能自增

编译器给数组分配了空间,数组a的地址就是一个常量了,让常量自增这肯定是不行的。

继续扩展:

在指针自增的时候,编译器会自动识别类型,比如指针是指向int型的,想获取下一个的地址时,指针直接p++就行了,不要多此一举的p+4了
特别需要注意的是,在void指针使用的时候,不能使用指针运算,应为void型编译器不能识别类型的长度(即指针所指对象的体积),p++这样就是不合法的,即不能进行数学运算,也不能使用*取值操作,想使用必须转换为其它的类型

三、

标题:对字符数组,字符指针,字符串常量

1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写
“abc”,那么编译器帮你存储的是"abc\0"

2."abc"是常量吗?答案是有时是,有时不是。

不是常量的情况:“abc"作为字符数组初始值的时候就不是,如
char str[] = “abc”;
因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc”,而又因为
字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
char str[3] = {‘a’,‘b’,‘c’};
又根据上面的总结1,所以char str[] = “abc”;的最终结果是
char str[4] = {‘a’,‘b’,‘c’,’\0’};
做一下扩展,如果char str[] = “abc”;是在函数内部写的话,那么这里
的"abc\0"因为不是常量,所以应该被放在栈上。

是常量的情况: 把"abc"赋给一个字符指针变量时,如
char* ptr = “abc”;
因为定义的是一个普通字符指针,并没有定义空间来存放"abc",所以编译器得帮我们找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器
最合适的选择。所以尽管ptr的类型不是const char*,并且ptr[0] = ‘x’;也能编译
通过,但是执行ptr[0] = ‘x’;就会发生运行时异常,因为这个语句试图去修改程序
常量区中的东西。
记得哪本书中曾经说过char* ptr = “abc”;这种写法原来在c++标准中是不允许的,
但是因为这种写法在c中实在是太多了,为了兼容c,不允许也得允许。虽然允许,
但是建议的写法应该是const char* ptr = “abc”;这样如果后面写ptr[0] = 'x’的
话编译器就不会让它编译通过,也就避免了上面说的运行时异常。
又扩展一下,如果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.根据上面的总结,来实战一下:
对于char str[] = “abcdef”;就有sizeof(str) == 7,因为str的类型是char[7],
也有sizeof(“abcdef”) == 7,因为"abcdef"的类型是const char[7]。
对于char ptr = “abcdef”;就有sizeof(ptr) == 4,因为ptr的类型是char
对于char str2[10] = “abcdef”;就有sizeof(str2) == 10,因为str2的类型是char[10]。
对于void func(char sa[100],int ia[20],char p);
就有sizeof(sa) == sizeof(ia) == sizeof§ == 4,
因为sa的类型是char
, ia的类型是int*,p的类型是char*。

四、

C语言中字符数组和字符串指针分析,该贴原址:http://www.cnblogs.com/gigikouyi/archive/2006/08/01/464737.html

这几天搞Unix上的C程序,里面用到了很多字符数组和字符串指针,我记得在学完C语言后相当一段时间里,对指针这个东西还是模模糊糊,后来工作也没怎么用到过C,虽然网上这类的文章也有很多,还是决定自己在这做个小总结,也算加深下自己的印象,写了下面的测试程序:

#include <stdio.h>int main(int argc, char *argv[])
{char day[15] = "abcdefghijklmn";char* strTmp = "opqrstuvwxyz";printf("&day is %x\n",&day);printf("&day[0] is %x\n",&day[0]);printf("day is %x\n",day);printf("\n&strTmp is %x\n",&strTmp);printf("&strTmp[0] is %x\n",&strTmp[0]);printf("strTmp is %x\n",strTmp);getchar(); return 0;
}

运行后屏幕上得到如下结果:

其实看到结果估计很多东西就好明白了,

先看看前三个输出也就是关于变量day的,在 char day[15] = “abcdefghijklmn”; 这个语句执行的时候,系统就分配了一段长15的内存,并把这段内存起名为day,里面的值为"abcdefghijklmn",如下图所示:

再看程序,第一个输出,&day,&号是地址运算符,也就是day这个变量的内存地址,很明显,在最前面,也就是a字符所在字节的地址;
对于第二个输出也就好理解了,&day[0],就是day数组中第一个变量(也就是a)的地址,因此他们两个是一样的;
第三个输出是day,对于数组变量,可以使用变量名来索引变量中的内容,其实这里的day可以理解成数组变量退化的指针,并且指向数组的开头,既然把它理解成指针,那么它的值肯定是地址了,所以他的值和上面两个也一样。

再看看后面三个输出,关于字符串指针strTmp,在执行char* strTmp = “opqrstuvwxyz”;后,内存的图示如下:
如图所示,内存分配了两段内存,一个名为strTmp,类型是一个字符指针,另外一段是一个字符串常量,且strTmp里面存放着字符常量的首地址,注意这里无法通过strTmp修改这段字符串,因为是常量;于是程序中的后面三个输出就好理解了;

&strTmp:strTmp这个字符指针的地址
&strTmp[0]:strTmp所指字符常量第一个字符的地址
strTmp:strTmp这个字符指针的值,即字符常量的首地址

因此,最后两个的值是一样的。
指针可以这样理解,指针这种类型,和int,char,double等等是一样的,只是它用来保存地址值的,而int变量保存整数,char变量保存字符,仅此而已,就char型指针或者int指针,本质是一样的,都是存放的地址,只不过那个地址所里面的变量类型不同而已,还有一种void型指针,就是可以放任何类型变量的地址。

五、个人代码以及注释,纯属个人理解,定有不妥之处,望批评指正:

#include <stdio.h>int main(int argc, char *argv[])
{char* strTmp = "abcd";printf("strTmp is %s\n",strTmp);//将字符串常量"abcd"的地址所隐含的内容转换成“string类型”printf("strTmp is %d\n",strTmp);//将字符串常量"abcd"的地址转换成int类型,这里不同的机子不同的时间的运行结果可能会不一样,因为地址可能会发生变化printf("strTmp is %c\n",strTmp);//将字符串常量"abcd"的地址转换成字符型,这里不同的机子不同的时间的运行结果可能会不一样,因为地址可能会发生变化printf("*strTmp is %c\n",*strTmp);//将字符串常量"abcd"的地址所隐含的内容转换成字符型,由下面注释的这句会抛出异常可知,这里并无截取字符串,*strTmp长度本身就是1//printf("*strTmp is %s\n",*strTmp);//不能将字符转换成字符串型getchar(); return 0;
}

六、后来又有看到下面这样的说法可供读者参考:

  1. C语言中没有字符串类型,只有用字符数组来表示。这和c++中string是有区别的,C++中string是可以直接赋值如string s;s=“Hello world”;但是C语言中的字符数组却不能这样。所以,这里的strTmp可以理解为字符数组的首地址,也可以用它代表整个字符数组,所以能输出所有字符数组中的内容。

2.字符串就是字符数组或者是指针。 内存实现都一样的。 数组名字就是一个指针。

char ch[100] ;
char *p;
p =ch;

3.定义的字符串方式举例:

字符串定义其实很简单在c/c++语言中定义一个字符串可以使用如下的语法:

char *s1=“string1”;//定义字符串常量,指针形式char s2[]=“string2”;//定义字符串常量,数组形式char *s3=new char[10];//定义字符串变量并分配内存 指针形式strcpy(s3,"string3");//为s3赋值char s4[10];//定义字符串变量,数组形式strcpy(s4,"string4");//为s4赋值

以上四种方法都能定义一个字符串,同时通过字符串在内存中的分布可以清楚地知道是什么情况

  1. C语言中字符串赋值方法strcpy(chard,chars)其中s代表是源字符串,d代表目标字符串,也就是你要赋值的字符串。

5.c语言中的字符串跟java或c++中的字符串不同。如char *p;其中p是一个指针,p中存储一个内存缓冲区的首地址。所谓的内存缓冲区就是一段连续的内存地址,里面存放了一系列的字符。那系统又是如何判断在哪里结束呢。那就是根据符号‘\0’。这个字符占一个字节,8位,每位的值都是0。

额外补充一点,在c++里面,用cout输出a,应该输出的是字符串的地址,但是为什么直接输出字符呢?
char * a=“biaobiao”
cout<<a;


查看了一下源代码后,哎呀,原来是操作符重载了呀,怪不得那么奇妙呢。。。

template <class _Traits>
basic_ostream<char, _Traits>& operator<<(basic_ostream<char, _Traits>& _Ostr,const char* _Val) { // insert NTBS into char streamusing _Elem = char;using _Myos = basic_ostream<_Elem, _Traits>;ios_base::iostate _State = ios_base::goodbit;streamsize _Count        = static_cast<streamsize>(_Traits::length(_Val));streamsize _Pad          = _Ostr.width() <= 0 || _Ostr.width() <= _Count ? 0 : _Ostr.width() - _Count;const typename _Myos::sentry _Ok(_Ostr);if (!_Ok) {_State |= ios_base::badbit;} else { // state okay, insert_TRY_IO_BEGINif ((_Ostr.flags() & ios_base::adjustfield) != ios_base::left) {for (; 0 < _Pad; --_Pad) { // pad on leftif (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) {_State |= ios_base::badbit; // insertion failed, quitbreak;}}}if (_State == ios_base::goodbit && _Ostr.rdbuf()->sputn(_Val, _Count) != _Count) {_State |= ios_base::badbit;}if (_State == ios_base::goodbit) {for (; 0 < _Pad; --_Pad) { // pad on rightif (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) {_State |= ios_base::badbit; // insertion failed, quitbreak;}}}_Ostr.width(0);_CATCH_IO_(ios_base, _Ostr)}_Ostr.setstate(_State);return _Ostr;
}

要想让它不被重载的话,简单呀,直接破坏后面的参数不就得了嘛,你说是不是呀???

cout方法使用字符串中的终止空字符来确定何时停止显示字符。
对于其他类型的指针,C++将其对应于 void *,并打印地址的数值表示。
所以如果想要获得字符串的地址,必须将其强制转换为 void * 类型或 int * 类型

cout << (void *)a << endl;

2020-11-30(为什么字符串可以赋值给字符指针变量)相关推荐

  1. 为什么字符串可以赋值给字符指针变量

    本文系转载,对原文的主要几点进行了提炼,想看更详细的内容,请看原帖: https://blog.csdn.net/yichu5074/article/details/81701850?utm_sour ...

  2. 实验室每日一题 2020.11.30

    实验室每日一题 2020.11.30 先打开没有加密的文本文档,里面有一串密文,根据结尾的+推测应该是XXencode,直接找个在线网站解密,又得到一串密文:fwilvyhublqwhuhvwlqj, ...

  3. 《nature》2020.11.30期,重症COVID-19的主要遗传危险因素来自尼安德特人

    <nature>最新2020.11.30期快报! 1.重症COVID-19的主要遗传危险因素来自尼安德特人 严重COVID-19的风险是由一个遗传自尼安德特人的基因组片段引起的,南亚和欧洲 ...

  4. latex 插入表格_【2020.11.30】IEEE trans英文latex写作心得和学习历程

    安装:texstudio+texlive(时间较长的是texlive,估计要1小时左右,建议安装的时候放在一边吃个饭回来就差不多) https://www.cnblogs.com/libbin/p/1 ...

  5. 【2020.11.30提高组模拟】剪辣椒(chilli)

    剪辣椒(chilli) 题目描述 在花园里劳累了一上午之后,你决定用自己种的干辣椒奖励自己. 你有n个辣椒,这些辣椒用n-1条绳子连接在一起,任意两个辣椒通过用若干个绳子相连,即形成一棵树. 你决定分 ...

  6. 字符串赋值给字符指针

    今天在看书中例题时看到了如下代码: #include <iostream> using namespace std;int main() {char *p = NULL;p = " ...

  7. C语言 将字符串赋值给字符指针

    #include <stdio.h> int main(void){char *s="hello";printf("输出字符:%c \n", *s) ...

  8. keil c语言字符串赋值,keil c指针变量赋值 请指点下迷津

    这个是我在多路温湿度控制系统中用到的接收方法,Rs485方式的 如果想采用Rs485的朋友也可以参考一下 #define FRAME_HEAD          0x24 uchar RxBuf[7] ...

  9. c语言中不能将字符串赋值给字符数组

    参考文章:c语言中,为什么字符串可以赋值给字符指针变量 1.可以将字符串赋值给指向字符的指针p,而不能将字符串赋值给一个字符数组. char a[10]="hello";//正确的 ...

最新文章

  1. docker WARNING: bridge-nf-call-iptables is disabled 处理
  2. SAP-SAP预制凭证相关的表
  3. SQL语言之DQL语言学习(十一)分页查询
  4. Git之签署工具GPG的安装和使用
  5. httpclient base64 文件上传_文件上传下载
  6. C语言试题五十七之假定输入的字符串中只包含字母和*号。请编写函数function,它的功能是:删除字符串中所有*号。在编写函数时,不得使用c语言提供的字符串函数。
  7. php server 性能,PHP中:$_SERVER[‘REQUEST_TIME’]和 time()有什么区别,那个性能快!...
  8. D3 interpolate
  9. JSK-27321 统计单词数【字符串】
  10. 为Web 开发者Bug 报告和跟踪工具
  11. Python-基本语法元素
  12. 【资料分享】《建筑照明设计标准》(GB50034-2013)
  13. 创建VSIX项目模板
  14. OCP 创建可插拔数据库PDB
  15. QQ文件路径,QQ图片保存地址
  16. 28张高清数据分析全知识地图,强烈建议收藏
  17. 如何使用spring2.0
  18. YBT高效进阶3.4.2 洛谷P2341 POJ2186受欢迎的牛Popular Cows
  19. 【BirdDog】 Full NDI在体育/电竞直播中的降成本、轻量化、多机位现场制作
  20. 团队RESTful 风格API规范

热门文章

  1. Python之woe:woe库的简介、安装、使用方法之详细攻略
  2. 成功解决ValueError: Cannot feed value of shape (80, 15, 1, 1) for Tensor 'Placeholder_1:0', which has sh
  3. Matlab之DNN:基于Matlab利用神经网络模型(epochs=10000000)预测勒布朗詹姆斯的2018年总决赛(骑士VS勇士)第一场得分、篮板、助攻
  4. 基于DataTables实现根据每个用户动态显示隐藏列,可排序
  5. 【每天一个linux命令】read
  6. EasyUI中datagrid的行编辑模式中,找到特定的Editor,并为其添加事件
  7. DA14580做主机
  8. JavaScript定时器原理及高级使用
  9. 特征工程(3):特征选择—信息增益
  10. 波卡链Substrate (7)Babe协议五“Epoch纪元更新”