tekynet 阅读(878) 评论(15)

看完后.明白**讲的为什么char** 不能自动转化为 const char**,(原文)但对我影响最深的是下面的话:

==================================================================
char *p="abc" 能不能编译通过要看你使用的编译器。鉴于大量遗留代码的存在,大部分编译器允许其通过,或者给个警告。当然,程序员自己必须保证绝不去修改其值。

程序员不应该在代码中出现*p='A'这样的语句。这是当初约定好了的:编译器允许char *p="abc"通过,而程序员保证不去修改它。
b. *p='A'编译时应该允许通过,因为单就这条语句而言,它完全合法。
c. 运行时*p='A'能不能通过要看实际的运行环境,包括你使用的操作系统、编译器、编译器选项 等等,一句话,其运行结果由不得你,且不应该由你去关心,因为这种行为本身已经违反约定了。
==================================================================

工作关系吧,用CString 和string用的太多了,很少这样定义字符串 char *p=“abcde“了
匝一看,还不适应,:(,渐渐的回想才想起一些来(哎,还是太生疏,赶快写下来,以后别忘了)

这样定义的字符串char *p=“abcde“ ; char *p1=“123445667“;

正如上面提到的是不能再 *p='A',运行的时候会出错,同样,strcpy(p,p1)也会出错哟,

"abcde"字符串可以看做是个常量字符串了,是不能被修改的,

但如果 char p[]=“abcde“ 这样定义,就没有问题,你可以修改*p='A',只要不越界就ok.

并且发现这样两种定义
char *p=“abcde“

char p[]=“abcde“

在运行的时候,p指向的地址也不是一样的,可见char *p=“abcde“还是有特殊的处理 :),具体怎么处理就不知道了,高手请指教:)


随着测试,又发现个问题,可能是个老问题了吧:


int main(int argc, char* argv[])

 int t[10];
 char p1[7]="123456";
 const char *p2="1234567890123213123";
 
 int len(0);
 
  //*p1='C';  err

 len=strlen(p1);
 printf("%d\n",len);
 
 strcpy(p1,p2);   ///??????????
 
 printf("%s\n",p1);
 
 len=strlen(p1);
 
 printf("%d\n",len);
 return 0;
}

我定义的是7个字符数组, 但用strcpy把p2拷到p1中,p1是放不下的,但程序却正常执行,warning ,err都没有,运行也正常?


输出

6
1234567890123213123
19

应该是使用内存越界了阿??怎么会正常运行呢?

难道对于内存越界的使用,运气好才崩溃表现出来,运气不好就正常运行??


评论列表
lostpencil
re: 看了 **的 想到的
感觉:
char *p="abcd";
*p='a';
是一个执行期的毛病,编译期不违背任何原则吧,和语言的设计有关.
至于后面的东西,本来编译器都允许指针访问越界的,这个和运气无关,这个是语言的蹩脚特色吧,灵活高效当然要放弃一些东西.不在于安全不安全,而在于值得不值得.
有几本c的书对这个讲的比较详细,可惜我都记混了,不知道是哪一本了,:)
新年快乐!
lostpencil
re: 看了 **的 想到的
谁叫你明明是常量不加const呢,呵呵
清风雨
re: 一出门就看到一个错误
一直,犹豫着,会是有所内容呢,还是只是就事论事?
发现,有两个评论,那么,我想肯定是有内容了。

结果一看开头就有一个错误。所以,也就不继续往下看了。希望作者还要先弄清楚哦!—— 可不是表面那么回事。
teky
re: 看了 **的 想到的
清风雨,我就是不太清楚,才贴上来问问,有什么问题,麻烦你指导一下:)
小明
re: 看了 **的 想到的
char *p="abcde"

char p[]="abcde"
有很大不同的

简单来说,"abcde"是分配在程序常量堆的一个变量
char *p="abcde"
是声明一个指针,指向了那个常量
char p[]="abcde"
是声明在堆栈上的一个数组,它的内容拷贝那个常量

举个例子来说
char * foo()
{
    char *p="abcde";
    return p;
}
是可以work的
char * foo()
{
    char p[]="abcde";
    return p;
}
就是不对的,函数执行完,这段内存就释放了

小明
re: 看了 **的 想到的
还有你的strcpy为什么没有问题,是因为你声明了int t[10];

进入函数后,堆栈是这样的
p2
p1[7]
t[10]
地址由低到高
所以你strcpy超出的部分都到t[10]里面去了
你要去掉这个,程序一定报错!

teky
re: 看了 **的 想到的
to 小明,讲的很清楚了,谢谢
再问一下
char * foo() 

    char *p="abcde"; 
    return p; 


这样分配的"abcde"在程序常量堆中,那启不就是个全局的了?在程序退出的时候它才释放?
小明
re: 看了 **的 想到的
对,程序退出才释放。
teky
re: 看了 **的 想到的
to 小明:
一开始程序并没有定义t[10] ,strcpy越界并没有报错,

后来,我发现这个问题以后,想看看p1,p2的地址怎么分配的,才加了个
t[10] 试试,但还是没有找到原因,t,p1,p2的地址分配也没有看懂 :(
周星星
re: 看了 **的 想到的
我定义的是7个字符数组, 但用strcpy把p2拷到p1中,p1是放不下的,但程序却正常执行,warning ,err都没有,运行也正常?
--- 编译没错这很好理解,因为你这里没有任何一句有语法错误。
运行时有没有错误就不是由你决定的喽。

内存越界也很容易看出来,打印出 strcpy(p1,p2) 执行前后的 t 数组内容就可以看出来了。
顺便说一下,这个错误很隐蔽的,因为它本身从现象上似乎没有错,但它却暗暗的修改了一个不相干的变量,出现这样的错误后,调试者一般只从 t 相关的语句上去寻找,当然是找不到。

还有一种越界,但却没有运行时错误的情况,这是由内存对齐造成的,比如你想分配一个5字节的空间,系统有可能分配给你8个字节的空间,这时当你越界这5个字节,但仍在8个字节内的时候,一切都是正常的。这个错误更难发现。
teky
re: 看了 **的 想到的
to 星星 谢谢,

运行时有没有错误就不是由你决定的喽。

和和,也就是看运气了?
teky
re: 看了 **的 想到的
to 小明:

去掉的确没有错误,我测试过的
恩,我去看看 :)



周星星
:)给你一个内存示意图
#include <cstdio>
#include <cstring>
int main( void )

 char t[2];
 char p[2];
// 一种可能的内存分配(X代表因为字节对齐而留空的空间)
// p[0] p[1] X X X X X X X X X X t[0] t[1] X X X X X X X X X X
 strcpy( p, "123456789ABCDEF" );
// 拷贝后的内存值
// p[0] p[1] X X X X X X X X X X t[0] t[1] X X X X X X X X X X
//    1    2 3 4 5 6 7 8 9 A B C   D    E   F                   
 return 0;
}
suomynona
re: 看了 **的 想到的
提一个
char* p="abc";
"abc"==p; // 这个是true哦.

发表评论
切换编辑模式