当前位置: 首页 > news >正文

golang基础语法(三)常量、指针、别名、关键字、运算符、字符串类型转换

Go语言中的常量使用关键字const定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,并且只能是布尔型数字型(整数型、浮点型和复数)字符串型

由于编译时的限制,定义常量的表达式必须为能被编译器求值的常量表达式。

一、常量声明格式

声明格式(type类型可以省略):

const name [type] = value

例如:

const pi = 3.14159

和变量声明一样,可以批量声明多个常量:

const (e  = 2.7182818pi = 3.1415926
)

所有常量的运算都可以在编译期完成,这样不仅可以减少运行时的工作,也方便其他代码的编译优化,当操作数是常量时,一些运行时的错误也可以在编译时被发现

例如:整数除零、字符串索引越界、任何导致无效浮点数的操作等。

常量间的所有算术运算、逻辑运算和比较运算的结果也是常量

对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex 和 unsafe.Sizeof。

因为它们的值是在编译期就确定的,因此常量可以是构成类型的一部分

如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式,对应的常量类型也是一样的。例如:

const (a = 1bc = 2d
)
fmt.Println(a, b, c, d) // "1 1 2 2"

iota常量声明器

常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。

在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加1

比如,定义星期日到星期六,从0-6

const (Sunday  = iota //0MondayTuesdayWednesdayThursdayFridaySaturday  //6
)

二、指针

指针(pointer)在Go语言中可以被拆分为两个核心概念:

  • 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算
  • 切片,由指向起始元素的原始指针、元素数量和容量组成

受益于这样的约束和拆分,拥有指针高效访问的特点,不会发生指针偏移,从而避免了非法修改关键性数据的问题。同时,垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。

切片比原始指针具备更强大的特性,而且更为安全。切片在发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。

1、如何理解指针

var a int = 10

如果用大白话来解释上述语句:

在内存中开辟了一片空间,空间内存放着数值10,这片空间在整个内存当中,有一个唯一的地址,用来进行标识,指向这个地址的变量就称为指针

一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节,占用字节的大小与所指向的值的大小无关。

当一个指针被定义后没有分配到任何变量时,它的默认值为 nil

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。

在变量名前面添加&操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:

//其中 v 代表被取地址的变量
//变量 v 的地址使用变量 ptr 进行接收
//ptr 的类型为*T,称做 T 的指针类型
//*代表指针。ptr := &v    // v 的类型为 T
package main
import ("fmt"
)
func main() {var cat int = 1var str string = "ms的go教程"fmt.Printf("%p %p", &cat, &str)
}

变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址

&操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用*操作符,也就是指针取值

    // 指针与变量var room int = 10  // room房间 里面放的 变量10var ptr = &room  // 门牌号px  指针  0xc00000a0a8
​fmt.Printf("%p\n", &room)  // 变量的内存地址 0xc00000a0a8
​fmt.Printf("%T, %p\n", ptr, ptr)  // *int, 0xc00000a0a8
​fmt.Println("指针地址",ptr)   // 0xc00000a0a8fmt.Println("指针地址代表的值", *ptr)  // 10

取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。

2、使用指针修改值

通过指针不仅可以取值,也可以修改值

形参和实参,类似c语言

package mainimport "fmt"func main() {// 利用指针修改值var num = 10modifyFromPoint(num)fmt.Println("未使用指针,方法外", num) //10000var num2 = 22newModifyFromPoint(&num2)     // 传入指针fmt.Println("使用指针 方法外", num2) //1000
}func modifyFromPoint(num int) {// 未使用指针num = 10000fmt.Println("未使用指针,方法内:", num) //10
}func newModifyFromPoint(ptr *int) {// 使用指针*ptr = 1000                    // 修改指针地址指向的值fmt.Println("使用指针,方法内:", *ptr) //1000
}

3、创建指针的另一种方法

Go语言还提供了另外一种方法来创建指针变量,格式:new(类型)

	var str0 *string = new(string)str := new(string)*str0 = "不是哥们,还能这样new指针啊"*str = "不是哥们,还能这样new指针啊"fmt.Println(*str0)fmt.Println(*str)

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

4、指针显转

// 示例1: 基本类型指针转换
num := 100
pInt := &num               // *int 类型
pFloat := (*float32)(unsafe.Pointer(pInt)) // 转为 *float32

unsafe.Pointer是 unsafe包中最重要的类型,它是一个可以指向任意类型的指针

特点:

  • 可以转换为任何类型的指针
  • 不能进行算术运算(不同于 C 的 void*
  • 不能直接解引用(必须先转换为具体类型指针

定义:

type Pointer *ArbitraryType  // ArbitraryType 只是一个占位符,没有实际意义

5、指针小案例

获取命令行的输入信息

Go语言内置的 flag 包实现了对命令行参数的解析,flag 包使得开发命令行工具更为简单。

package mainimport ("flag""fmt"
)
// 定义命令行参数
var mode = flag.String("mode", "", "fast模式能让程序运行的更快")
​
func main() {// 解析命令行参数flag.Parse()fmt.Println(*mode)
}
进入cmd输出:
go run demo1.go --mode=fast

三、变量的生命周期

变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。

变量的生命周期与变量的作用域有不可分割的联系:

  1. 全局变量:它的生命周期和整个程序的运行周期是一致的;
  2. 局部变量:它的生命周期则是动态的,从创建这个变量的声明语句开始,这个变量不再被引用为止;
  3. 形式参数和函数返回值:它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁

go的内存中应用了两种数据结构用于存放变量:

  1. 堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态加入到堆上(堆被扩张)。当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);
  2. 栈(stack):栈又称堆栈, 用来存放程序暂时创建的局部变量,也就是我们函数的大括号{ }中定义的局部变量。

栈是先进后出,往栈中放元素的过程,称为入栈,取元素的过程称为出栈。

栈可用于内存分配,栈的分配和回收速度非常快

在程序的编译阶段,编译器会根据实际情况自动选择或者上分配局部变量的存储空间,不论使用 var 还是 new 关键字声明变量都不会影响编译器的选择。

var global *int 
func f() {var x intx = 1global = &x //全局变量引用局部变量
}
func g() {y := new(int)*y = 1
}

上述代码中,函数 f 里的变量 x 必须在堆上分配,因为它在函数退出后依然可以通过包一级的 global 变量找到,虽然它是在函数内部定义的。

用Go语言的术语说,这个局部变量 x 从函数 f 中逃逸了

相反,当函数 g 返回时,变量 y 不再被使用,也就是说可以马上被回收的。因此,y 并没有从函数 g 中逃逸,编译器可以选择在栈上分配 *y 的存储空间,也可以选择在堆上分配,然后由Go语言的 GC(垃圾回收机制)回收这个变量的内存空间。

四、型别名

 类型别名是 Go 1.9 版本添加的新功能,主要用于解决代码升级、迁移中存在的类型兼容性问题。

格式:
TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类型

就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。
类型别名:type TypeAlias = Type

输出 int 类型

还有一种是类型定义:

定义Name为Type类型 ,定义之后 Name为一种新的类型
类型定义:type Name Type

输出 Name 类型

类型别名与类型定义表面上看只有一个等号的差异,那么它们之间实际的区别有哪些呢?

package main
import ("fmt"
)
// 将NewInt定义为int类型
type NewInt int// 将int取一个别名叫IntAlias
type IntAlias = intfunc main() {// 将a声明为NewInt类型var a NewInt// 查看a的类型名 main.NewIntfmt.Printf("a type: %T\n", a)// 将a2声明为IntAlias类型var a2 IntAlias// 查看a2的类型名 int //IntAlias 类型只会在代码中存在,编译完成时,不会有 IntAlias 类型。fmt.Printf("a2 type: %T\n", a2)
}

五、注释

和其他编程语言一样的

在 package 语句之前的注释内容将被默认认为是这个包的文档说明。

一个包可以分散在多个文件中,但是只需要对其中一个进行注释说明即可

六、关键字和标识符

1、关键字

关键字即是被Go语言赋予了特殊含义的单词,也可以称为保留字。

Go语言中的关键字一共有 25 个:

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

之所以刻意地将Go语言中的关键字保持的这么少,是为了简化在编译过程中的代码解析。

和其它语言一样,关键字不能够作标识符使用。

2、标识符

标识符是指Go语言对各种变量、方法、函数等命名时使用的字符序列,标识符由若干个字母、下划线_、和数字组成,且第一个字符必须是字母。

下划线_是一个特殊的标识符,称为空白标识符

标识符的命名需要遵守以下规则:

  • 由 26 个英文字母、0~9、_组成;
  • 不能以数字开头,例如 var 1num int 是错误的;
  • Go语言中严格区分大小写;
  • 标识符不能包含空格;
  • 不能以系统保留关键字作为标识符,比如 break,if 等等。

命名标识符时还需要注意以下几点:

  • 标识符的命名要尽量采取简短且有意义;
  • 不能和标准库中的包名重复;
  • 为变量、函数、常量命名时采用驼峰命名法,例如 stuName、getVal;

3、特殊的标识符:预定义标识符

如下表所示:

appendboolbytecapclosecomplexcomplex64complex128uint16
copyfalsefloat32float64imagintint8int16uint32
int32int64iotalenmakenewnilpanicuint64
printprintlnrealrecoverstringtrueuintuint8uintptr

预定义标识符一共有 36 个,主要包含Go语言中的基础数据类型和内置函数,这些预定义标识符也不可以当做标识符来使用。

七、运算符优先级

所谓优先级,就是当多个运算符出现在同一个表达式中时,先执行哪个运算符。

优先级分类运算符结合性
1逗号运算符,从左到右
2赋值运算符=、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|=从右到左
3逻辑或||从左到右
4逻辑与&&从左到右
5按位或|从左到右
6按位异或^从左到右
7按位与&从左到右
8相等/不等==、!=从左到右
9关系运算符<、<=、>、>=从左到右
10位移运算符<<、>>从左到右
11加法/减法+、-从左到右
12乘法/除法/取余*(乘号)、/、%从左到右
13单目运算符!、*(指针)、& 、++、--、+(正号)、-(负号)从右到左
14后缀运算符( )、[ ]、->从左到右

注意:优先级值越大,表示优先级越高。

八、字符串与其他数据类型的转换

1、整数与字符串

str转int:strconv.Atoi(字符串)

newStr1 := "1"
intValue, _ := strconv.Atoi(newStr1)
fmt.Printf("%T,%d\n", intValue, intValue)  // int,1
​

int转str:strconv.Itoa(整形)

intValue2 := 1
strValue := strconv.Itoa(intValue2)
fmt.Printf("%T, %s\n", strValue, strValue)

2、浮点数与字符串

str转float:strconv.ParseFloat(字符串,64位 )

string3 := "3.141592"
f,_ := strconv.ParseFloat(string3, 64)
fmt.Printf("%T, %f\n", f, f)  // float64, 3.141592

float转string:strconv.FormatFloat(小数, 格式化类型, 保留小数点后几位, 64位)

floatValue := 3.1415926
formatFloat := strconv.FormatFloat(floatValue, 'f', 2, 64)
fmt.Printf("%T,%s",formatFloat,formatFloat)


4个参数:

  • 要转换的浮点数
  • 格式标记(b、e、E、f、g、G)
  • 精度
  • 指定浮点类型(32:float32、64:float64)

格式标记:
‘b’ (-ddddp±ddd,二进制指数)
‘e’ (-d.dddde±dd,十进制指数)
‘E’ (-d.ddddE±dd,十进制指数)
‘f’ (-ddd.dddd,没有指数)
‘g’ (‘e’:大指数,‘f’:其它情况)
‘G’ (‘E’:大指数,‘f’:其它情况)
如果格式标记为 ‘e’,‘E’和’f’,则 prec 表示小数点后的数字位数
如果格式标记为 ‘g’,‘G’,则 prec 表示总的数字位数(整数部分+小数部分)

九、小练习

package mainimport ("bufio""fmt""os"
)var level = 1
var ex = 0
func main()  {fmt.Println("请输入你的角色名字")//捕获标准输入,并转换为字符串reader := bufio.NewReader(os.Stdin)input, err := reader.ReadString('\n')if err != nil {panic(err)}//删除最后的\nname := input[:len(input)-1]fmt.Printf("角色创建成功,%s,欢迎你来到张三游戏,目前角色等级%d \n",name,level)s := `你遇到了一个怪物,请选择是战斗还是逃跑?1.战斗2.逃跑`fmt.Printf("%s \n",s)for {input, err := reader.ReadString('\n')if err != nil {panic(err)}selector := input[:len(input)-1]switch selector {case "1":ex += 10fmt.Printf("杀死了怪物,获得了%d经验 \n",ex)computeLevel()fmt.Printf("您现在的等级为%d \n",level)case "2":fmt.Printf("你选择了逃跑\n")fmt.Printf("%s \n",s)case "exit":fmt.Println("你退出了游戏")//退出os.Exit(1)default:fmt.Println("你的输入我不认识,请重新输入")}}
}func computeLevel() {if ex < 20 {level = 1}else if ex < 40{level = 2}else if ex < 200{level = 3}else {level = 4}
}

http://www.dtcms.com/a/399885.html

相关文章:

  • 普定县建设局网站河北seo平台
  • dify-随笔
  • 免费开店的平台有哪些标题优化方法
  • seo顾问服务公司站长怎么做软文网站
  • 【JNA】JAVA使用JNA调用C++ dll文件(3)编译Linux版本.so文件
  • MyBatis 操作数据库(⼊⻔)
  • [baka编程]初入C++,如何理解新概念——类和对象
  • 竞价网站做推广一款app是如何制作出来的
  • 北京建设银行网站广东官网网站建设平台
  • 淘宝客怎么在网站做推广网站每年续费费用
  • Pyside6 + QML - 多线程01 - QThread 基础(子线程执行耗时任务)
  • 农产品应该建设哪个网站屏幕分辨率 网站开发
  • 摄影作品展示网站flash全站源码山东住建部和城乡建设官网
  • 购物网站功能模块图使用div建设的网站
  • Python爬虫实战:获取上海石油天然气交易中心2025年液化天然气交易数据并做分析
  • springboot项目添加请求url及请求入参日志
  • Spring XML 配置简介
  • 阿里云虚拟主机如何上传网站国外flash网站
  • NXP - 安装后的MCUXpresso IDE里面有所有的支持包,不用另外去下载
  • 公司网站域名更改怎么做做美团旅游网站多少钱
  • 网站建设初稿wordpress 教學
  • 【nvm for windows安装问题】手动安装方案
  • 网站定制开发怎么写网站后台管理系统图片
  • 如何本地搭建网站贵阳市门户网站
  • 网站设计理念怎么写wordpress插件清理
  • 语义精炼技巧生成对抗网络(3)基于Wasserstein GAN 的特征生成
  • 3.算法——遗传算法
  • html怎么做网站版块网站空间购买官方
  • 成都建站网站软件开发涵盖网站开发吗
  • DDOS高防的优点是什么