打印

指针和字符串那点事--C语言功底不扎实的勿进

[复制链接]
8212|34
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
armmage|  楼主 | 2010-11-11 18:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
标题不雷就没人关心?还是说程序细节从不过问?
好吧 请看一个很简单的C语句

char *p = "123";

相信这句意思不难,但是我发现的问题是,字符串"123"被编译器创建和初始化在什么位置?flash只读区 还是在ram读写区?有人关心过么?
也许各位大侠们觉得这是个无须过多关注的问题,字符串具体在哪由编译器自己决定,简单说也就是爱放哪放哪,关我毛事。那么请看下面的真实测试

ADS1.2环境下 芯片是lpc21xx

char *p = "123";  //定义并赋值
*p = '5';             //修改指针指向的内容

此两句ADS编译通过

但是当运行到 *p = '5'; 时ARM7进入了 数据预取异常模式  简单说 就是卡在DataAbort模式下 死机了。在AXD环境下查看内存会发现,字符串"123"被编译器创建在flash上 属性是只读的 如果通过指针p去修改 编译不报错 但运行会死机。
当然 规范的编程我也知道,此处定义p指针加上const修饰就没问题了,至少隐患算是解决了。真正的问题又来了,当我把同样程序移植到 IAR编译环境时同样的

char *p = "123";  //定义并赋值
*p = '5';             //修改指针指向的内容

IAR编译器把字符串"123"创建在RAM上,p指针可以不用const修饰 并且也可以通过p修改字符串内容。

我想请大家讨论 或 指教
这种不同的编译器产生的代码默认定位问题是否有办法修改,我查过IAR的用户手册,没有办法做到控制"123"字符串的位置。
当然 定义成数据是可以的 char __flash a[] = "123";
但我想要的效果是 直接用字符串对指针赋值。

此贴在技术交流区发过  无人问津 遂转入ARM专业版 此问题不问个水落石出 誓不罢休
不是我矫情,上21前以问人无数,公司同事,大学同学,大学老师,网友····均表示从未关心此问题,也不需要关心(说起来有点火大)

相关帖子

沙发
HWM| | 2010-11-11 19:42 | 只看该作者
to LZ:

char *p = “123”;

原则上,只定义了个指向类型char的指针,后随的 "123"只 是个附带的常数(字符串)。所以,没有明确的信息说明其一定要占据RAM。因此,编译系统完全可以随意处理,只要能利用*p取到"123"即可。如果一定要定位在RAM内,用数组。

使用特权

评论回复
板凳
armmage|  楼主 | 2010-11-11 21:57 | 只看该作者
楼上也是熟面孔的大侠了。
问题一:你说的编译系统随意处理,就是这‘随意’二字让我的程序在ADS和IAR不能通用,尤其是我例举的ADS的情况,编程不规范,不使用const修饰的话,后果很严重---现象是你都不知道什么时候会死机。
问题二:之所以用 char *p 来定义,指针的好处嘛我也不多说了,用数组那么长度将看情况变化。当在一个结构体数组中包含一个字符串成员时,你就明白我说的数组完全不好用。
typedef struct students_informertion
{
    char *name;       //姓名
    uint8 age;          //年龄
    uint8  number;   //编号
    ........                  //其他信息
}SI;

声明一个包含字符串指针的结构体数组 统计学生信息用
然后在定义的时候初始化

SI si_array[] =
{
    "张三", 20, 1,
    "李四", 21, 2,
    "王五", 18, 3,
    "赵一二", 22, 4,
    "欧阳三四", 22, 5,
    "刘七", 24, 6,
}:

对 si_array[]的任意元素的成员 *name 赋值  只需要直接写 字符串就好了 很方便。但是要换成用 数组来装那长度就不确定了,将数组声明为最大元素个数,当统计的人数很多时,无疑是浪费很多空间。
所以用指针来装最合理,但是问题同样有:
ADS环境下 默认 char *name = "张三";
字符串"张三"存储在flash中 不可修改(最好用const修饰)
这样的好处是 字符串本身不占用RAM 节约内存,可以想象下统计几百人信息时那姓名字符串的量放内存里要疯了。
坏处是字符串不可修改, 如果是做一个动态的表格,人员信息随时要更改,那就实现不了了。
同样在IAR环境下 char *name = "张三";
字符串"张三"存储在RAM中 ,这样的好处和坏处很上述正好相反。

我所想表述的实际问题就是以上说的 这种情况下不知道各位可有高招?
我想要的效果是 不用数组用指针装字符串,定义的同时初始化,字符串存放由我决定。

使用特权

评论回复
地板
xinzha| | 2010-11-11 22:08 | 只看该作者
我记得把字符串常量作为const的居多,这样也更符合c语言的规范。
char *p ="123";直接把p指向了const区
而如果写成 char p[]="123";则是在栈区要了块内存并且复制了一份字符串常量的副本,这时候再对p进行操作就没有问题了

使用特权

评论回复
5
armmage|  楼主 | 2010-11-11 22:20 | 只看该作者
楼上兄弟 我不是来和你讨论const的:'(
我现在纠结的是 大量使用字符串的时候 编译器默认的存储方式让我很头疼

使用特权

评论回复
6
xinzha| | 2010-11-11 22:30 | 只看该作者
只能说是ads的方式更规范一些。
比如说几个函数中都有相同的打印,编译器为了效率就会只建立一个实例给所有函数引用,如果这个时候允许你修改了,其他函数再引用这个指针的时候打印的就是错误的内容。还是按照最通用的笨办法好一些,省得在不同的环境下得到不同的结果,这也是可移植性的重要部分。

使用特权

评论回复
7
HWM| | 2010-11-11 23:26 | 只看该作者
to 3L:

关于你的例子:


typedef struct students_informertion
{
    char *name;       //姓名
   uint8 age;          //年龄
   uint8  number;   //编号
   ........                  //其他信息
}SI;

声明一个包含字符串指针的结构体数组 统计学生信息用
然后在定义的时候初始化

SI si_array[] =
{
    "张三", 20, 1,
    "李四", 21, 2,
    "王五", 18, 3,
    "赵一二", 22, 4,
    "欧阳三四", 22, 5,
    "刘七", 24, 6,
}:



请你注意了,si_array[] 中的单元 si_array[i] 是按照类型 SI 分配内存的。其第一个分量为 char *name 被初始化成指向字符串:
  "张三"
   "李四"
   "王五"
   "赵一二"
   "欧阳三四"
   "刘七"

之中某个的首地址。而这些字符串完全可能被编译系统分配到 ROM 中去。因此,别指望一定可以修改那些字符串。

当然,如果有幸编译系统将其分配到了 RAM 中,则对其修改也许可以。但这并不属规范。

使用特权

评论回复
8
HWM| | 2010-11-11 23:39 | 只看该作者
设想一下,如果 name 被改了(注意,改的是指针)。那个原先被指的字符串还能找回来吗?采用数组则不同,其名是个常量指针,是不能被改动的。

使用特权

评论回复
9
流行音乐| | 2010-11-11 23:43 | 只看该作者
修改字符窜常量本来就不是规范的行为,不同的编译器有不同的处理也不奇怪,因为有些编译器允许你修改(例如IAR),有些编译器不允许你修改(例如ADS)。需要改变的是楼主的习惯。

使用特权

评论回复
10
xwj| | 2010-11-11 23:57 | 只看该作者
同意LS,实际上还是LZ该被打PP

使用特权

评论回复
11
focuson| | 2010-11-12 03:30 | 只看该作者
本帖最后由 focuson 于 2010-11-12 03:36 编辑

标题强!呵呵。

9楼10楼说的对。

char *p = "123";  //定义并赋值
*p = '5';               //修改指针指向的内容


很不合规范的。所谓的野指针就是这样产生的。

先进一点的编译器如VC等,当看见 char *p="123" 就自动把它变成了 const char *p="123".(常量指针,所指向的地址的内容不能变化)。所以你第二句就根本编译不通过。 但另一些不那么完善的编译器,就让你麻烦了。

使用特权

评论回复
12
呆板书生| | 2010-11-12 09:01 | 只看该作者
好像没有规定,变量赋初值就一定是常量,

比如,int ni =1; 这没有规定ni就是常量,所以,才有const

同样,如果,

char   ucTab[] = "123";
char *p = ucTab;

那ucTab 是常量? *P是常量?

应该只是和编译器的默认值有关

使用特权

评论回复
13
armmage|  楼主 | 2010-11-12 09:12 | 只看该作者
你们说的意思我都知道啊(不要打我PP啊)
const 有啥好处我也懂的,上面写的例子是为了简便,实际我程序里能用const修饰保护的地方,我都用了。
流行音乐说编译器各自处理不同,这样造成我程序移植很大障碍,不是我改习惯问题,其实同样一段程序我分别用在LPC21XX和ATMEGA8上  同样是用 const char *name = "张三"; 但是ADS把"张三"放在FLASH中,
IAR把"张三"放RAM中,这个字符串我本来也就没想用指针去修改,所以定义为const的,但是mega8的内存爆掉了,后来查原因菜知道被字符串挤爆了······。因此我才提出这个问题,想请教解决办法,再次感谢各位指点。
总结下 1  const修饰这样的字符串才是规范的用法,尤其在不同编译环境下,我这点是知道的,所以打PP就饶了我吧。
       2  现在我所需求的效果是,同样用 char *name = "张三"; 来装字符串信息,只是字符串的存储要我说了算,不能由着编译器一句随意,爱放哪放哪,因为有时候实际需求中,这个字符串在程序运行时是要更改的。
       3  好了,问题3来了,很多大侠说用char数组来装字符串,再把数组赋值给结构体的指针,数组的的定位在FLASH还是RAM这个事各个编译器都很容易实现,这是绝对妥妥的,但是看我上述例子,实际统计信息有人的名字,公司,地址,毕业院校等等,这些都是用中文字符串来表示的,一统计就是好几百啊,用定义数组的办法我真要疯了。所以我才想出偷懒的办法,直接在初始化结构体时直接对指针赋值。
       4  现在这各问题我找到一点解决思路了,貌似前面某各大侠提过,那就是程序定位的配置,我在ADS的设置选项里没查看到关于字符串默认的存放设置(谁知道有悄悄告诉我下),但是有各功能我相信能吧字符串定义在fash或ram的权利掌握在我手里,这就是ADS的分散加载功能,刚才举例所说的那个结构体数组,我把定义他的整个目标文件 xxxx.o 单独配置加载域和执行域, 字符串能否修改,就看我的执行域在哪了,不知道这个思路对否,还没去验证。话说回来,苦了我的IAR for AVR,貌似mega8不支持ARM这样的程序定位功能,前面有大侠说过IAR也可以定位,但他用的是5.40版本,相信不是对AVR编程的·····,哎mega8继续爆内存ing
再次拜谢各位

使用特权

评论回复
14
jack_shine| | 2010-11-12 09:22 | 只看该作者
楼主的不把问题解决不爽快的心情值得理解:handshake

使用特权

评论回复
15
batsong| | 2010-11-12 09:37 | 只看该作者
我用的iar for 430

用字符串初始化指针,我记得是在flash的啊

可能是IAR发现你后面有修改字符串的地方,所以又给你在RAM里拷贝了一份

使用特权

评论回复
16
armmage|  楼主 | 2010-11-12 09:37 | 只看该作者
楼上你也帮出个点子嘛,或者我哪说的不对你指正下。工作的事天大,不解决了饭吃不好,觉睡不好,连女朋友都不想找······

使用特权

评论回复
17
xiepengchenghn| | 2010-11-12 10:48 | 只看该作者
mark

使用特权

评论回复
18
scutzhh| | 2010-11-12 11:17 | 只看该作者
加个CONSTANT吧!

使用特权

评论回复
19
scutzhh| | 2010-11-12 11:18 | 只看该作者
系统放在哪儿倭寇一般不追究!能用就行!

使用特权

评论回复
20
chunk| | 2010-11-12 11:29 | 只看该作者
你想调戏编译器?还是想让编译器调戏你?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

286

帖子

1

粉丝