Difference: 1. Go language can use the new keyword to allocate memory and create pointers of specified types, but C language cannot. 2. The array name arr in the C language represents the address of the first element of the array, which is equivalent to "&arr[0]"; the array name arr in the Go language does not represent the address of the first element of the array, but represents the value of the entire array. 3. Go language does not support pointer arithmetic, but C language supports pointer arithmetic. 4.
The operating environment of this tutorial: Windows 7 system, GO version 1.18, Dell G3 computer.
C and Go are both languages with pointer concepts. This article mainly uses the similarities and differences between the two to deepen the understanding and use of Go pointers.
Operator
Both C and Go are the same:
&
Operator takes out the location of the variable The memory address*
operator takes out the value in the memory address pointed to by the pointer variable, also called "Dereference"
C language version example:
#include <stdio.h> int main() { int bar = 1; // 声明一个指向 int 类型的值的指针 int *ptr; // 通过 & 取出 bar 变量所在的内存地址并赋值给 ptr 指针 ptr = &bar; // 打印 ptr 的值(为地址),*prt 表示取出指针变量所指向的内存地址里面的值 printf("%p %d\n", ptr, *ptr); return (0); } // 输出结果: // 0x7ffd5471ee54 1</stdio.h>
Go language version example:
package main import "fmt" func main() { bar := 1 // 声明一个指向 int 类型的值的指针 var ptr *int // 通过 & 取出 bar 变量所在的内存地址并赋值给 ptr 指针 ptr = &bar // 打印 ptr 变量储存的指针地址,*prt 表示取出指针变量所指向的内存地址里面的值 fmt.Printf("%p %d\n", ptr, *ptr) } // 输出结果: // 0xc000086020 1
Go can also use the new
keyword to allocate memory Creates a pointer of the specified type.
// 声明一个指向 int 类型的值的指针 // var ptr *int ptr := new(int) // 通过 & 取出 bar 变量所在的内存地址并赋值给 ptr 指针 ptr = &bar
Array name and array first address
For an array
// C int arr[5] = {1, 2, 3, 4, 5}; // Go // 需要指定长度,否则类型为切片 arr := [5]int{1, 2, 3, 4, 5}
In C, the array name arr
represents The address of the first element of the array is equivalent to &arr[0]
and &arr
represents the first address of the entire array arr
// C // arr 数组名代表数组首元素的地址 printf("arr -> %p\n", arr); // &arr[0] 代表数组首元素的地址 printf("&arr[0] -> %p\n", &arr[0]); // &arr 代表整个数组 arr 的首地址 printf("&arr -> %p\n", &arr); // 输出结果: // arr -> 0061FF0C // &arr[0] -> 0061FF0C // &arr -> 0061FF0C
Run the program It can be found that the output values of arr
and &arr
are the same, but their meanings are completely different.
First of all, the array name arr
as an identifier is the address of arr[0]
, viewed from the perspective of &arr[0]
It is a pointer to a value of type int.
And &arr
is a pointer to a value of type int[5].
You can further verify the pointer offset
// C // 指针偏移 printf("arr+1 -> %p\n", arr + 1); printf("&arr+1 -> %p\n", &arr + 1); // 输出结果: // arr+1 -> 0061FF10 // &arr+1 -> 0061FF20
This involves the knowledge of offset: the movement of a pointer of type T
is based on sizeof(T)
is the moving unit.
arr 1
: arr is a pointer to a value of type int, so the offset is1*sizeof(int)
##&arr 1
: &arr is a pointer pointing to int[5], its offset is
1*sizeof(int)*5
arr and
&arr in C language. Next, let’s take a look at Go language
// 尝试将数组名 arr 作为地址输出 fmt.Printf("arr -> %p\n", arr) fmt.Printf("&arr[0] -> %p\n", &arr[0]) fmt.Printf("&arr -> %p\n", &arr) // 输出结果: // arr -> %!p([5]int=[1 2 3 4 5]) // &arr[0] -> 0xc00000c300 // &arr -> 0xc00000c300
&arr[0] and
&arr are consistent with C language.
arr in Go is no longer the address of the first element of the array, it represents the value of the entire array, so the output will prompt
%!p([5 ]int=[1 2 3 4 5])
Pointer arithmetic
A pointer is essentially an unsigned integer, representing a memory address. Pointers and integer values can be added and subtracted, such as the pointer offset example above:- Add
n
: A type is The pointer of
Tmoves to the high bit in units of
n*sizeof(T).
- minus
n
: A pointer of type
T, in units of
n*sizeof(T)Move low.
sizeof(T) represents the bytes occupied by the data type, for example
int is 4 bytes in a 32-bit environment , 8 bytes in 64-bit environment
#include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}; // ptr 是一个指针,为 arr 数组的第一个元素地址 int *ptr = arr; printf("%p %d\n", ptr, *ptr); // ptr 指针向高位移动一个单位,移向到 arr 数组第二个元素地址 ptr++; printf("%p %d\n", ptr, *ptr); return (0); } // 输出结果: // 0061FF08 1 // 0061FF0C 2</stdio.h>Here
ptr moved from
0061FF08
sizeof( int) = 4 bytes to
0061FF0C, pointing to the address of the next array element
package main import "fmt" func main() { arr := [5]uint32{1, 2, 3, 4, 5} // ptr 是一个指针,为 arr 数组的第一个元素地址 ptr := &arr[0] fmt.Println(ptr, *ptr) // ptr 指针向高位移动一个单位,移向到 arr 数组第二个元素地址 ptr++ fmt.Println(ptr, *ptr) } // 输出结果: // 编译报错: // .\main.go:13:5: invalid operation: ptr++ (non-numeric type *uint32)
编译报错 *uint32
非数字类型,不支持运算,说明 Go 是不支持指针运算的。
这个其实在 Go Wiki[1] 中的 Go 从 C++ 过渡文档中有提到过:Go has pointers but not pointer arithmetic.
Go 有指针但不支持指针运算。
另辟蹊径
那还有其他办法吗?答案当然是有的。
在 Go 标准库中提供了一个 unsafe
包用于编译阶段绕过 Go 语言的类型系统,直接操作内存。
我们可以利用 unsafe
包来实现指针运算。
func Alignof(x ArbitraryType) uintptr func Offsetof(x ArbitraryType) uintptr func Sizeof(x ArbitraryType) uintptr type ArbitraryType func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType type IntegerType type Pointer func Add(ptr Pointer, len IntegerType) Pointer
核心介绍:
uintptr
: Go 的内置类型。是一个无符号整数,用来存储地址,支持数学运算。常与unsafe.Pointer
配合做指针运算unsafe.Pointer
: 表示指向任意类型的指针,可以和任何类型的指针互相转换(类似 C 语言中的void*
类型的指针),也可以和uintptr
互相转换unsafe.Sizeof
: 返回操作数在内存中的字节大小,参数可以是任意类型的表达式,例如fmt.Println(unsafe.Sizeof(uint32(0)))
的结果为4
unsafe.Offsetof
: 函数的参数必须是一个字段 x.f,然后返回 f 字段相对于 x 起始地址的偏移量,用于计算结构体成员的偏移量
原理:
Go 的 uintptr
类型存储的是地址,且支持数学运算
*T
(任意指针类型) 和 unsafe.Pointer
不能运算,但是 unsafe.Pointer
可以和 *T
、 uintptr
互相转换
因此,将 *T
转换为 unsafe.Pointer
后再转换为 uintptr
,uintptr
进行运算之后重新转换为 unsafe.Pointer
=> *T
即可
代码实现:
package main import ( "fmt" "unsafe" ) func main() { arr := [5]uint32{1, 2, 3, 4, 5} ptr := &arr[0] // ptr(*uint32类型) => one(unsafe.Pointer类型) one := unsafe.Pointer(ptr) // one(unsafe.Pointer类型) => *uint32 fmt.Println(one, *(*uint32)(one)) // one(unsafe.Pointer类型) => one(uintptr类型) 后向高位移动 unsafe.Sizeof(arr[0]) = 4 字节 // twoUintptr := uintptr(one) + unsafe.Sizeof(arr[0]) // !!twoUintptr 不能作为临时变量 // uintptr 类型的临时变量只是一个无符号整数,并不知道它是一个指针地址,可能被 GC // 运算完成后应该直接转换回 unsafe.Pointer : two := unsafe.Pointer(uintptr(one) + unsafe.Sizeof(arr[0])) fmt.Println(two, *(*uint32)(two)) } // 输出结果: // 0xc000012150 1 // 0xc000012154 2
甚至还可以更改结构体的私有成员:
// model/model.go package model import ( "fmt" ) type M struct { foo uint32 bar uint32 } func (m M) Print() { fmt.Println(m.foo, m.bar) } // main.go package main import ( "example/model" "unsafe" ) func main() { m := model.M{} m.Print() foo := unsafe.Pointer(&m) *(*uint32)(foo) = 1 bar := unsafe.Pointer(uintptr(foo) + 4) *(*uint32)(bar) = 2 m.Print() } // 输出结果: // 0 0 // 1 2
小 Tips
Go 的底层 slice
切片源码就使用了 unsafe
包
// slice 切片的底层结构 type slice struct { // 底层是一个数组指针 array unsafe.Pointer // 长度 len int // 容量 cap int }
总结
Go 可以使用
&
运算符取地址,也可以使用new
创建指针Go 的数组名不是首元素地址
Go 的指针不支持运算
Go 可以使用
unsafe
包打破安全机制来操控指针,但对我们开发者而言,是 "unsafe" 不安全的
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of What is the difference between go language and c language on pointers?. For more information, please follow other related articles on the PHP Chinese website!

VScode中怎么配置C语言环境?下面本篇文章给大家介绍一下VScode配置C语言环境的方法(超详细),希望对大家有所帮助!

在C语言中,node是用于定义链表结点的名称,通常在数据结构中用作结点的类型名,语法为“struct Node{...};”;结构和类在定义出名称以后,直接用该名称就可以定义对象,C语言中还存在“Node * a”和“Node* &a”。

c语言将数字转换成字符串的方法:1、ascii码操作,在原数字的基础上加“0x30”,语法“数字+0x30”,会存储数字对应的字符ascii码;2、使用itoa(),可以把整型数转换成字符串,语法“itoa(number1,string,数字);”;3、使用sprintf(),可以能够根据指定的需求,格式化内容,存储至指针指向的字符串。

在c语言中,没有开根号运算符,开根号使用的是内置函数“sqrt()”,使用语法“sqrt(数值x)”;例如“sqrt(4)”,就是对4进行平方根运算,结果为2。sqrt()是c语言内置的开根号运算函数,其运算结果是函数变量的算术平方根;该函数既不能运算负数值,也不能输出虚数结果。

C语言数组初始化的三种方式:1、在定义时直接赋值,语法“数据类型 arrayName[index] = {值};”;2、利用for循环初始化,语法“for (int i=0;i<3;i++) {arr[i] = i;}”;3、使用memset()函数初始化,语法“memset(arr, 0, sizeof(int) * 3)”。

c语言合法标识符的要求是:1、标识符只能由字母(A~Z, a~z)、数字(0~9)和下划线(_)组成;2、第一个字符必须是字母或下划线,不能是数字;3、标识符中的大小写字母是有区别的,代表不同含义;4、标识符不能是关键字。

c语言编译后生成“.OBJ”的二进制文件(目标文件)。在C语言中,源程序(.c文件)经过编译程序编译之后,会生成一个后缀为“.OBJ”的二进制文件(称为目标文件);最后还要由称为“连接程序”(Link)的软件,把此“.OBJ”文件与c语言提供的各种库函数连接在一起,生成一个后缀“.EXE”的可执行文件。

c语言计算n的阶乘的方法:1、通过for循环计算阶乘,代码如“for (i = 1; i <= n; i++){fact *= i;}”;2、通过while循环计算阶乘,代码如“while (i <= n){fact *= i;i++;}”;3、通过递归方式计算阶乘,代码如“ int Fact(int n){int res = n;if (n > 1)res...”。


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

SublimeText3 Linux new version
SublimeText3 Linux latest version

SublimeText3 Chinese version
Chinese version, very easy to use

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Mac version
God-level code editing software (SublimeText3)
