((void()())0)();
可改写为以下阐述:
typedef void (funcptr)();( (funcptr) 0)();
godbolt :直接在网页上看到gcc天生的程序源码
godbolt链接:https://gcc.godbolt.org/
int main() { typedef void (funcptr)(); ( (funcptr) (void ) 0)(); }
对应的组合措辞,搭配-Os(空间最佳化)
main: pushq %rax xorl %eax, %eax call %rax xorl %eax, %eax popq %rdx ret
一道口试题:
void (d) (int &, char ()(char , char ));
上述声明的解读:
d is a pointer to a function that takes two parameters:a reference to an int anda pointer to a function that takes two parameters:a pointer to a char and
a pointer to a pointer to a char
3.and returns a pointer to a pointer to a char
and returns a pointer to a pointer to voidsignal函数的声明办法也很经典:
How to read this prototype?
链接:https://stackoverflow.com/questions/15739500/how-to-read-this-prototype
Go 措辞也有指针1999年4月27日,Ken Thompson和Dennis Ritchie自美国总统柯林顿手中接过1998年National Medal of Technology (国家科技奖章),隔年12月,时年58岁的Ken Thompson自贝尔实验室退休。Ken Thompson自贝尔实验室退休后成为一名翱翔员。大概是整日翱翔天涯,得到颇多启示,在2006年,他进入Google事情,隔年他和过去贝尔实验室的同寅Rob Pike及Robert Griesemer等人在公司内部提出崭新的Go程序措辞,后者可用于云端运算在内的浩瀚领域。
指针这个好东西,当然也要从C 措辞带过去给Go 措辞,连同美妙的struct。
根据第一份Golang Talk,Robert Griesemer, Ken Thompson及Rob Pike等三人认为,天下在变,但系统措辞却已十年未有剧烈变革Go 之前的程序措辞未能达到:新增函数库不是一个精确的方向
须要重新思考全体架构来开拓新的程序措辞
在实践层面,pointer 和struct 每每是成双成对存在(下方会阐明)
先罗列你已经知道的部分C 措辞: 超好懂的指针
链接地址:https://kopu.chat/2017/05/15/c%E8%AA%9E%E8%A8%80-%E8%B6%85%E5%A5%BD%E6%87%82%E7%9A%84%E6%8C%87%E6%A8%99%EF%BC%8C%E5%88%9D%E5%AD%B8%E8%80%85%E8%AB%8B%E9%80%B2%EF%BD%9E/
Everything you need to know about pointers in C
链接地址:https://boredzo.org/pointers/
迷惑该如何阐明qsort(3)的参数和设计考量呢?
为何我看到的程序每每写成类似下面这样?
struct list lpp; for (lpp = &list; lpp != NULL; lpp = &(lpp)->next)
转头看C 措辞规范在开拓工具和规范标准篇提过参考第一手资料的主要性,以下ISO/IEC 9899 (简称“C99”)和指针干系的描述:
规范 征采“ object ”,共涌现735处征采“ pointer ”,共涌现637处。有趣的是,许多教材每每不谈object,而是急着评论辩论pointer,殊不知,这两者实在便是一体两面
object != object-oriented前者的重点在于「数据表达法」,后者的重点在于“everything is object”
C11 ( ISO/IEC 9899:201x ) / 网页版
& 不要都念成and,涉及指针操作的时候,要读为“address of”C99 标准[6.5.3.2] Address and indirection operators 提到'&' address-of operator
C99 [3.14] objectregion of data storage in the execution environment, the contents of which can represent values
C 措辞的工具就指在实行期间,数据储存的区域,可以明确表示数值的内容
很多人误认在C 措辞程序中,(int) 7 和(float) 7.0 是等价的,实在以数据表示的角度来看,这两者截然不同,前者对应到二进位的“111”,而后者以IEEE 754 表示则大异于“111”
C99 [6.2.4] Storage durations of objectsAn object has a storage duration that determines its lifetime. There are three storage durations: static, automatic, and allocated.
把稳生命周期(lifetime)的观点,中文讲「初始化」时,觉得像是「盘古开天」,很随意马虎令人误解。实在initialize的英文意义很狭隘: “to set (variables, counters, switches, etc.) to their starting values at the beginning of a program or subprogram.”
The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined.
在object 的生命周期以内,其存在就意味着有对应的常数内存地址。把稳,C 措辞永久只有call-by-value
The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
作为object操作的「代名词」(alias)的pointer,倘若要在object生命周期以外的机遇,去取出pointer所指向的object内含值,是未知的。考虑先做ptr = malloc(size); free(ptr);倘若之后做ptr,这个allocated storage已经超出原来的生命周期
An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration.
C99 [6.2.5] TypesA pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type. A pointer type derived from the referenced type T is sometimes called ''pointer to T''. The construction of a pointer type from a referenced type is called ''pointer type derivation''.
把稳到术语!
这是C措辞只有call-by-value的实证,函数的通报都涉及到数值,这里的“incomplete type”要把稳看,稍后会阐明。要区分char []和charArithmetic types and pointer types are collectively called scalar types. Array and structure types are collectively called aggregate types.
把稳“scalar type”这个术语,日后我们看到++, --, +=, -=等操作,都是对scalar (标量)标量只有大小,它们可用数目及单位来表示(例如温度= 30 o C)。标量遵守算数和普通的代数法则。把稳:标量有「单位」(可用sizeof操作得知单位的「大小」),假设ptr是个pointer type,对ptr++来说,并不是纯挚ptr = ptr + 1,而是递增或递移1个「单位」
An array type of unknown size is an incomplete type. It is completed, for an identifier of that type, by specifying the size in a later declaration (with internal or external linkage). A structure or union type of unknown content is an incomplete type . It is completed, for all declarations of that type, by declaring the same structure or union tag with its defining content later in the same scope.
这是C/C++常见的forward declaration技巧的事理,比方说我们可以在头文件声明struct GraphicsObject;(不用给内部定义)然后struct GraphicsObject initGraphics(int width, int height);是合法的,但struct GraphicsObject obj;不合法
Array, function, and pointer types are collectively called derived declarator types. A declarator type derivation from a type T is the construction of a derived declarator type from T by the application of an array-type, a function-type, or a pointer- type derivation to T.
这句话很主要,看起来三个不干系的术语「数组」、「函数」,以及「指针」都称为derived declarator types,读到此处会以为惊异的人,表示不足理解C 措辞
“derivative”这词在是微积分学中便是导数。一个函数在某一点的导数描述了这个函数在这一点附近的变革率。导数的实质是通过极限的观点对函数进行局部的线性逼近。
回到C 措辞,你看到一个数值,是scalar,但可能也是自某个派生出的declarator type derivation,实际对应到array, function, pointer 等型态的derivation
(练习题)设定绝对地址为0x67a9的32-bit整数变数的值为0xaa6,该如何写?
(int32_t const) (0x67a9) = 0xaa6;/ Lvalue /
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.关键描述!
规范void 和char 彼此可互换的表示法
void memcpy(void dest, const void src, size_t n);
英文很主要安装cdecl程序,可以帮你产生C程序声明。
$ sudo apt-get install cdecl
利用案例:
$ cdeclcdecl> declare a as array of pointer to function returning pointer to function returning pointer to char
会得到以下输出:
char ((a[])())()
把前述C99 规范的描述带入,可得:
cdecl> declare array of pointer to function returning struct tagstruct tag (var[])()
如果你没办法用英文来讲授C 程序的声明,常日表示你不理解!
cdecl 可阐明C 程序声明的意义,比方说:
cdecl> explain char (fptab[])(int)declare fptab as array of pointer to function (int) returning pointer to char
void 之谜void 在最早的C 措辞是不存在的,直到C89 才确立,为何要设计这样的类型呢?
链接地址:https://www.bell-labs.com/usr/dmr/www/primevalC.html
最早的C措辞中,任何函数若没有特殊标注返回类型,一律变成int(伴随着0作为返回值),但这导致无从验证function prototype和实际利用的状况
void 的设计,导致开拓者必须透过 explicit (显式) 或逼迫转型,才能存取终极的object,否则就会丢出编译器的缺点信息,从而避免危险的指针操作我们无法直接对void 做数值操作
void p = ...; void p2 = p + 1; / what exactly is the size of void? /
换句话说,void 存在的目的便是为了强制利用者利用显式转换类型或是逼迫转换类型,以避免Undefined behavior 产生
C/C++ Implicit conversion vs. Explicit type conversion
C99对sign extension的定义和解释
链接地址:https://www.ptt.cc/bbs/C_and_CPP/M.1460791524.A.603.html
对某硬件架构,像是ARM,我们须要额外的 alignment。ARMv5 (含)以前,若要操作32-bit整数(uint32_t),该指针必须对齐32-bit边界(否则会在dereference时触发exception)。于是,当要从void 位址读取uint16_t时,须要这么做:
/ may receive wrong value if ptr is not 2-byte aligned // portable way of reading a little-endian value /uint16_t value = (uint16_t ) ptr;uint16_t value = (uint8_t ) ptr | (((uint8_t ) (ptr + 1)) << 8);
延伸阅读: 内存管理、对齐及硬件特性
void 真的万能吗?依据C99 规格6.3.2.3:8 [ Pointers ]
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
换言之,C99不担保pointers to data (in the standard, “objects or incomplete types” eg char or void )和pointers to functions之间相互转换是精确的
可能会招致undefined behavior (UB)把稳:C99规范中,存在一系列的UB,详见未定义行为篇延伸阅读C 措辞内存定址根本The Lost Art of Structure Packing
链接地址:http://www.catb.org/esr/structure-packing/
没有「双指针」只有「指针的指针」「双马尾」(旁边「独立」的个体) 和「马尾的马尾」(由单一个体关联到另一个体的对应) 不同
中文的「双」有「对称」且「独立」的含义,但这跟「指针的针」行为完备迥异讲「双指针」已经不是「懂不懂C 措辞」,而是措辞认知的问题C 措辞中,万物皆是数值(everything is a value),函数调用当然只有call-by-value。「指针的指针」(英文便是a pointer of a pointer) 是个常见用来改变「传入变量原始数值」的技巧
考虑以下程序:
int B = 2;void func(int p) { p = &B; }int main() { int A = 1, C = 3; int ptrA = &A; func(ptrA); printf("%d\n", ptrA); return 0;}
在第5 行(含) 之前的内存示意:
第6行ptrA数值传入func后的内存示意:
func依据函数调用篇描述,将变数p的值指定为&B
由上图可见,原来在main中的ptrA内存值没有改变。这不是我们期望的结果,该如何战胜呢?可透过「指针的指针」来改写,如下:
int B = 2;void func(int p) { p = &B; }int main() { int A = 1, C = 3; int ptrA = &A; func(&ptrA); printf("%d\n", ptrA); return 0;}:
在第5 行(含) 之前的内存示意:
第6行时,传入func的参数是&ptrA,也便是下图中的&ptrA(temp):
进入func实行后,在编译器产生对应参数通报的程序中,会复制一份刚传入的&ptrA,产生一个自动变量p,将&ptrA内的值存在个中,示意如下:
在func中把p指向到的值换成&B:
经由上述「指针的指针」,ptrA的数值从1变成2,而且ptrA指向的工具也改变了。
未完待续