1
tghgffdgd 2017-12-26 09:33:15 +08:00 via Android
你发帖这么久时间自己试一下早就知道答案了,程序员多练习很重要。
|
3
RDF OP @tghgffdgd 似乎在部分实现中,其开始的地址显示相同,而进行‘+1 ’的操作后,可以看出内存增量的不同, 但 cout 形式的话,不会判断。
|
4
justou 2017-12-26 10:00:14 +08:00
int* 加 1 后是移动了 sizeof(int)个字节了
|
5
xiubin 2017-12-26 10:01:12 +08:00 via iPhone
字符串首地址不就是第一个字符的地址吗?
没了解过 cpp,只学过 c |
7
amaranthf 2017-12-26 10:14:32 +08:00 1
用了这么多年 C++,还不知道字符串的起始地址和字符串首字符的地址有啥区别……
|
8
changnet 2017-12-26 10:19:53 +08:00 via Android
我也觉得这两个地址是一样的啊
|
10
fxxkgw 2017-12-26 10:30:00 +08:00
感觉这个问题是科班还是培训机构或者转行最好的判别题目之一。。
|
11
wekw 2017-12-26 11:00:07 +08:00
可能在动态语言程序员眼里,数组是一种高级数据结构吧。。。。
|
12
Juggernaut 2017-12-26 11:04:02 +08:00
c 里面这样操作就是第一个 int 的地址,p+1 的话移 4byte ;
CPP 不知道怎么操作 |
13
RDF OP short tell[10];
tell displays &tell[0] &tell displays address of whole array 地址数字相同,但是含义不一样。 &tell[0], is the address of a 2-byte block of memory &tell is the address of a 20-byte block of memory So tell + 1 adds 2 to the address value &tell + 1 adds 20 to the address value ......估计还会忘... |
14
RDF OP 上文是:字符串首字符的地址,读地址,并显示字符,那(int * )强制转换出的,就是 H 字符所在实际内存地址,
......依旧小晕... |
15
facetest 2017-12-26 11:37:14 +08:00 via Android
好好上过基础课的就不会来发这种问题
|
16
tghgffdgd 2017-12-26 11:46:01 +08:00 via Android
@RDF 用多维数组去理解吧,比如 char arr[5][6][7], arr 和 arr[0] 和 arr[0][0] 和 &arr[0][0][0] 几个值相同但意义不一样
|
18
RDF OP @tghgffdgd 还在啃 Compound Types 符合类型的基础, 这部分的基础有点难啃, 快了。
|
20
gnaggnoyil 2017-12-27 10:16:08 +08:00
C++中 const char [N]并不 type alias 于 int *,所以 LZ 这种行为是 UB.
|
21
RDF OP @gnaggnoyil c++ 指针可以直接以数组形式使用。
|
22
RDF OP @gnaggnoyil 参考 C++11 核心定义
|
23
gnaggnoyil 2017-12-27 18:19:00 +08:00
@RDF array to pointer decay 只会在有限的几个场景(比如函数传参)中使用,你这里在传参之前已经做 cast 了,所以这个 cast 是 UB.
而且就算你拿 const char *来,把它 cast 到 int *都是 UB. |
24
RDF OP @gnaggnoyil 不可能是未定义,因为这在 c11 的概念中是有明确定义的:双引号内的为字符串。cout 打印时读取字符串第一个字符的内存地址。并将其认成字符串进行输出。这是一般情况下直接用双引号进行输出时的明确定义。( int*)是对地址的转义进行强制的格式转换。使其可以输出首字符所在内存的内存地址。而不是直接将字符串的首字符内存地址认成字符串输出而进行字符串输出。
这是有明确定义的行为。 |
25
gnaggnoyil 2017-12-27 18:51:26 +08:00
@RDF 你要明确定义?好,你可以去翻翻标准[expr.cast]章节看看把 const char [N]转换成 int *是什么样的行为,而这并不取决于你如何用转换之后的值.
如果你要取得指针本身所包含的值,标准规定的唯一正确的方法是将其 reinterpret_cast 到 std::uintptr_t. 顺便说下虽然规定的具体用语有差别但是从 C++98 以来到现在乱转型实际结果大致都是不变的——不是错误就是会引起 UB.C 的规定可能比 C++要宽松些,不过我对 C 不熟所以不发表评论. |
26
RDF OP @gnaggnoyil 就是 c++11 修订的明确定义
|
27
RDF OP @gnaggnoyil
The expression "Home of the jolly bytes" is a string constant; hence it evaluates as the address of the beginning of the string.The cout object interprets the address of a char as an invitation to print a string, but the type cast (int *) converts the address to type pointer-to-int, which is then printed as an address. In short, the statement prints the address of the string, assuming the int type is wide enough to hold an address. 对于,(int *) ,不存在未定义的问题。 只是讨论其定义的实际区间问题。 |
28
RDF OP C++11
对 cout <<"Home of the jolly bytes"<<endl; 读取字符串"Home of the jolly bytes"所在内存,开始位置的内存地址(即字符 H 存储的内存的地址),cout 读取到字符串地址时,直接打印由该地址处开始的字符,直到\0; 打印 Home of the jolly bytes 对于 cout << (int *)"Home of the jolly bytes"<<endl; 则是对字符串"Home of the jolly bytes"中,开始字符 H 的内存地址进行解除引用,以打印出其所在内存的内存地址,而非‘由字符串内存地址开始打印字符’。 但疑惑的是, 这个地址,在数值上, [字符串的开始的内存地址的值] 和 [字符串首字符的内存地址的值] 在数值上相同。如果是直接声明的字符串 short ccc [8] ,可以 cout<<ccc <<endl; 即&ccc[0],其指向一个 short 内存块地址( 2bit ),形如* short cout<<ccc+1 <<endl; 和 cout<<&ccc <<endl;直接指向了含有 16bit 的 short 数组,+1 使其向后指了 16bit cout<<&ccc+1 <<endl; 借由+1 进行 但疑惑的是,对字符串取( int*) 查看到的内存地址, 实际表述为 [字符串的开始的内存地址的值] 还是 [字符串首字符的内存地址的值] 。不超 int 范围的前提下, 取法没问题, 但是这个对象没有找到清晰的解释。 |
29
RDF OP @gnaggnoyil
数组名被解释为其第一个元素的地址。 对数组名应用地址运算符时,得到的是整个数组的地址 对于,cout<<"statement"<<endl; cout 对象对所认为的字符串地址,直接打印该地址处的字符,强制*解除引用 则得到地址的值,但是由于 { 在 cout 和多数 C++表达式中,char 数组名,char 指针,以及用引号括起的字符串常量都被解释为 字符串第一个字符的地址。 } 因此 { 如果给 cout 提供一个指针,它将打印地址,如果指针类型为 char *,则显示指向的字符串 而 int* 便是将字符串指针*强制转换为另一种类型来进行显示。 } |
30
RDF OP @gnaggnoyil
即有: cout << "a"<< endl; 打印 a cout << *"a"<< endl; 打印 a [此时为 char 指针] cout << (int *) "a" << endl; 打印"a"字符串( a 和'\0' )中第一个字符的地址。 ----------------------------------------- cout << (int *)*"a" << endl;对 a 的地址解除引用得到 a 的值,由于上文, [此时为 char 指针] ,对其再次给一个强制转换的指针,以直接打印 a 在 ASCII 系统上的 ASCII 编码,61 |
31
RDF OP @gnaggnoyil
即,对: #include "stdafx.h" #include <iostream> const int ArSize = 20; int main() { using namespace std; char name[ArSize]; cout << "ASCIIized: "; cin >> name; cin.get(); cout << "ASCIIized:\n"; int i = 0; while (name[i] != '\0') { cout << name[i] << ": " << int(name[i]) << endl; cout << name[i] << ": " << (int *)*name << endl; cout << name[i] << ": " << (int*)(*name + 1) << endl; cout << name[i] << ": " << (int *)name<< endl; cout << name[i] << ": " << (int*)(name+1) << endl; i++; } cin.get(); } 有: ASCIIized: abc ASCIIized: a: 97 a: 00000061 a: 00000062 a: 0053FB6C a: 0053FB6D b: 98 b: 00000061 b: 00000062 b: 0053FB6C b: 0053FB6D c: 99 c: 00000061 c: 00000062 c: 0053FB6C c: 0053FB6D |
32
RDF OP @gnaggnoyil
即,对: #include "stdafx.h" #include <iostream> const int ArSize = 20; int main() { using std::cin; using std::endl; using std::cout; char name[ArSize]; cout << "ASCIIized: "; cin >> name; cin.get(); cout << "ASCIIized:\n"; int i = 0; while (name[i] != '\0') { cout << name[i] << ": " << int(name[i]) << endl; cout << name[i] << ": " << (int *)*name << endl; cout << name[i] << ": " << (int*)(*name + 1) << endl; cout << name[i] << ": " << (int *)name<< endl; cout << name[i] << ": " << (int*)(name+1) << endl; i++; } cin.get(); } 有: ASCIIized: abc ASCIIized: a: 97 a: 00000061 a: 00000062 a: 0053FB6C a: 0053FB6D b: 98 b: 00000061 b: 00000062 b: 0053FB6C b: 0053FB6D c: 99 c: 00000061 c: 00000062 c: 0053FB6C c: 0053FB6D |
33
RDF OP 上文的表述形式少做修改,并显式表达:
#include "stdafx.h" #include <iostream> const int ArSize = 20; int main() { using std::cin; using std::endl; using std::cout; char name[ArSize]; cout << "ASCIIized: "; cin >> name; cin.get(); cout << "ASCIIized:\n"; int i = 0; while (name[i] != '\0') { cout << name[i] << ": " << int(name[i]) << endl; cout << name[i] << ": " << (int *)(char *)(name[i]) << endl; cout << name[i] << ": " << (int*)(char *)(name[i] + 1) << endl; i++; } cout <<endl<< name[i] << ": " << (int*)(name) << endl; cout << name[i] << ": " << (int*)(name + 1) << endl; cin.get(); } 有: ASCIIized: abc ASCIIized: a: 97 a: 00000061 a: 00000062 b: 98 b: 00000062 b: 00000063 c: 99 c: 00000063 c: 00000064 : 0135FD7C : 0135FD7D |
34
RDF OP 第二次修订:
// 5.13 print name with while.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> const int ArSize = 20; int main() { using std::cin; using std::endl; using std::cout; char name[ArSize]; cout << "ASCIIized: "; cin.getline(name, ArSize); cout << "ASCIIized:\n"; int i = 0; while (name[i] != '\0') { cout << name[i] << ": " << int(name[i]) << endl; cout << name[i] << ": " << (int *)(char *)(name[i]) << endl; cout << name[i] << ": " << (int*)(char *)(name[i] + 1) << endl; i++; } cout <<endl<< name[i] << ": " << (int*)(name) << endl; cout << name[i] << ": " << (int*)(name + 1) << endl; cin.get(); } 有: ASCIIized: abc ASCIIized: a: 97 a: 00000061 a: 00000062 b: 98 b: 00000062 b: 00000063 c: 99 c: 00000063 c: 00000064 : 005AFA54 : 005AFA55 |
35
RDF OP @gnaggnoyil
重新修订并补全说明: #include "stdafx.h" #include <iostream> const int ArSize = 20; int main() { using std::cin; using std::endl; using std::cout; char name[ArSize]; cout << "ASCIIized: "; cin.getline(name, ArSize); cout << "ASCIIized:\n"; int i = 0; while (name[i] != '\0') { cout << "打印 ASCII-10 进制-数组第 i 位字符所在地" << endl; //打印 ASCII-10 进制-数组第 i 位字符所在地 cout << name[i] << ": " << int(name[i]) << endl << endl; cout << "打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转" << endl; //打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转 cout << name[i] << ": " << (int *)(char *)(name[i]) << endl; cout << name[i] << ": " << (int*)(char *)(name[i] + 1) << endl << endl; cout << "打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符" << endl; //打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符 cout << endl << name[i] << ": " << (&name[i]) << endl; cout << name[i] << ": " << (&name[i] + 1) << endl << endl; cout << "打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出" << endl; //打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出 cout << endl << name[i] << ": " << (int*)(&name[i]) << endl; cout << name[i] << ": " << (int*)(&name[i] + 1) << endl << endl; i++; } cout << "" << endl; cout << "打印整个数组的内存地址的开始位置-将 char 类型的地址-转 int 以使其由 cout 正常输出--对 char 类型的内存地址" << endl; //打印整个数组的内存地址的开始位置-将 char 类型的地址-转 int 以使其由 cout 正常输出--对 char 类型的内存地址 cout <<endl<< name[i] << ": " << (int*)(&name) << endl; cout << name[i] << ": " << (int*)(&name + 1) << endl << endl; cout << "打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址" << endl; cout << "解除引用,得整个的 char 内存地址的指针--cout 对 char 指针由开始位置进行输出,直到\\0" << endl; //打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 //解除引用,得整个的 char 内存地址的指针--cout 对 char 指针由开始位置进行输出,直到\0 cout << endl << name[i] << ": " << (char *)(&name) << endl; cout << "到达了 char 数组的外部,所以值为未定义" << endl; //到达了 char 数组的外部,所以值为未定义 cout << name[i] << ": " << (char *)(&name + 1) << endl << endl; cout << "打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址" << endl; cout << "解除引用,得整个的 char 内存地址的指针-转 int 以使其由 cout 正常输出-直到\\0" << endl; //打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 //解除引用,得整个的 char 内存地址的指针-转 int 以使其由 cout 正常输出-直到\0 cout << endl << name[i] << ": " << (int*)(char *)(&name) << endl; cout << "对整个数组内存地址+1-偏移量为整个数组的长度+1" << endl; cout << "解除引用,得整个偏移后的 char 内存地址的 char 指针--将 char 指针转 int 指针由 cout 正常输出" << endl; //对整个数组内存地址+1-偏移量为整个数组的长度+1 //解除引用,得整个偏移后的 char 内存地址的 char 指针--将 char 指针转 int 指针由 cout 正常输出 cout << name[i] << ": " << (int*)(char *)(&name + 1) << endl << endl; cin.get(); } 对应输出: ASCIIized: abc ASCIIized: 打印 ASCII-10 进制-数组第 i 位字符所在地 a: 97 打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转 a: 00000061 a: 00000062 打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符 a: abc a: bc 打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出 a: 004FFCA4 a: 004FFCA5 打印 ASCII-10 进制-数组第 i 位字符所在地 b: 98 打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转 b: 00000062 b: 00000063 打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符 b: bc b: c 打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出 b: 004FFCA5 b: 004FFCA6 打印 ASCII-10 进制-数组第 i 位字符所在地 c: 99 打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转 c: 00000063 c: 00000064 打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符 c: c c: 打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出 c: 004FFCA6 c: 004FFCA7 打印整个数组的内存地址的开始位置-将 char 类型的地址-转 int 以使其由 cout 正常输出--对 char 类型的内存地址 : 004FFCA4 : 004FFCB8 打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 解除引用,得整个的 char 内存地址的指针--cout 对 char 指针由开始位置进行输出,直到\0 : abc 到达了 char 数组的外部,所以值为未定义 : 烫烫 4栽麿 打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 解除引用,得整个的 char 内存地址的指针-转 int 以使其由 cout 正常输出-直到\0 : 004FFCA4 对整个数组内存地址+1-偏移量为整个数组的长度+1 解除引用,得整个偏移后的 char 内存地址的 char 指针--将 char 指针转 int 指针由 cout 正常输出 : 004FFCB8 应该说明完善了。 |
36
RDF OP 补充:
为了测试阈限, const int ArSize = 2; 输入为 a 有 字符串存的字符分别为‘ a ’和‘\0 ’ 可以检查得更为仔细: ASCIIized: a ASCIIized: 打印 ASCII-10 进制-数组第 i 位字符所在地 a: 97 打印 ASCII-16 进制-数组第 i 位-char 指针-int 指针转 a: 00000061 a: 00000062 打印数组第 i 位字符所在地-内存地址-cout 对 char 内存地址-直接输出其字符 a: a a: 打印数组第 i 位字符所在地-内存地址-将 char 内存地址以 int 形式输出 a: 0113F998 a: 0113F999 打印整个数组的内存地址的开始位置-将 char 类型的地址-转 int 以使其由 cout 正常输出--对 char 类型的内存地址 : 0113F998 : 0113F99A 打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 解除引用,得整个的 char 内存地址的指针--cout 对 char 指针由开始位置进行输出,直到\0 : a 到达了 char 数组的外部,所以值为未定义 : 烫烫烫坉 X N; 打印整个数组的内存地址的开始位置-将 char 类型的地址-对 char 类型的内存地址 解除引用,得整个的 char 内存地址的指针-转 int 以使其由 cout 正常输出-直到\0 : 0113F998 对整个数组内存地址+1-偏移量为整个数组的长度+1 解除引用,得整个偏移后的 char 内存地址的 char 指针--将 char 指针转 int 指针由 cout 正常输出 : 0113F99A |
37
RDF OP 这样细节就拦清了 :)
|