| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 900 人关注过本帖
标题:[求助]一道C语言题
只看楼主 加入收藏
xiaomingzhao
Rank: 1
等 级:新手上路
帖 子:4
专家分:0
注 册:2007-11-19
收藏
 问题点数:0 回复次数:3 
[求助]一道C语言题

现在有一个字符串,我们不知道串里的具体内容,只知道里面是数字,数字中有一个 \0 ,要求把 \0前后的数字字符读出来组成一个新的字串,换句话说就是去了数字字符中的\0.
下面是我编的一段程序,可为什么当我把*p1 ="26545\08947";该为*p1 ="26545\018947";或*p1 ="26545\028947";或*p1 ="26545\038947";或*p1 ="26545\048947";或*p1 ="26545\058947";或*p1 ="26545\068947";或*p1 ="26545\078947";都不能得到结果呢?变为或p1 ="26545\088947";时就正常了!望高手们帮我解释一下,谢谢了!


#include <stdio.h>
#include <string.h>
#include <malloc.h>

int main()
{
char *p1 ="26545\08947";
char *p2;

p2 = (char *)malloc(100);

strcpy(p2,p1);
p1 = p1 + strlen(p1) +1;

strcat(p2,p1);

printf("%s\n",p2);

free(p2);
return 0;
}

搜索更多相关主题的帖子: C语言 
2007-11-19 14:43
luomingqu
Rank: 1
等 级:新手上路
帖 子:11
专家分:0
注 册:2007-11-19
收藏
得分:0 
2007-11-19 16:20
云水禅心
Rank: 1
等 级:新手上路
帖 子:9
专家分:0
注 册:2007-10-25
收藏
得分:0 
...
2007-11-20 03:29
cdutli
Rank: 1
等 级:新手上路
帖 子:4
专家分:0
注 册:2007-10-28
收藏
得分:0 

#include <stdio.h>
#include <string.h>
#include <malloc.h>

int main()
{
char p1[] ="26545\08947"; //字符串在现在的标准CC++中的类型是:const字符的数组,故不要用一个指针变量指向常量

//否则无意中修改了串时可能发生非法访问错误!而是采用将字符串文字量复制到字符数组中进行操作

char *p2;

p2 = (char *)malloc(100);

strcpy(p2,p1);
char *p3 = p1 + strlen(p1) +1; //!!!

strcat(p2,p3);

strcat(p2,p1);

printf("%s\n",p2);

free(p2);
return 0;
}

//之所以p1中改为\00~\07均不显示预期数字,因为CC++允许:转义符'\'后面接一个八进制数,用于表示ASCII码等于该值的字符,故\00~\07分别表示值为0~7的ASCII字符,显示结果对照ASCII表一目了然。而\08、\09,8,9为十进制数了,故编译器解析为\0

//因为空字符没有任何输出效果,所以正常的字符串中是不会出现空字符的。因此,用它来表示一个字符串的结束. 故在字符串中间最好不要出现空字符\0


参考:
[URL=http://www.d2school.com/bcyl/bhcpp/newls/ls06.htm#6.2.4]null[/URL]

关于字符串常量问题的讨论,参考哈搜集的资料:

(1),问题代码:

以下两程序在TC可以正常运行, 在VC6.0环境下出现异常,属于同一问题,
file1.c
#include<stdio.h>
int main()
{
void copy_string(char from[], char to[]);
char *a = "I am a teacher.";
char *b = "You are a student.";
printf("string a = %s\nstring b = %s\n", a, b);
printf("copy string a to string b: \n");
//经过调试,问题出现在下面的函数调用, 可不知道为什么
copy_string(a, b);
printf("string a = %s\nstring b = %s\n", a, b);
return 0;
}


void copy_string(char from[], char to[])
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i]; //会在此处抛出异常!!无法执行!!
i++;
}
to[i] = '\0';
}
-------------------------------------------------------------------
file2.c
#include<stdio.h>
int main()
{
void copy_string(char *from, char *to);
char *a = "I am a teacher.";
char *b = "You are a student.";
printf("string a = %s\nstring b = %s\n", a, b);
printf("copy string a to string b: \n");
//经过调试,问题出现在下面的函数调用, 可不知道为什么
copy_string(a, b);
printf("string a = %s\nstring b = %s\n", a, b);
return 0;
}

void copy_string(char *from, char *to)
{
for (; *from != '\0'; from++, to++)
*to = *from;
*to = '\0';
}

(2),解决方案:

两个程序中的子函数copy_string()写的都是正确的,但是在main()函数是从字符串a、b的定义来看,所作的操作是在程序中定义两个字符串常量,然后将这两个字符串常量的地址传给了a和b。在程序中,常量和变量在内存中的存储位置是不同的,决定了变量可以被修改,而常量不可以。程序的改正方法很简单:
方法1:

char *a = "I am a teacher.";
char *b = "You are a student.";
改为
char a[] = "I am a teacher.";
char b[] = "You are a student.";

方法2:

char *a = "I am a teacher.";
char *b = "You are a student.";
改为
char a[100] = "I am a teacher.";
char b[100] = "You are a student."; //固定空间,不灵活
(注:100是字符串最大长度,也可改成其它的值)

两种方法都可以通过,但是方法2与更安全,因为使用方法1有一个限制,由于方法1的定义方法中定义的字符数据的长度是字符串长度加1,空间相对而言是有限的,使用方法1时如果a的长度比b大,那么程序还是会报错。
如果使用方法2,那么设定一个最大长度,相对于方法1会安全许多。

(3),详细阐述(【liqiang321cde】):

char *b = "You are a student.";

像上面这样的形式,其实可以看成是在程序的常量段定义一个字符串 "You are a student.",然后申请一个字符指针,这个指针指向这个常量段。

表面上看b[1]或是*(b+1)好像是指向了字符串 "You are a student."中的第二个字符o,但实际上*(b+1)指向的是程序中的另外的与这个字符串根本无关的其它的程序代码,而这些代码用户没有权限修改,所以用调试模式可以发现报错为0xC0000005: Access Violation !!!

如果从你懂汇编语言的话,可以从VC++中得出的汇编代码中看出:
char *b = "You are a student."形式,字符串的段定义是“CONSTSEGMENT”,即常量段。
变量定义如下:
_TEXTSEGMENT
_a$ = -4
_b$ = -8
可以看出a和b是4字节的指针变量

char b[] = "You are a student."形式,字符串是在程序的运行段中赋值的。
变量定义如下:
_TEXTSEGMENT
_a$ = -16
_b$ = -36
这里,_a$ = -16是因为字符串a有15个字符,加一个'\0',为16个;

_b$ = -36是因为字符串b有18个字符,加一个'\0',为19 个,由于在a的基础上偏移,再加上变量所占内存要是4的倍数(单位字节),所以实际上b占了20个字节,所以偏移是20+16=36,所有变量偏移都是负 的,所以加个负号。

注:在VC++6.0环境下,Project-->Setting->C/C++ ->Category 选Listing Files->Listing File type->Assembly, Machine Code, and Source.

编译,在Debug文件夹中将出现一个.cod的文件,这个就是汇编代码。

在VS.net2003环境下,项目->属性->C/C++ ->输出文件->汇编输出->选择"程序集、机器码和源代码",然后编译,也会出现.cod文件,这个就是汇编代码。

常量字符串(即字符串文字量)其实在VC中也可以修改
但是必须改成Release工程,Debug工程不允许对常量字符串进行直接的修改,因为Debug工程会对指针进行安全检查,发现不安全的指针操作就会终止程序继续运行。

你非要在Debug工程里修改常量字符串也可以,但是必须通过间接手段(即复制一个副本),才能修改Debug工程内的常量字符串。若企图在Debug工程内直接修改常量字符串,会碰的头破血流

问题中的那2个程序在Debug工程时能运行但是结果不正确,而且会弹出一个对话框警告你进行了不安全操作。但是改成Release工程以后可以运行也没有警告对话框,结果也正确~~~

因为Debug工程包含大量调试模块,会对你的程序进行安全检查。你看看Debug工程都比Release工程的程序大的多,就是因为Debug工程含有Release工程没有的调试检查模块!!

Debug工程弹出的警告是:“xx指令引用的xx内存,该内存不能为写”,
因为Debug工程里常量指针是只读的不允许写入数据

应当指出的是,如果 Debug有错,即使 Release正常,程序肯定是有Bug的,只不过是 Release 的没有表现出来而已。

故总结一下:

Release和Debug工程对变量的处理方法是不同的。

char *a = "I am a teacher."; char *b = "You are a student.";
在Debug被放进静态存储区里,是只读的区域,不能直接修改,但是可以用间接方法修改。

附录一:出现问题的代码写的不够优雅,就是说不规范!!
【liqiang123abc】:
我还想告诉楼主几句
问题中的代码写的不够优雅,就是说不规范
在一个小程序里也许没什么大不了,如果你做大型的应用程序也这样干的话,你会碰的头破血流滴!!

【liqiang123abc】:
我觉得他的代码写的不好,是在向编译器挑战!!
首先,他的指针使用有明显的问题,企图修改只读数据区的数据(上面讨论了改正方法,小问题);
第二,他的字符串复制是短的a向长的b复制!!(大问题!!)

如果出现反过来的情况,长的向短的复制会如何?会出现溢出错误!!!
在小程序里也许无关紧要,如果是大型程序后果会很严重

我们把楼主的代码稍微改一下,改成b向a复制,看看结果是什么?
#include<stdio.h>
int main()
{
void copy_string(char from[], char to[]);
char *a = "I am a teacher.";
char *b = "You are a student.";
printf("string a = %s\nstring b = %s\n", a, b);
printf("copy string a to string b: \n");
copy_string(b,a);
printf("string a = %s\nstring b = %s\n", a, b);
return 0;
}
void copy_string(char from[], char to[])
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i];
i++;
}
to[i] = '\0';
}
这样,长的字符串b写满了短字符串a 以后,继续向后边写进数据,超过了短字符串的区域,覆盖改写了后边的数据!!在大型程序里会出现严重错误,到时候叫你哭都哭不出来!!(程序崩溃!)



即使更正了先前的错误——把常量字符指针改成了数组,代码仍然有问题!!
因为即使是修改了的编译运行都正常的程序,仍然存在一个潜在的bug!!(就是短字符串给长字符串赋值,长字符串最后会留下一些原来的尾巴!!当心这些尾巴!!)

如果让我给他那个修改以后的程序打分的话,只能给他50分

楼主修改过的程序就是把
char *a = "I am a teacher."; char *b = "You are a student.";
改成了char a[] = "I am a teacher."; char b[] = "You are a student.";

这么改表面上看似乎一切都正常了,编译没问题、运行都没问题。但是却有一个bug潜伏下来。

我们来仔细分析一下复制字符串的过程:a向b复制了16个字符以后结束了。那么我们知道b字符串比a长,那么的b尾巴上的字符呢?b尾巴上的3个字符依旧在那里。如果是在应用程序里,此时我们复制b的内容到别的数组就可能出问题,如果用strcpy一般没有什么事(碰到尾巴前的第一个’\0’就结束了),但是要是使用memcpy复制,问题就来了,尾巴上的3个字符可能也会被复制!!(因为此时是按照b的存储长度复制的,而不是按\0结尾的字符串长度复制, b的存储长度 > 先前复制到b中的字符串长度<即a的长度> ) 此时那就看你运气好不好了,也可能什么事都没有,也可能让你后悔一辈子!!
【liqiang123abc】:
我把楼主修改过的程序稍微修改一下,让程序把b尾巴上的字符显示出来
看看是什么:

#include<stdio.h>
int main()
{
void copy_string(char from[], char to[]);
char a[] = "I am a teacher.";
char b[] = "You are a student.";
printf("string a = %s\nstring b = %s\n", a, b);
printf("copy string a to string b: \n");
copy_string(a,b);
printf("string a = %s\nstring b = %s\n", a, b);
printf(" string b 的尾巴: %s\n", &b[16]);
return 0;
}

void copy_string(char from[], char to[])
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i];
i++;
}
to[i] = '\0';
}

尾巴上的3个字符是 
1字母t 
2标点符号. 
3结束字符\0

【liqiang123abc】:
我认为潜伏的bug更加可怕!因为它是潜伏的!!如果是明显的bug我们可以看见,正如楼主的程序,运行的时候跳出来一个警告,傻瓜也知道程序出问题了。
潜伏的bug比较可怕,他象一个潜伏的特务,隐姓埋名、藏在角落里,一旦接到上峰密令"干掉楼主,提头来见"。他就从隐藏多年的角落里跳出来,撕掉伪装的面具,拔出枪来干掉可怜的楼主,向上司复命

解决:可以将目的串数组清空后再复制到其中,或者使用string

附录二:还有些不明白的地方
1) 既然b字符串 存储于常量存储区,无法修改, 那为什么在TC和VS2003可以编译通过呢, 是编译规则不一样,还是其他什么原因?
2) 上面的朋友认为这样的程序不规范, 如果要用指针实现字符数组的操作,并且要求既规范又高效, 如何写?

关于(1)的解答:

是编译规则不一样。

在TC下编译可以通过很正常,因为TC使用的编译器是按照Intel_8086指令集编写的,没有保护模式, 不管读写都可以,所以可以编译运行;而vc6.0的Debug工程有保护机制,它将常量字符串放到静态存储区,数据是只读的,你企图直接修改只读数据当然会出错了。

我看了一下TC程序出来的汇编代码,两个字符串在程序是按字符数组的形式存储的,所以不会出错.
在VS.net2003中我也试过了,会报错,不知道是不是我们的编译器设置有区别.


关于(2)的解答:

其实字符数组在函数内部是被当作指针调用的,你的那个修改字符串的函数内部字符数组和字符指针完全等价。不过在main函数里字符数组和字符指针是不一样的2种概念,千万不能混淆!

你只要记住在函数内部字符数组和字符指针完全等价就行了
在函数内部:字符数组 == 字符指针



2007-11-20 10:54
快速回复:[求助]一道C语言题
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018097 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved