C++ 引用 解引用 取地址 指针 双指针 指针的引用 二级指针

本文详细解析了C/C++中的引用、取地址、指针、双指针等概念的区别与联系,通过实例说明了如何正确使用这些特性进行编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了把引用 解引用 取地址 指针 双指针 指针的引用 二级指针 这一块弄清楚,钻研与查阅资料得到如下体会;

1.        引用与取地址  (reference and address)  引用的&前面一定有类型符号,否则就不是引用, 取地址的&后面一定有变量名称,一旦出现&,一定把此搞明白

 int b =10, int &a,  这明显就是引用,&b 这就是取地址, test1(int &a, int &b) 这些是引用用在定义函数的时候, test4(&a, &b) 这个时候是取地址,&后面必须是变量名字,不能是其它的,比如&*b是不容许的,

2.       引用是变量的别名,与变量有相同的地址和值, & 只在定义的时候出现,以后不用出现, int &a =b,   以后直接在使用的时候用a 代表b, 不是用&a 代表b (单独出现&a,此时就是应用的地址了, 它就是b的地址)  &a==&b,地址是相等的。函数中如传递变量,  f(b), 如需要传递变量的地址f(&b)

3.     一个变量(包括指针)包括三个部分, 变量名字, 变量的值, 变量的地址,内存中地址是唯一的, 生命周期内名字和地址是关联的,多个名字可关联一个地址,值是地址上的储存内容,可以变的,名字没有直接和值关联,名字关联地址,地址在关联值, 这三个是三回事,地址和引用,地址,解引用就是解决此三部分不同的问题。

     一般的赋值等是对变量的值赋值,而不是地址等其他的, int b=10, 是b的值赋为10, 而不是地址 int *f , f=&b, 这是把指针的值赋为b的地址,而指针f本省的地址没有涉及

4.     指针与解引用 (pointer and dereference)  指针的*前面一定有类型符号,否则就不是指针, 解引用的*后面一定要有地址(指针或相当于值是地址的变量)一旦出现×,一定高明,  int * f, 只是指针定义, 而如果单独出现 *f 这个时候是解引用,  test8(int ×a, int ×b) 这些是指针用在定义函数的时候, test9(×a, ×b) 这个时候是解引用,传递的指针对应的解引用, ×&f  这个就是变量f的地址的解引用,就是变量f的值

5 . 指针里面的值是变量的地址, × 只在定义的时候出现,以后不出现, int*f,   f=&b,  以后直接用f 代表 &b, 用*f(解引用)代表b, 代表是可以写等于的意思。而不是以后也用*f=&b, f=b.

6. 指针就是变量本身储存的值是另一个变量的地址, 和它本身的地址是两回事, 解引用是指针的值(地址)作为另一个变量的地址, 这个地址对应的值。int *f =&b, 

此时f的地址是:HHH, f的值是AAA,  b的地址是AAA, b的值是CCC,  HHH 就是&f, ×f 就是CCC。二级指针就是指向指针的指针,相当于这里的CCC又是一个变量的地址, 

7. 作为参数传递等的时候,一定要抛开类新定义时前两*或&的符号,左边的值与右边建立对等关系 ,左边值是地址,右边就是地址,左边值是值, 右边也就是普通的值,  如 fee(int* f), int k,  在实参引用的时候要fee(&h), 不能fee(h), 因为定义中*是类型符号,f的值是地址, 要与&h才一直,  fee(int &h) , int k,  在实参引用的时候要fee(k). 因为&是类型符,h是引用,值和变量一样, 而不能是地址参数fee(&h)

8.  引用不分配内存, 而指针分配, int d, int& f=d,, int *u =&d , 此时d和f 的值, 地址一模一样,u的值和d的地址上一样, u有它自己的地址。

9. 作为参数传递,指针作为参数的时候是临时拷贝一个副本( 不同的地址),它的值和原来的指针一样,及指向的地址和原来的指针指向的一样,他们有相同的解引用,因此对函数内这个临时指针的值的操作都不会影响到原来的指针,只是把临时指针的值变了,及指的地址变了,如果不对这个临时指针操作,而对它的解引用操作, 由于和原来的指针指向同一个地址,有相同的解引用,因此对临时指针的解引用操作会影响到原来的解引用,要在函数退出后而使原来的值的变化有保留,就必须把要改变的值放在传递指针的解引用里面。 要想让指针作为参数在函数中操作完后有变化有保留,因此要让产生的临时指针指向这个指针的地址,临时指针的解引用就是这个指针,临时指针的解引用变化,这个指针的值就变化,这个指针的解引用,和临时指针的解引用的解引用一致,就是二级指针或双指针。及指针用作参数传出时需要二级指针。

int *a=&b, 

int c*=&d,   

exchange(int*f, int*e),  

{

  int * temp 

temp =f;

f=e,

e=temp

}

exchange(a, c)

执行时; 

f 是一个临时指针指向b的地址, *f就是b    函数外a 指向b的地址

e是一个临时指针指向d的地址, *e就是d   函数外c指向d的地址

交换后

f 是一个临时指针指向d的地址, *f就是d    函数外a 指向b的地址

e是一个临时指针指向b的地址, *e就是b   函数外c指向d的地址

因此函数内部变了,外部没有变, 退出后还是原样子。

如果是

exchange(int*f, int*e),  

{

  int  temp 

temp =*f;

*f=*e,

*e=temp

}


exchange(a, c)


此时执行

f 是一个临时指针指向b的地址, *f就是b    函数外a 指向b的地址 *a就是b

e是一个临时指针指向d的地址, *e就是d   函数外c指向d的地址  *c就是d

交换后

f 是一个临时指针指向b的地址, *f就是d    函数外a 指向b的地址 *a就是b   但此时 b的值变成d的值了,  b的名称和地址没有变

e是一个临时指针指向d的地址, *e就是b   函数外c指向d的地址  *c就是d   但此时 d的值变成b的值了      d的名称和地址没有变

如前名称和地址是绑定的, 这次是把值变了


如果是

exchange(int**f, int**e),  

{

  int * temp 

temp =*f;

*f=*e,

*e=temp

}


exchange(&a, &c)  注意此时格式

执行时 


f 是一个临时指针指向指针a的地址, *f就是a,    * a就是b, **f就是 b,   函数外a 指向b的地址 *a就是b

e是一个临时指针指向指针c的地址, *e就是c    *c就是d, **e就是 d     函数外c指向d的地址  *c就是d

执行后

f 是一个临时指针指向指针a的地址, *f就是a, 此时a的名称和地址没有变,但值变成了c的值   * a就指向* c 即 d, **f就是 d,   函数外a 指向d的地址 *a就是d

e是一个临时指针指向指针c的地址, *e就是c  此时c的名称和地址没有变, 但值变成a的值,  *c就指向*a  即 b, **e就是 b     函数外c指向d的地址  *c就是b

这样的操作是在指针的值的层面上做了交换,上面一种是在原数据基础上做了交换。 

10. 引用作为参数,是直接把实参的地址和值当作引用的,起了个别名,相当于给这段地址关联了一个名字,相比指针少了一个临时的指针的地址。因此函数内部对别名的操作就相当于对原实参操作。

exchange(int &f, int &e),  

{

  int  temp 

temp =f;

f=e,

e=temp

}

exchange(b, d)

执行时
f 的值是b, f的地址是b 的地址

e的值是d, e的地址是 d 的地址

执行后

 f的值是c的值, f 的地址还是原来b的地址 b的地址中变成c的值了

e的值变为d的值, e的地址还是原来b的地址, c的地址中变成b的值了,

在数值层面上交换了。

11. 引用和指针相结合,产生了指针类型的引用 int *&f  它的意思是f和一个指针有相同的地址和值。

exchange(int *&f, int *&e),  

{

  int  temp 

temp =f;

f=e,

e=temp

}

exchange(a, c)  注意里面一定要指针

执行时   f  就相当于指针  a

e 就相当于指针c, 

 这样相当于把外面指针直接拿到函数内使用了。 比刚才二级指针效率提升了许多。

引申 

一般的情况是

            二级指针------> 一级指针---->原数据

 要想原数据改变有多种方式

           直接改变 

           利用一级指针做中介 改变一级指针的解引用

           利用二级指针做中介,改变二级指针的解引用, 此时绑定一级指针值和原数据,

           使用指针作为中介的地方都可直接使用引用。去掉中介。


-------------










<think>我们正在讨论CFFI中的指针类型转换。根据引用[1]和[2],在C语言中,我们通常使用`intptr_t`或`uintptr_t`来将指针转换为足够大的整数类型,以保证不丢失信息。在CFFI中,我们可以使用`ffi.cast`函数进行类型转换。 引用[3]提到ctypes中的类型别名和继承关系,但这里我们主要关注CFFI。 在CFFI中,指针类型转换的常见场景包括: 1. 将指针转换为整数(用于打印或存储) 2. 将整数转换回指针 3. 在不同类型的指针之间转换(例如,将`void*`转换为具体类型的指针) 下面我们分别介绍这些情况: ### 1. 指针转整数 在CFFI中,我们可以使用`ffi.cast`将指针转换为整数。为了可移植性,应该使用`intptr_t`或`uintptr_t`类型。 ```python from cffi import FFI ffi = FFI() # 分配一个整数 int_ptr = ffi.new("int*", 42) # 将指针转换为整数(使用intptr_t) addr = ffi.cast("intptr_t", int_ptr) print(addr) # 打印一个整数地址 # 或者使用uintptr_t addr_unsigned = ffi.cast("uintptr_t", int_ptr) ``` 注意:直接打印`addr`会显示一个整数,这个整数就是指针的地址值。 ### 2. 整数转指针 同样,我们可以将整数转换回指针。注意,确保这个整数是之前由指针转换而来的有效地址。 ```python # 将整数转换回指针 int_ptr2 = ffi.cast("int*", addr) print(int_ptr2[0]) # 应该输出42 ``` ### 3. 指针类型之间的转换 有时我们需要将一种类型的指针转换为另一种类型,例如将`void*`转换为`int*`。 ```python # 创建一个void指针 void_ptr = ffi.cast("void*", int_ptr) # 将void指针转换回int指针 int_ptr3 = ffi.cast("int*", void_ptr) print(int_ptr3[0]) # 输出42 ``` ### 4. 结构体指针转换 如果需要在不同结构体指针之间转换,或者将结构体指针转换为基本类型指针,同样使用`ffi.cast`。 ```python ffi.cdef(""" struct Point { double x; double y; }; struct Pixel { int r; int g; int b; }; """) # 创建一个Point结构体指针 point = ffi.new("struct Point*", [1.0, 2.0]) # 转换为void指针 void_p = ffi.cast("void*", point) # 再转换为Pixel结构体指针(注意:这通常是不安全的,因为结构体布局不同) pixel = ffi.cast("struct Pixel*", void_p) # 访问pixel的成员(但这里point和pixel的内存布局不同,所以访问可能出错) # 因此,这种转换应谨慎使用,确保两种结构体在内存布局上兼容。 ``` ### 注意事项 - 类型转换可能导致未定义行为,特别是在不兼容的类型之间转换。例如,将一个`double*`转换为`int*`并访问,可能由于对齐问题或类型表示不同而导致错误。 - 在将指针转换为整数时,使用`intptr_t`或`uintptr_t`可以确保整数足够大以保存指针值。这是可移植的[^1]。 - 避免直接使用`int`或`long`来转换指针,因为它们的位数可能与指针不同(例如在64位系统上,指针是64位,而`int`可能是32位)。 ### 错误处理 如果转换的类型未定义,CFFI会抛出`ffi.error`。因此,在转换前确保已经用`ffi.cdef`声明了相关类型。 ### 示例:通过整数传递指针 有时我们需要在Python中存储指针,以便稍后使用。可以通过整数来存储: ```python # 存储指针 int_ptr = ffi.new("int*", 100) addr = ffi.cast("intptr_t", int_ptr) store_address = addr # 可以存储这个整数 # 以后使用 int_ptr_restored = ffi.cast("int*", store_address) print(int_ptr_restored[0]) # 输出100 ``` ### 总结 在CFFI中,使用`ffi.cast`进行指针类型转换,语法为: ```python new_ptr = ffi.cast("目标类型", 原指针或整数) ``` 目标类型可以是: - 整数类型:`intptr_t`, `uintptr_t` - 指针类型:`int*`, `void*`, `struct Point*`等 通过类型转换,我们可以在需要时改变指针的类型,但要注意转换的合法性和安全性。 §§相关问题§§ 1. CFFI中如何声明`intptr_t`和`uintptr_t`类型? 2. 在CFFI中,如何检查两个指针是否指向同一个地址? 3. 如何用CFFI处理`void*`类型的指针? 4. 在CFFI中,指针类型转换与C语言中的强制类型转换有何异同?</think>### CFFI 指针类型转换详解 在 CFFI 中进行指针类型转换是 C 语言互操作的关键操作,主要用于处理不同指针类型间的兼容性问题。以下是核心方法和注意事项: #### 一、基础指针转换 使用 `ffi.cast()` 函数进行显式类型转换: ```python from cffi import FFI ffi = FFI() # 创建原始指针 int_ptr = ffi.new("int*", 42) # 转换为 void 指针 void_ptr = ffi.cast("void*", int_ptr) # 转换回 int 指针 restored_ptr = ffi.cast("int*", void_ptr) print(restored_ptr[0]) # 输出: 42 ``` #### 二、指针与整数互转 处理指针和整数间的转换(如地址运算): ```python # 指针转整数 (使用 intptr_t) addr = ffi.cast("intptr_t", int_ptr) print(f"指针地址: 0x{addr:x}") # 输出 16 进制地址 # 整数转指针 (确保地址有效性) new_ptr = ffi.cast("int*", addr + 4) # 地址偏移 ``` #### 三、结构体指针转换 处理复杂结构体间的类型转换: ```python ffi.cdef(""" struct Point { float x, y; }; struct Color { uint8_t r, g, b; }; """) # 创建 Point 指针 point_ptr = ffi.new("struct Point*", [1.5, 2.5]) # 转换为 Color 指针 (需确保内存布局兼容) color_ptr = ffi.cast("struct Color*", point_ptr) color_ptr.r = 255 # 修改内存内容 ``` #### 四、类型安全注意事项 1. **对齐问题** 转换前检查类型大小和对齐: ```python print(ffi.sizeof("struct Point")) # 输出结构体大小 print(ffi.alignof("struct Color")) # 输出对齐要求 ``` 2. **未定义行为** 避免不兼容的类型转换: ```python # 危险操作:float* 转 int* float_ptr = ffi.new("float*", 3.14) int_ptr = ffi.cast("int*", float_ptr) print(int_ptr[0]) # 输出未定义结果 ``` 3. **内存安全** 使用 `ffi.buffer()` 检查原始内存: ```python buf = ffi.buffer(point_ptr) print(buf[:]) # 输出原始字节内容 ``` #### 五、实用技巧 1. **函数指针转换** ```python ffi.cdef("typedef int (*callback_t)(int);") @ffi.callback("int(int)") def py_func(x): return x * 2 # 转换为通用函数指针 generic_func = ffi.cast("void*", py_func) ``` 2. **数组指针转换** ```python int_arr = ffi.new("int[]", [1, 2, 3]) char_ptr = ffi.cast("char*", int_arr) # 访问第二个字节 print(char_ptr[1]) # 取决于系统字节序 ``` > **关键原则**: > 1. 始终使用 `intptr_t`/`uintptr_t` 进行指针-整数转换[^1] > 2. 避免 `void**` 等二级指针的直接转换[^2] > 3. 复杂转换前先用 `ffi.typeof()` 验证类型布局[^3] > ```python > print(ffi.typeof(int_ptr)) # <cdata 'int *'> > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张博208

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值