[讨论]
拨开C语言中字符串的那些迷雾(持续更新)
《C和指针》大概很多人都看过吧!最近重新整理了一下笔记,发现可能有很多人跟我在一条起跑线上,为了让你们少走弯路
我决定慢慢的把我的体会和笔记挑一些和大家分享,希望能帮到一些人,如果有错误还请各位共同探讨,一起砸砖。
前面论坛中也有许多人提到过字符串常量,版主也在许多帖中不止一次的提到过;现在我想系统的总结一下:
1.字符串常量:C语言中没有显式的字符串数据类型,字符串是以字符常量的形式出现或者存贮在字符数组中
字符串常量适用于不需要修改的字符串,需要修改的可以使用动态内存分配或字符数组;
C语言中的字符串常量实质是一个指向该字符串第一个字符的指针。
当一个含字符串常量的表达式出现在程序中时,编译器就把这些指定字符的一份拷贝存贮在内存中的某个位置,
并返回一个指向该字符串第一个字符的指针。
有了这一点那么它就可以像指针一样使用了,可以进行指针计算,下标引用,间接访问
"abc"+1; //指针向后移动一个字节,指向字符b
*“abc“ //输出字符a
"abc"[2] //输出字符c
"abc"[4] //数组越界,其值不可预测
至于其中他的
*p="abc";
那就不用说了吧!
再举一例:
void test(int n)
{
n+=5;
n/=10;
printf("%s","**********"+9-n);
}
这个程序比你们以前写的短多了吧!
五楼有更新。。。。。。。。。
[ 本帖最后由 yaoyong 于 2012-9-23 00:11 编辑 ]
有幸离楼主这么近
关注
关注!
生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙
===================================
做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
前面说过字符串就是一个指针常量,现在请考虑下面这两种形式的初始化:
char *ptstr="abc"; //A声明
char a[ ]="abc"; //B声明
char *a[]="abc"; //C声明
char a[5];
a="abc"; //D声明
char a[]={"abc"}; //E声明
char a[5]={"abc"}; //F声明
输出printf("%c ",a[1]);
A声明完全正确,将指针常量赋值给指针变量;
B声明在书中说是不允许的,因为指针并不是字符数据,但我在编译和执行时都通过了;
C明显不行,要用大括号;
D貌似与B是一样的,但根本就无法通过编译;
E,F都能通过编译而且能执行;
在A声明中创建了一个指针变量,用来容纳字符串返回的指针常量,不过请注意,仅仅就是指针常量而已
没有其他的什么东西了,不开辟内存。
请看下面一组赋值:
*("abc"+1)='m';
*(ptstr+1)='m';
*(a+1)='m';
首先要弄清楚左值(l-value)与右值(r-value);
其次是C语言中内存中标识位置(内存)的方式,可以用变量,也可以直接用地址,这就是所谓的间接访问与直接访问;
再者要注意是位置还是值,位置是可以放东西的(里面的东西可以变),而值只能是某个东西不能变;
最后。。。。。。还是老问题,指针与数组的缠绵;
我先写到这里,具体情况且听下回分解;
[ 本帖最后由 yaoyong 于 2012-9-24 22:36 编辑 ]
回复 沙发 幻城 的帖子
谢谢支持,不过最近可能没时间。。。。。。。
回复 板凳 _本杰明 的帖子
那本书我也有,哈哈,看了一半。。。烂尾了
回复 4楼 chenzhufly 的帖子
*("abc"+1)='m'编译的时候没有错误,但是运行的时候就有错误了,怎么解释?错误显示内存不能为write什么的;
回复 5楼 yaoyong 的帖子
*("abc"+1)='m'编译的时候没有错误,但是运行的时候就有错误了,怎么解释?错误显示内存不能为write什么的;
回复 9楼 qq626926200 的帖子
当然咯,字符串常量是不允许修改的,如果尝试重写字符串常量标准对此未定义;
回复 楼主 yaoyong 的帖子
现在开始解密咯!
先看第一个声明:
指针变量中存贮着字符串指针常量的值,编译器为该变量分配内存,假设一个指针变量占4个字节,那么就分配四个字节给ptstr;
B声明中实际上是提供了为字符数组初始化的简单方式,等价于
char a[4]={'a','b','c',0};
那我们则么区分字符串常量究竟是初始化式的简化还是指针常量呢?这个就简单了,只有在这种初始化时不是指针常量,其他都是。
C声明的是一个字符串数组,数组中存储的是每个字符串所返回的指针常量
D那就是高不成低不就了,这也看一下,那也摸一把,到头来弄得个神马也不是。。。。。。
数组名是一个指针常量,不允许修改;
E,F与B大同小异,实质一样的;
回复 11楼 yaoyong 的帖子
下标访问不仅数组可以有,任何指针都可以,所以数组在使用时并不检查下标是否越界;
对于*("abc"+1)='m';亦可写成“abc”[1]='m';这是对字符串常量的操作,常量是不允许修改的
即使指针常量亦是如此;
而*(ptstr+1)=‘m’;指针的加法表示指针向平移适当的位置,这里是平移一个字节
但是这个位置里面是什么呢?有其他的东西吗?这都是不知道的,ptstr不过是个指针罢了
连房产都没有,还只有个别人的门牌号,就想当房东,那不是抢占地皮吗?如果运气好碰上了好人还好,睁一只眼闭一只眼,万一碰上坏人,直接就挂了你,那就惨咯。。。。
这一点*(a+1)就表现得很好,a可是房东啊,这里都是他的地盘,里面想放什么那当然就随他便咯!
回复 楼主 yaoyong 的帖子
引用: 编译器就把这些指定字符的一份拷贝存贮在内存中的某个位置
关于这个,我稍微补充一下:
其实,不能说是把 指定字符 的拷贝存储在内存中。
或者说是,就是把这组字符串常量存储在内存中的静态存储区。
是直接存储,而非拷贝的副本,因为,如果说它是副本,那正本在哪呢?说拷贝的时候,会给人一种暗示:这个东西,在内存的其他地方有一份正本。
实际上并非如此。
回复 8楼 qq626926200 的帖子
有的编译器狗屎,比如微软他家的这一系列,连这种这么明显的 使用未开辟内存 的定义 也不报错。
所以,请不要把 编译通过太当一回事。
请注意观察 警告——当然那,如果那些狗屎编译器能提供警告,我也不至于骂它狗屎。它就是连警告都不吭声,才狗屎。
这个窗口的意思就是,你(读)写内存有问题,这种情况下,如果你分析明白了,自然就是 写了未开辟的内存空间。
回复 5楼 yaoyong 的帖子
关于这个,俺觉得,正名很重要。
乃们最好要 咬文嚼字,,何为 “定义”,何为“声明”。
声明一般是引用。
定义才是真正涉及内存分配行为的。
回复 13楼 辛昕 的帖子
关于这一点,根据作者的意思我想可能是他把原本写下来的那个字符串当成了本身,然后运行程序的时候就说成拷贝,,,,,,
嗯,确实是把他本身存储在静态存储区。
回复 15楼 辛昕 的帖子
对对对,关于这一点在关键字extern上表现得尤为突出
这里应该是定义而非声明
多谢斑竹耐心指点,我会更加努力地
看到楼主提到左值和有值,这的确是相当不错的。
c语法的这一部分其实很复杂而且混淆地厉害,诸如结合性,优先级,让人头疼地想自杀一百次都完全没有问题。
所以一种策略是回避,打不过就要跑。
比如说优先级,加个小括号是不会怀孕的,把一长条表达式分成几条也不会死……
这一部分我想告诉你的一件事情是,,不要自找麻烦~
看到楼主提到左值和有值,这的确是相当不错的。
c语法的这一部分其实很复杂而且混淆地厉害,诸如结合性,优先级,让人头疼地想自杀一百次都完全没有问题。
所以一种策略是回避,打不过就要跑。
比如说优先级,加个小括号是不会怀孕的,把一长条表达式分成几条也不会死……
这一部分我想告诉你的一件事情是,,不要自找麻烦~