解析变量类型

typedef char * pstring;
const pstring cstr = 0; //cstr是指向char的常量指针
const pstring *ps; //ps是一个指针,它的对象是指向char的常量指针

上面两条声明语句的基本数据类型都是const pstring,const是对给定类型的修饰。pstring实际上指向char的指针,因此const pstring就是指向char的常量指针,而非指向常量字符的指针。

遇到类型别名时,人们往往会错误地尝试把类型别名展开,以理解该语句的含义,这是错误的!
例如:

typedef char * pstring;
const pstring cstr = 0;
展开成:
const char * cstr = 0;//是对const pstring cstr 的错误理解

声明语句中用到的pstring时,其基本数据类型是指针。可是展开之后数据类型就变成了char,星号成了声明符的一部分。这样展开的结果是,const char成了基本数据类型。展开前声明了一个指向char的常量指针,展开后则声明了一个指向const char的指针。

验证程序如下:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    char arr[10] = "Hello";
    typedef char *pstring;
    const pstring cstr = 0;
    const pstring *ps;

    cout << std::is_same<decltype(cstr), char *const>::value << endl;
    cout << std::is_same<decltype(ps),char *const *>::value << endl;

    //cstr = arr;//错误,cstr是一个指向char的常量指针,不变的是指针本身的值而非指向的那个值
    return 0;
}

如果使用typeid验证类型的话需要注意typeid会忽略顶层的cv-qualifier(const或volatile).验证程序:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    typedef char *pstring;
    const pstring cstr = 0;
    const pstring *ps;

    cout << typeid(cstr).name() <<endl;
    cout << typeid(ps).name() <<endl;

    return 0;
}

执行结果:

[root workspace]#./a.out
Pc
PKPc
[root workspace]#
[root workspace]#./a.out | c++filt -t
char*
char* const*
[root workspace]#
[root workspace]#echo PKPc | c++filt -t
char* const*
[root workspace]#echo Pc | c++filt -t
char*
[root workspace]#

确定函数调用原型

C++语言的名字修饰
extern C

#include <iostream>
using namespace std;

void print(int i) {
    cout << " Here is int " << i << endl;
}
void print(double f) {
    cout << " Here is float " << f << endl;
}
void print(char const *c) {
    cout << " Here is char* " << c << endl;
}

int main() 
{
    print(10);
    print(10.10);
    print("ten");
    return 0;
}

编译后,我们使用nm查看其中的符号:

[root c++]#nm a.out
0000000000202010 B __bss_start
0000000000202130 b completed.7697
                 U __cxa_atexit@@GLIBC_2.2.5
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000202000 D __data_start
0000000000202000 W data_start
0000000000000880 t deregister_tm_clones
0000000000000910 t __do_global_dtors_aux
0000000000201d78 t __do_global_dtors_aux_fini_array_entry
0000000000202008 D __dso_handle
0000000000201d80 d _DYNAMIC
0000000000202010 D _edata
0000000000202138 B _end
0000000000000b54 T _fini
0000000000000950 t frame_dummy
0000000000201d68 t __frame_dummy_init_array_entry
0000000000000db4 r __FRAME_END__
0000000000201f80 d _GLOBAL_OFFSET_TABLE_
0000000000000abc t _GLOBAL__sub_I__Z5printi
                 w __gmon_start__
0000000000000ba8 r __GNU_EH_FRAME_HDR
00000000000007b0 T _init
0000000000201d78 t __init_array_end
0000000000201d68 t __init_array_start
0000000000000b60 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000000b50 T __libc_csu_fini
0000000000000ae0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000000a39 T main
00000000000008c0 t register_tm_clones
0000000000000850 T _start
0000000000202010 D __TMC_END__
0000000000000a73 t _Z41__static_initialization_and_destruction_0ii
00000000000009a0 T _Z5printd
000000000000095a T _Z5printi
00000000000009f0 T _Z5printPKc
                 U _ZNSolsEd@@GLIBCXX_3.4
                 U _ZNSolsEi@@GLIBCXX_3.4
                 U _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
                 U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
                 U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
0000000000202020 B _ZSt4cout@@GLIBCXX_3.4
                 U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
0000000000000b68 r _ZStL19piecewise_construct
0000000000202131 b _ZStL8__ioinit
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4

我们可以看到符号中有print的相关信息:

00000000000009a0 T _Z5printd
000000000000095a T _Z5printi
00000000000009f0 T _Z5printPKc

我们上面的链接维基百科部分给了清晰明确的解释,简单说是由于C++支持函数重载需要将类型信息等加到函数名中,否则无法处理同名情况,开头_Z,然后是函数名长度,最后是参数类型。针对简单的函数重载我们可以清晰看到具体调用了哪个函数,那么如果复杂一点的呢?比如最后一行的_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc?

依旧可以使用c++filt工具

[root c++]#c++filt -t _Z5printPKc
print(char const*)
[root c++]#
[root c++]#c++filt -t _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)