V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RDF
V2EX  ›  程序员

地址转换为 int 指针后,打印出的是字符串第一个字符的地址 还是字符串开始的地址?

  •  1
     
  •   RDF · 2017-12-26 09:24:33 +08:00 · 2946 次点击
    这是一个创建于 2528 天前的主题,其中的信息可能已经有所发展或是发生改变。
    地址转换为 int 指针后,打印出的是字符串第一个字符的地址 还是字符串开始的地址?
    cout << (int *)"Home of the jolly bytes"<<endl;
    37 条回复    2017-12-28 09:56:51 +08:00
    tghgffdgd
        1
    tghgffdgd  
       2017-12-26 09:33:15 +08:00 via Android
    你发帖这么久时间自己试一下早就知道答案了,程序员多练习很重要。
    RDF
        2
    RDF  
    OP
       2017-12-26 09:52:47 +08:00
    @tghgffdgd 不会判断打印出的是字符串第一个字符的地址 还是字符串开始的地址。
    RDF
        3
    RDF  
    OP
       2017-12-26 09:54:15 +08:00
    @tghgffdgd 似乎在部分实现中,其开始的地址显示相同,而进行‘+1 ’的操作后,可以看出内存增量的不同, 但 cout 形式的话,不会判断。
    justou
        4
    justou  
       2017-12-26 10:00:14 +08:00
    int* 加 1 后是移动了 sizeof(int)个字节了
    xiubin
        5
    xiubin  
       2017-12-26 10:01:12 +08:00 via iPhone
    字符串首地址不就是第一个字符的地址吗?

    没了解过 cpp,只学过 c
    tghgffdgd
        6
    tghgffdgd  
       2017-12-26 10:07:31 +08:00
    @RDF +1 操作看指针类型,char*是加 1,int*是加 4 你本地多写几个例子然后打出来看看应该就很容易理解了
    amaranthf
        7
    amaranthf  
       2017-12-26 10:14:32 +08:00   ❤️ 1
    用了这么多年 C++,还不知道字符串的起始地址和字符串首字符的地址有啥区别……
    changnet
        8
    changnet  
       2017-12-26 10:19:53 +08:00 via Android
    我也觉得这两个地址是一样的啊
    RDF
        9
    RDF  
    OP
       2017-12-26 10:21:31 +08:00
    @tghgffdgd 嗷嗷,谢谢~
    fxxkgw
        10
    fxxkgw  
       2017-12-26 10:30:00 +08:00
    感觉这个问题是科班还是培训机构或者转行最好的判别题目之一。。
    wekw
        11
    wekw  
       2017-12-26 11:00:07 +08:00
    可能在动态语言程序员眼里,数组是一种高级数据结构吧。。。。
    Juggernaut
        12
    Juggernaut  
       2017-12-26 11:04:02 +08:00
    c 里面这样操作就是第一个 int 的地址,p+1 的话移 4byte ;
    CPP 不知道怎么操作
    RDF
        13
    RDF  
    OP
       2017-12-26 11:25:42 +08:00
    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

    ......估计还会忘...
    RDF
        14
    RDF  
    OP
       2017-12-26 11:31:00 +08:00
    上文是:字符串首字符的地址,读地址,并显示字符,那(int * )强制转换出的,就是 H 字符所在实际内存地址,

    ......依旧小晕...
    facetest
        15
    facetest  
       2017-12-26 11:37:14 +08:00 via Android
    好好上过基础课的就不会来发这种问题
    tghgffdgd
        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] 几个值相同但意义不一样
    RDF
        17
    RDF  
    OP
       2017-12-26 11:49:45 +08:00 via Android
    @tghgffdgd 哦哦
    RDF
        18
    RDF  
    OP
       2017-12-26 15:40:38 +08:00 via Android
    @tghgffdgd 还在啃 Compound Types 符合类型的基础, 这部分的基础有点难啃, 快了。
    RDF
        19
    RDF  
    OP
       2017-12-26 15:40:56 +08:00 via Android
    @RDF 复合
    gnaggnoyil
        20
    gnaggnoyil  
       2017-12-27 10:16:08 +08:00
    C++中 const char [N]并不 type alias 于 int *,所以 LZ 这种行为是 UB.
    RDF
        21
    RDF  
    OP
       2017-12-27 15:55:56 +08:00 via Android
    @gnaggnoyil c++ 指针可以直接以数组形式使用。
    RDF
        22
    RDF  
    OP
       2017-12-27 15:57:21 +08:00 via Android
    @gnaggnoyil 参考 C++11 核心定义
    gnaggnoyil
        23
    gnaggnoyil  
       2017-12-27 18:19:00 +08:00
    @RDF array to pointer decay 只会在有限的几个场景(比如函数传参)中使用,你这里在传参之前已经做 cast 了,所以这个 cast 是 UB.

    而且就算你拿 const char *来,把它 cast 到 int *都是 UB.
    RDF
        24
    RDF  
    OP
       2017-12-27 18:31:15 +08:00 via Android
    @gnaggnoyil 不可能是未定义,因为这在 c11 的概念中是有明确定义的:双引号内的为字符串。cout 打印时读取字符串第一个字符的内存地址。并将其认成字符串进行输出。这是一般情况下直接用双引号进行输出时的明确定义。( int*)是对地址的转义进行强制的格式转换。使其可以输出首字符所在内存的内存地址。而不是直接将字符串的首字符内存地址认成字符串输出而进行字符串输出。
    这是有明确定义的行为。
    gnaggnoyil
        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 不熟所以不发表评论.
    RDF
        26
    RDF  
    OP
       2017-12-27 19:37:37 +08:00 via Android
    @gnaggnoyil 就是 c++11 修订的明确定义
    RDF
        27
    RDF  
    OP
       2017-12-27 19:46:12 +08:00
    @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 *) ,不存在未定义的问题。 只是讨论其定义的实际区间问题。
    RDF
        28
    RDF  
    OP
       2017-12-27 20:11:05 +08:00
    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 范围的前提下, 取法没问题, 但是这个对象没有找到清晰的解释。
    RDF
        29
    RDF  
    OP
       2017-12-27 20:34:29 +08:00
    @gnaggnoyil
    数组名被解释为其第一个元素的地址。
    对数组名应用地址运算符时,得到的是整个数组的地址
    对于,cout<<"statement"<<endl; cout 对象对所认为的字符串地址,直接打印该地址处的字符,强制*解除引用 则得到地址的值,但是由于
    {
    在 cout 和多数 C++表达式中,char 数组名,char 指针,以及用引号括起的字符串常量都被解释为 字符串第一个字符的地址。
    }

    因此
    {
    如果给 cout 提供一个指针,它将打印地址,如果指针类型为 char *,则显示指向的字符串
    而 int* 便是将字符串指针*强制转换为另一种类型来进行显示。
    }
    RDF
        30
    RDF  
    OP
       2017-12-27 20:46:25 +08:00
    @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
    RDF
        31
    RDF  
    OP
       2017-12-28 08:39:21 +08:00
    @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
    RDF
        32
    RDF  
    OP
       2017-12-28 08:41:54 +08:00
    @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
    RDF
        33
    RDF  
    OP
       2017-12-28 08:55:44 +08:00
    上文的表述形式少做修改,并显式表达:


    #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
    RDF
        34
    RDF  
    OP
       2017-12-28 08:57:53 +08:00
    第二次修订:

    // 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
    RDF
        35
    RDF  
    OP
       2017-12-28 09:39:19 +08:00
    @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



    应该说明完善了。
    RDF
        36
    RDF  
    OP
       2017-12-28 09:52:08 +08:00
    补充:

    为了测试阈限,
    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
    RDF
        37
    RDF  
    OP
       2017-12-28 09:56:51 +08:00
    这样细节就拦清了 :)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5583 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:53 · PVG 15:53 · LAX 23:53 · JFK 02:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.