BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #39440同步于 2010/5/20
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

[自娱自乐]C/C++中的字符串、字符型数组和字符型数据指针

DaiGakuSei
2010/5/20镜像同步13 回复
老话题了,嗯。 先看这两个东东 char *str1 = "fucking world!" char str2[] = "fucking world!"; 它俩有啥区别捏? 区别1:str1是个指向字符型数据的指针,若用sizeof(str1)测长,得到4(指针数据在内存中占据的字节数),str2是个字符型的数组变量,str2[]表示这个数组按照数据长度在定义时分配内存空间。它存储的是一个字符串数据"fucking world!"(不是常量),每个字符存在str2的一个单元,哦,别忘了还有结尾处的'\0'符号。用sizeof(str2)测长得到整个字符串的占据的字节数15。 区别2:str1指针指向的是一个字符串常量,存于内存静态区(又叫全局区)。str2数组装载着一个15个字符组成的字符串变量,若是局部变量,则存于内存栈区;若是全局变量,则存于内存静态区。 字符串常量是不可以用str1[1] = 'A'这样的方式赋值的哟。我们的谭浩强老前辈就在《C程序设计(第二版)》中犯过这样的错,被人揪住了小辫子~但str2[1]='A'可以,因为str2是字符型的数组变量 很简单,是吧~ 那么再想想这两个函数 #include <iostream> using namespace std; char* CreateString_1() { char* str = "fucking world!"; return str; } char* CreateString_2() { char str[] = "fucking world!";//注意这么写会出错 return str; } void main() { char* str1 = CreateString_1(); char* str2 = CreateString_2(); cout<<str1<<endl; cout<<str2<<endl; } 程序正常运行,但输出结果出乎预料 fucking world! 操操操操操操操操操操操操操操操操操操操操 呃,乱码……为啥呢?因为在CreateString_2()中,char str[] = "fucking world!";是个数组变量的声明。而函数中的局部变量在函数结束时是会被程序自动从内存栈区删除的。return回来的仅仅是指向str数组首地址的指针(PS:数组名在当作数据使用时会被编译器认为是指针数据),数组中的数据呢,灰走啦!因此在输出时自然会出现未可预料的数据。 而CreateString_1()中char *str1 = "fucking world!";声明的是一个指向数组型数据的指针,这个指针指向的是内存静态区的字符串常量"fucking world!"。常量的生命周期就是整个程序的生命周期。因此它自然不会被程序自动释放掉。 哦,顺便说下。字符串的初始化 char str[10] = "hello";//定义时赋值 char str[] = "hello"; char a[10]; a[10] = {'h','e','l','l','o','\0'};//别忘了'\0',否则只是存了5个字符的数组 //警告:以下方式赋值方式错误!(变量指针指向常量) char a[10]; a = "hello"; 利用strcpy char a[10]; strcpy(a, "hello"); 还要说说 printf和cout对字符型数据指针的处理。 str[] = "hello world!"; printf(str);这样写是不会错的,因为printf的参数为printf(const char*,...),const char* 则是要输出(到I/O终端)的字符串,“...”表示这个字符串中包含的要转化成字符的其它类型数据。 printf(&str[1]); 嗯,我真是蛋疼了,但有些人(比如技术面试官)比我还蛋疼。这个输出的是啥子? 答案是ello world! printf碰到指向字符型数据的指针就会把它当作字符串来对待。这里&str[1]是str数组第二位的地址。printf碰到这地址会把这地址上的数据以及后面的数据按char型来输出,直到碰上'\0'为止。(没碰到就一直输出下去……) cout也如此。 cout<<&str[1];输出的也是ello world! cout<<str[1];输出的则是e 嗯,最后扯扯数组和指针吧……我懒得ctrl+v那些圣经,比如《C++ Primer》中的原文。 按自己的理解,扯两句拉倒。 简而言之,数组是由程序在内存中自动开辟的一块空间。当把数组名当变量使用时,编译器会把它当成一个指针,数值为数组的首地址。当然你可以用a+n等方式访问其它地址。换句话说,数组的名称只可以被(编译器)“看成”指针,不是定义上的指针!数组在生命期结束后会由程序自行释放。 而指针就是指针,占着内存4个字节,存储着一块内存的地址的数据。程序员可以用指针在内存堆区开辟一块动态区域。而开辟的区域是像数组一样以相等的小块(大小为数据类型在内存中占的字节数)顺次排列的。因此,可以把数组的访问方式(a[n]、a+n等)用于被指针开辟的区域中,这块区域就被称为“动态数组”(指针不是动态数组~)。 可以使用“指针名++”的方式来访问、遍历这块区域。这在数组中可不行,“数组名++”是错误的。 而且,被动态开辟的内存空间要由程序员自行释放,否则会发生内存泄漏(不是内存溢出,二者区别自己搜去) 一开头就用字符串的例子说过了,但还要再强调一次 char* p = (char*)malloc(100); sizeof(p)值为4,因为p就是个指针 char p[100]; sizeof(p)值为100 int p[100]; sizeof(p)值为多少? 100吗?当然不是。 答出400是一般正确的答案,但严格来说应该是“在Win32程序中为400”~ 顺便说说两个可能(被菜鸟)混淆的问题。 char *p = (char*)malloc(100); p = "fucking world!" free(p); 出错吧!p已经指向了非动态分配的区域!free()只能用于释放动态分配的内存~ new和delete同理。 对菜鸟来说,这比 char p[10]; p = "caonima"; 更难以察觉,因为上面的错误可以在编译期发现。 void _Size(char p[1000]) { cout<<sizeof(p); } 在函数的形参是数组时,编译器会自动把它转化为指针~输出值是4! char p[]也一样 在面对蛋疼、变态、生孩子没屁眼的公司技术面试官时,希望以上的闲扯会对一些人有所帮助~
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
laoguan机器人#1 · 2010/5/20
哎呀,我也老是干些类似的活,刚才还研究了一把struct对齐、char*转化struct*的事~~ 我也蛋疼啊
sd4137838机器人#2 · 2010/5/20
p = "caonima";
jmpesp机器人#3 · 2010/5/20
给lz修改几个错误: 纠正1: 区别2:str1指针指向的是一个字符串常量,存于内存静态区(又叫全局区)。str2数组装载着一个15个字符组成的字符串变量,存于内存栈区。 (这个不确切,str2要在栈中分配的话只能是局部变量,如果str2是全局变量,则分配内存依然是全局数据区。) 纠正2: 简而言之。 变量数组(相对于常量数组而言)是由程序在内存栈区自动开辟的一块空间。当把数组名当变量使用时,编译器会把它当成一个指针,初始值为数组的首地址。当然你可以用a++等方式访问其它地址。(这个可不行,典型的语义错误,a已经被当做右值的语义处理)换句话说,数组的名称只可以被(编译器)“看成”指针,不是定义上的指针!数组在生命期结束后会由程序自行释放。 【 在 DaiGakuSei 的大作中提到: 】 : 老话题了,嗯。 : 先看这两个东东 : char *str1 = "fucking world!" : ...................
chrischen25机器人#4 · 2010/5/20
赞LZ和3L,以前就是不怎么分得清这两玩意
DaiGakuSei机器人#5 · 2010/5/20
【 在 jmpesp 的大作中提到: 】 : 给lz修改几个错误: : 纠正1: : 区别2:str1指针指向的是一个字符串常量,存于内存静态区(又叫全局区)。str2数组装载着一个15个字符组成的字符串变量,存于内存栈区。 : ................... 糟……第一个考虑不周 第二个精虫上脑……把该在动态内存部分出现的内容写到数组部分中了。 唉,写技术文章前要让知识滴水不漏啊……我还是太年轻了~ char *a = "ABCDEF"; cout<<*(++a); 输出B char b[10] = "ABCDEF"; cout<<*(++a); 编译错 ++ needs a l-value
liangwu1316机器人#6 · 2010/5/20
寓教于乐
bullet机器人#7 · 2010/5/20
qiuyesuifeng机器人#8 · 2010/5/21
这个必须顶。。
loveway2008机器人#9 · 2010/5/21
赞~