[疑难] 菜鸟提问题

Colorful 2007-05-30
发现一个挺有意思的事情。废话不多说,直接上代码。
int main(char[][] argv)
{
    const char[] stringA = "Hello";  // 在默认代码段申请一块只读内存

    char[] stringB = stringA;  // stringB保存的内存地址跟stringA并不一致

    printf("stringA's Address = 0x%X\n", stringA.ptr);
    printf("stringB's Address = 0x%X\n", stringB.ptr);

    //stringA[1] = 'Q';  // 编译器在编译期保证stringA只读

    /* 看我指针大法 */
    char* pointer = stringA.ptr;
    pointer[1] = 'Q';
    pointer = null;

    printf("\n");

    printf("stringA = \"%s\"\n", cast(char*)stringA); // stringA = "HQllo",内容已更改
    printf("stringA = \"%*s\"\n", stringA);                // stringA = "Hello",内容未更改

    printf("\n");

    /* 为了避免是否强制类型转换导致内容不一致,测试一下(其实是没理由的测试) */
    char* tempString = cast(char*)stringA;
    printf("tempString's Address = 0x%X\n", tempString); // 地址与stringA相同
    printf("tempString = \"%s\"\n", tempString);     // 内容也相同

    printf("\n");

    /* stringB中的内容丝毫未动 */
    printf("stringB = \"%s\"\n", cast(char*)stringB);
    printf("stringB = \"%*s\"\n", stringB);

    stringB[2] = 'W';  // 修改stringB中的内容

    printf("\n");

    /* 下面的结果比上面的还精彩 */
    printf("stringA = \"%s\"\n", cast(char*)stringA); // stringA = "HQllo"
    printf("stringA = \"%*s\"\n", stringA);           // stringA = "HeWlo"
    printf("stringB = \"%s\"\n", cast(char*)stringB); // stringB = "HeWlo"
    printf("stringB = \"%*s\"\n", stringB);           // stringB = "HeWlo";

    return 0;
}


问题1: 为什么用指针修改stringA内容之后,stringA的结果会不一致?
问题2: 为什么修改stringB内容之后,stringA也会跟着改变?

一开始,我以为stringB是stringA的切片,但是当stringA内容改变时,stringB内容却不曾改变。接下来,我以为编译器可能知道stringA只读,所以给stringB另外申请了一块内存,复制其内容。但是修改stringB内容时,stringA内容也跟着改变了.

望前辈们不吝赐教.
Colorful 2007-05-30
附加一个问题。
关于Copy on Write机制,D文档里给出的示例,俺没有看明白,还请达人给指点迷津。

我对Copy on Write的理解是这样的:
这个东东实际上就是延迟分配内存,只有到你要修改其内容的时候才去申请新内存。这样做的好处就是提升性能和减少内存占用。

不知道理解的对不对,呵呵。
oldrev 2007-05-30
1. char[] stringB = stringA; 这句是深拷贝,等价于
 char[] stringB = "Hello"; //为 stringB 赋初值

2. 首先,试图修改 const 的值是未定义的行为。其次,const char[] stringA 与 stringA.ptr 类型并不相同,const char[] stringA 是编译时常量,compiler 怎么处置是它自己的事。
3. doc 里的COW值的不是数组实现了COW,而是Phobos 里的数组操作函数实现了COW。这些函数如果没有修改源数组的值就返回源数组,但是这有一定的危险性,如:

import std.stdio;
import std.string;

void main(char[][] argv)
{
	char[] a = "ABCDE";
	char[] b = toupper(a);
	b[0] = 'S'; //这将导致源数组 a 被修改
	writefln(a);
}
Colorful 2007-05-30
晕,我把const最原始的定义给忘了,哈哈。
面壁......
achun 2007-05-30
oldrev 写道

import std.stdio;
import std.string;

void main(char[][] argv)
{
	char[] a = "ABCDE";
	char[] b = toupper(a);
	b[0] = 'S'; //这将导致源数组 a 被修改
	writefln(a);
}

不对吧!
import std.stdio;  
import std.string;  
void main(char[][] argv)  
{  
	char[] a = "abcde";  
	char[] b = toupper(a);  
	char[] c = a.dup;//dup 	创建一个同样大小的动态数组并将原数组的内容复制到新数组中。
	char[] d = a;
	b[0] = 'S'; 
	writefln(a);//print abcde

	c[0] = 'S';
	writefln(a);//print abcde
	
	d[0] = 'S';
	writefln(a);//print Sbcde
}
oldrev 2007-05-30
什么不对?"ABCDE" 没被 toupper(a) 修改,返回的仍然是 a。
程序运行后显示 "SBCDE"
achun 2007-05-30
你吧
char[] a = "ABCDE";

改成
char[] a = "abcde";

我有些不明白了,这个toupper的行为正常么?
oldrev 2007-05-30
这就是 COW
achun 2007-05-30
又仔细的看了一下DOC,里面正好是toupper的原代码,和string.d里的基本一致.
果然是COW
也就是说,在COW下,
如果程序绝对希望对数据进行真正的COPY 赋值,
就像上面的那样,那就要自己手工代码保证,
也就是 类似 dup 的操作了.
汗,我.....
DOC看的不仔细呀....
规则没有搞清楚呀....
这次印象深刻了.
另外发现string.d里的toupper里有
r[i] = cast(char) (c - (cast(char)'a' - 'A'));

猛一看似乎觉得效率低,又一想....
明白了.
1.(cast(char)'a' - 'A')
可能会被编译器优化直接计算成一个固定值
2.这就是标准库的写法呀
谁说平台一定用ASCII编码的?
比如IBM的有些系统平台就不是ASSCII编码,
不过这算法只适合 abcd...z连续,ABCD...Z也连续的规则
如果是aAbB...zZ的编码规则这个算法还是要错的.
oldrev 2007-05-30
程序内部使用什么编码根系统平台有什么关系?
Global site tag (gtag.js) - Google Analytics