Go语言学习笔记06--结构体struct与指针pointer

1.计算字符串中每个出现字符的次数案例
    1)map改写
        func main()  {
            inputCharArr := [20]byte{};
            checkNumDic := map[byte]int{};
            for i:=0; i<20; i++{
                fmt.Scanf("%c",&inputCharArr[i]);
            }
            for j:=0; j<20; j++{
                existFlag := false;
                for k,_ := range checkNumDic{
                    if k == inputCharArr[j]{
                        checkNumDic[k]++;
                        existFlag = true;
                    }
                }
                if(!existFlag){
                    checkNumDic[inputCharArr[j]] = 1;
                }
            }
            for k,v := range checkNumDic{
                fmt.Printf("字母%c,出现的次数是%d次\n",k,v);
            }
        }
    2)随意长度输入,判别输入出现字符个数
        func main()  {
            inputCharArr := []byte{};
            checkNumDic := map[byte]int{};
            for{
                var temp byte;
                fmt.Scanf("%c",&temp);
                inputCharArr = append(inputCharArr, temp);
                if temp == '\n'{
                    break;
                }
            }
            for j:=0; j                 if(inputCharArr[j] == '\n'){
                    continue;
                }
                existFlag := true;
                for k,_ := range checkNumDic{
                    if k == inputCharArr[j]{
                        checkNumDic[k]++;
                        existFlag = false;
                    }
                }
                if(existFlag){
                    checkNumDic[inputCharArr[j]] = 1;
                }
            }
            for k,v := range checkNumDic{
                fmt.Printf("字母%c,出现的次数是%d次\n",k,v);
            }

        }
    3)随意长度输入,判别输入出现字符个数(简化版本)
        func main()  {
            inputCharArr := []byte{};
            checkNumDic := map[byte]int{};
            for{
                var temp byte;
                fmt.Scanf("%c",&temp);
                inputCharArr = append(inputCharArr, temp);
                if temp == '\n'{
                    break;
                }
            }
            for j:=0; j                 if(inputCharArr[j] == '\n'){
                    continue;
                }
                checkNumDic[inputCharArr[j]]++;
            }
            for k,v := range checkNumDic{
                fmt.Printf("字母%c,出现的次数是%d次\n",k,v);
            }

        }
2.struct结构体
    众所周知go语言是从c语言衍生而来的第五类计算机编程语言,所以很多情况下go语言都带有着浓浓的c语言风格
    例如对于struct结构体的部分来说,go语言中的struct结构就基本类似于c语言中的情况。
    如果对其他编程语言有一定的了解,那么就会发现go语言中的struct结构和JS中的类与对象部分十分相似。
    在go语言中,结构体需要在main函数之外使用type关键字定义,并且通常首字母大写。
        eg:
            //结构体声明
            type 结构体名 struct{
                属性名1 属性值1类型
                属性名2 属性值2类型
                ...
            }
            //结构体使用
            func main(){
                var 实例名 结构体名
                实例名.属性名1 = 符合属性值1类型的数据 
                ...
            }        
        eg:
            //结构体声明
            type UserInfo struct{
                userName string
                userAge int
                userGender string
            }
            //结构体使用
            func main()  {
                var xiaomingInfo UserInfo;
                xiaomingInfo.userName = "小明";
                xiaomingInfo.userAge = 18;
                xiaomingInfo.userGender = "男";
                fmt.Println(xiaomingInfo);
            }
    (1)其他初始化方式
        1)var 实例名 结构体名 = 结构体名{属性名1:符合属性值1类型的额数据,...};
        eg:
            var xiaohongInfo UserInfo = UserInfo{userName:"小红",userAge:17,userGender:"女"};
            fmt.Println(xiaohongInfo);
        2)实例名 := 结构体名{属性名1:符合属性值1类型的额数据,...};
        eg:
            xiaolv := UserInfo{userName:"小绿",userAge:14,userGender:"女"};
            fmt.Println(xiaolv);
    (2)struct结构体的赋值与比较
        1)struct结构体的赋值
            在go语言中struct明显时一个复杂数据类型,但是结构体赋值却是“值传递”。
            其实结构体相互赋值的时候,是另开辟存储空间后值拷贝,所以一处修改另一处不会跟随变化。
            eg:
                stu1 := UserInfo{userName:"小绿",userAge:14,userGender:"女"};
                stu2 := stu1;
                stu2.userAge = 100;
                fmt.Println(stu1);//{小绿 14 女}
                fmt.Println(stu2);//{小绿 100 女}
        2)struct结构体的比较
            结构体比较可以直接用==双等号或!=不等于比较,但是不能使用大于或小于等符号判断。
            但是结构体中的信息是可以使用大于小于等于不等于等符号来比较的。
            eg:
                //合法操作
                if stu1 == stu2{...}
                if stu1.userAge > stu2.userAge{...}
                //违法操作
                if stu1 > stu2{...}
    (3)struct结构体数组和切片
        go语言中数组和切片的元素允许是任意的数据类型,那么struct作为数组和切片的元素也是允许的
        eg:
            var 数组名 [数组长度]结构体名
        eg:
            var userStructArr = [3]UserInfo;
        1)结构体数组排序
            eg:
                var userStructArr = [3]UserInfo{
                    UserInfo{"小明",17,"男"},
                    UserInfo{"小红",18,"男"},
                    UserInfo{"小绿",16,"女"}};

                for i:=0; i                     for j:=0; j                         if userStructArr[j].userAge > userStructArr[j+1].userAge{
                            userStructArr[j],userStructArr[j+1] = userStructArr[j+1],userStructArr[j];
                        }
                    }
                }
                fmt.Println(userStructArr);
        2)结构体切片
            切片的结构和数组也就相差一个长度,只要注意使用的时候声明结构体类型就可以。
            eg:
                var userStructSlice = []UserInfo{
                    UserInfo{"小明",17,"男"},
                    UserInfo{"小红",18,"男"},
                    UserInfo{"小绿",16,"女"}};

                userStructSlice = append(userStructSlice, UserInfo{"小白",12,"女"});
                fmt.Println(userStructSlice);
    (4)struct结构体作为map的value
        没什么特殊的,多注意类型的声明就行。
        eg:
            info := make(map[string][]UserInfo);
            info["一年1班"] = append(info["一年1班"], UserInfo{...},UserInfo{...},UserInfo{...});
            info["一年2班"] = append(info["一年2班"], UserInfo{...},UserInfo{...},UserInfo{...});
            info["一年3班"] = append(info["一年3班"], UserInfo{...},UserInfo{...},UserInfo{...});
            for key,val := range info{
                fmt.Println(key,"的学生信息有:");
                for index,data := range val{
                    fmt.Println(index,data);
                }
            }
    (5)struct结构体作为函数参数与返回值
        1)结构体作为函数参数
            结构体直接作为函数传参的时候采用值传递,所以作为函数参数传参的时候不会影响函数外面的部分
            返回值可以将作出的结构体修改带出函数之外
            eg:
                func paraStruct(tempStruct UserInfo) UserInfo {
                    tempStruct.userAge = 100;
                    return tempStruct;
                }
                xiaoming := UserInfo{"小明",18,"男"};
                result1 := paraStruct(xiaoming);
                fmt.Println(result1);        //{小明 100 男}
                fmt.Println(xiaoming);        //{小明 18 男}
        2)结构体数组作为函数参数
            如果是结构体数组进行传值,那么只关心数组的变量赋值就可以了。
            因为数组是值传递,那么内部的修改也不能对外部引发变化
            eg:
                func paraStructArr(tempStructArr [3]UserInfo) [3]UserInfo {
                    for index,_ := range tempStructArr{
                        tempStructArr[index].userAge += 100;
                    }
                    return tempStructArr;
                }
                stuArr := [3]UserInfo{
                    UserInfo{"小明",17,"男"},
                    UserInfo{"小红",18,"男"},
                    UserInfo{"小绿",16,"女"}};
                result2 := paraStructArr(stuArr);
                fmt.Println(result2);        //[{小明 117 男} {小红 118 男} {小绿 116 女}]
                fmt.Println(stuArr);        //[{小明 17 男} {小红 18 男} {小绿 16 女}]
        3)结构体切片作为函数参数
            如果是结构体切片进行传值,那么只关心切片的变量赋值就可以了
            切片本身是地址传递,因此内部的修改会造成外部的连锁变化。
            但是切片在追加数据的时候会造成地址变更问题,而追加的操作则不会引发外界的同步变更
            eg:
                func paraStructSlice(tempStructSlice []UserInfo) []UserInfo{
                    //修改
                    for index,_ := range tempStructSlice{
                        tempStructSlice[index].userAge += 100;
                    }
                    //追加
                    tempStructSlice = append(tempStructSlice, UserInfo{"小白",14,"女"});
                    return tempStructSlice;
                }
                stuSlice := []UserInfo{
                    UserInfo{"小明",17,"男"},
                    UserInfo{"小红",18,"男"},
                    UserInfo{"小绿",16,"女"}};
                result3 := paraStructSlice(stuSlice);
                fmt.Println(result3);        //[{小明 117 男} {小红 118 男} {小绿 116 女} {小白 14 女}]
                fmt.Println(stuSlice);        //[{小明 117 男} {小红 118 男} {小绿 116 女}]
        4)结构体字典作为函数参数
            如果是结构体map进行传值,那么就只要关心map的变量赋值就可以了
            map本身是地址传递,并且支持自动扩容(即内存地址不会发生变动)
            因此结构体map在进行传参的时候是地址传递,一处修改处处变化。
            eg:
                func paraStructMap(tempStructMap map[string][]UserInfo) map[string][]UserInfo {
                    tempStructMap["一年一班"][0].userAge = 100;
                    return tempStructMap;
                }
                stuMap := map[string][]UserInfo{
                    "一年一班":[]UserInfo{
                        UserInfo{"小明",17,"男"},
                        UserInfo{"小红",16,"女"}},
                    "一年二班":[]UserInfo{
                        UserInfo{"小绿",15,"男"},
                        UserInfo{"小白",14,"女"}}}
                result4 := paraStructMap(stuMap);
                fmt.Println(result4);
                    //map[一年二班:[{小绿 15 男} {小白 14 女}] 一年一班:[{小明 100 男} {小红 16 女}]]
                fmt.Println(stuMap);
                    //map[一年一班:[{小明 100 男} {小红 16 女}] 一年二班:[{小绿 15 男} {小白 14 女}]]
    (6)struct结构体练习案例--学生成绩
        已知:
            定义结构体存储5名学生
            每名学生三门成绩
        要求:
            1.计算每名学生的总成绩和平均成绩
            2.计算全体学生的总成绩平均成绩和每一门的平均成绩
        eg:
            //单一学生结构体
            type stuScoreInfoStruct struct {
                stuName string
                chineseScore int
                mathScore int
                englishScore int
            }
            //班级信息结构体
            type classRoomStruct struct {
                className string
                classStudents []stuScoreInfoStruct
            }
            //计算单一学生成绩信息函数
            func getOneStuScoreInfo(tempInfo stuScoreInfoStruct){
                allScore := tempInfo.chineseScore+tempInfo.mathScore+tempInfo.englishScore;
                averageScore := allScore/3;
                fmt.Printf("%s的总成绩是:%d,平均成绩是:%d\n",tempInfo.stuName,allScore,averageScore);
            }
            //计算所有学生成绩信息函数
            func getAllStuScoreInfo(classInfo classRoomStruct){
                chineseAllScore,mathAllScore,englishAllScore := 0,0,0;
                fmt.Println("--------------要求1----------------");
                for i:=0;i                     getOneStuScoreInfo(classInfo.classStudents[i]);//直接求出来每个人的信息
                    chineseAllScore += classInfo.classStudents[i].chineseScore;
                    mathAllScore += classInfo.classStudents[i].mathScore;
                    englishAllScore += classInfo.classStudents[i].englishScore;
                }
                fmt.Println("--------------要求2----------------");
                fmt.Printf("%s的成绩信息:\n",classInfo.className);
                fmt.Printf("总平均分是:%d\n",(chineseAllScore+mathAllScore+englishAllScore)/3);
                fmt.Printf("语文总平均分是:%d\n",chineseAllScore/3);
                fmt.Printf("数学总平均分是:%d\n",mathAllScore/3);
                fmt.Printf("英语总平均分是:%d\n",englishAllScore/3);
            }
            func main(){
                chineseScore,mathScore,englishScore,myCLassInfo := 0,0,0,classRoomStruct{};
                myCLassInfo.className = "三年二班";
                stuNameArr := []string{"小明","小红","小绿"};
                for i:=0; i<3; i++{
                    fmt.Printf("请输入%s第%d个学生%s的【语文】【数学】【英语】成绩\n",myCLassInfo.className,i+1,stuNameArr[i]);
                    fmt.Println("使用逗号间隔,回车表示结束:");
                    fmt.Scanf("%d,%d,%d",&chineseScore,&mathScore,&englishScore);
                    myCLassInfo.classStudents = append(myCLassInfo.classStudents, stuScoreInfoStruct{stuNameArr[i],chineseScore,mathScore,englishScore});
                }
                getAllStuScoreInfo(myCLassInfo);
            }
3.pointer指针
    在go语言中指针用来保存一些变量的内存地址,其性质与传统c/c++基本相同。
    例如,我们采用下面的语法格式来声明指针
        eg:
            var 指针 *数据类型
        eg:
            var p *int;
            var num int = 100;
            p = #//0xc000014038,这显然是一个内存地址信息
    (1)通过指针间接访问,修改变量的值
        同c语言一样,指针具有访问与修改其保存内存地址指向的变量的内容的功能
        通过*寻址运算符来操作与获取。
        eg:
            fmt.Println(*p);//100
            *p = 145;
            fmt.Println(*p);//145
    (2)指针、空指针与野指针
        在go语言中指针被声明后可能会有下列三种情况,这三种情况对应了指针的三种不同名称。
        1)空指针
            只声明指针变量,但是不对指针变量赋值,这样的指针就被称为空指针。
            空指针默认值为nil。
            eg:
                var p *int;
                fmt.Println(p);//
        2)指针
            指针变量被正确声明,且指向了一个合法可控的内存地址,
            这样的指针就是正确的指针。
            eg:
                var p *int;
                var num int = 100;
                p = #
                fmt.Println(p);//0xc000014385
        3)野指针
            指针变量被正确声明,但是且指向了一个未知的内存地址
            这样的指针就是野指针。
            eg:
                var p *int;
                p = 0xc000014385;
                *p = 100;//错误,内存未知
        在传统的c语言中野指针是不安全的指针访问,但并不会报错,依然能正常使用。
        但是在go语言中却并不允许任何不安全的方式访问。也就是说空指针和野指针两种方式都是不允许直接使用的。
    (3)指针空间与new关键词
        eg:
            var p *int;
            //*p = 100;指针只声明不赋值的情况下,是不能通过指针去进行操作的。
            //相当于c语言中的malloc
            p = new(int);
            //*p = 100;正确
        但是和c语言不同的是,new只需要分配,而不需要像传统c语言一样通过dealloc手动管理释放。
    (4)指针作为函数参数
        基本数据类型在进行传参的时候,由于值传递的原因,函数内无法影响到函数之外。
        eg:
            func swap(num1 int, num2 int){
                num1,num2 = num2,num1;
            }
            func main(){
                num1,num2 := 100,200;
                swap(num1, num2);
                fmt.Println(num1);//100
                fmt.Println(num2);//200
            }
        但如果通过指针,则可以完成这个需求。因为指针保存的就是内存地址,传递指针就相当于地址传递。
        eg:
            func swap(num1 *int,num2 *int){
                *num1,*num2 = *num2,*num1;
            }
            func main(){
                num1,num2 := 100,200;
                swap(&num1, &num2);
                fmt.Println(num1);//200
                fmt.Println(num2);//100
            }    

你可能感兴趣的:(go语言基础)