.

作者 : 万境绝尘

转载请注明出处http://www.hanshuliang.com/?post=30

.

结构体概述 : 结构体是 多个 变量的集合, 变量的类型可以不同;

-- 可进行的操作 : 结构体可以进行 拷贝 赋值操作, 可以作为 函数参数 和 函数返回值;

1. 结构体的基本使用

结构体声明 : struct 结构标记 {结构成员} 普通变量;

-- 结构体示例 :

	struct student
	{
		char *name;
		int age;
	};

-- 结构标记 : struct 后面的 student 是结构标记, 这个标记 可写 可不写, 其作用是 为结构命名, 结构标记可以代表 {} 中的声明的所有的成员变量;

-- 结构成员 : 在 {} 中定义的变量就是结构成员;

-- 普通变量 : 在声明结构体的时候后面可以加上若干普通变量, 相当于定义结构体变量;

结构体变量声明 : 可以在定义的时候声明变量, 也可以在定义完结构体使用 结构标记 声明变量;

-- 定义结构体时声明变量 : 这种声明变量的方式可以不用 结构标记, 变量名写在 花括号 后面, 用头号隔开;

	struct student
	{
		char *name;
		int age;
	} s1, s2, s3;

-- 使用结构标记声明 : 结构标记 student 代表了花括号的声明, 是 结构体的简写, 可以使用结构标记代替花括号中的内容;

struct student s4, s5, s6;

结构体内存分配 : 结构体内存是在声明变量的时候分配的, 如果只声明了结构体, 没有声明对应变量, 那么不会分配内存;

结构体变量初始化 :

-- 声明结构体的时候初始化 : struct student s1 = {"Tom", 12} ; 注意 初值表中必须时结构体对应类型的常量表达式;

-- 声明之后初始化 : 结构体变量名.成员名 可以访问结构体中的成员变量, s1.name = "Tom"; s2.age = 12;

结构体嵌套 : 结构体中的成员变量可以是 结构体变量;

	struct student
	{
		char *name;
		int age;
	} s1;

	struct class
	{
		struct student s1;
		struct student s2;
	} c1;

结构体代码示例 :

/*************************************************************************
    > File Name: base_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: 2014年03月24日 星期一 10时49分46秒
 ************************************************************************/

#include<stdio.h>

int main(int argc, char **argv)
{
	/*
	 * 声明结构体 同时声明变量s1
	 */
	struct student
	{
		char *name;
		int age;
	} s1;

	/*
	 * 结构体嵌套
	 */
	struct class
	{
		struct student s1;
		struct student s2;
	} c1;

	struct student s2 = {"Tom", 12};/*只有声明的时候才能对结构体初始化才能使用花括号赋值*/
	struct class c2 = {{"Jack", 13}, {"Pig", 15}};
	s1.name = "Hack";		/*变量声明后对结构体赋值只能一个一个赋值*/
	s1.age = 14;
	//s1 = {"fuck", 1};		/*只有在初始化的时候才能使用 花括号初始化结构体变量*/
	c1.s1.name = "CJ";
	c1.s1.age = 21;
	c1.s2.name = "KW";
	c1.s2.age = 22;

	/*访问结构体中的变量, 使用 . 进行访问*/

	printf("s1 : name = %s, age = %d \n", s1.name, s1.age);
	printf("s2 : name = %s, age = %d \n", s2.name, s2.age);
	printf("c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c1.s1.name, c1.s1.age, c1.s1.name, c1.s2.age);
	printf("c2 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c2.s1.name, c2.s1.age, c2.s1.name, c2.s2.age);

	return 0;
}

执行结果

octopus@octopus-Vostro-270s:~/code/c/struct$ gcc base_struct.c
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out
s1 : name = Hack, age = 14
s2 : name = Tom, age = 12
c1 : s1 : name = CJ, age = 21 ; s2 : name = CJ, age = 22
c2 : s1 : name = Jack, age = 13 ; s2 : name = Jack, age = 15

2. 结构体与函数

结构体的合法操作 :

-- 整体复制 : 结构体可以复制;

-- 整体赋值 : 声明结构体的时候可以整体赋值, 在其它情况下不可以;

-- & 取地址 : 使用 & 运算符获取 结构体地址;

-- 访问成员 : 使用 结构体变量名.成员变量名 可以访问成员变量;

函数传递结构体方法 :

-- 传递结构体成员 : 将结构体拆开, 将其中的成员变量分别传入;

struct class create_class(struct student s2, struct student s3)
{
	struct class c1 = {s2, s3};
	return c1;
}

-- 传递结构体 :  将整个结构体当做参数传入, 这种情况和传递其它类型参数一样, 都是通过值传递的;

struct class create_class(struct student s2, struct student s3)
{
	struct class c1 = {s2, s3};
	return c1;
}

-- 传递结构体指针 : 传递结构体的指针, 访问形式如下;

/*
 * 传入一个结构体指针
 * 通过指针访问结构体的方法 :
 *   (*结构体指针变量).成员变量 访问;
 *   结构体指针变量 -> 成员变量 访问;
 */
void printf_struct_pointer(struct student *s)
{
	printf("student : (*s).name = %s, (*s).age = %d \n", (*s).name, (*s).age);
	printf("student : s->name = %s, s->age = %d \n", s->name, s->age);
}

结构体指针访问 :

-- 示例 : 定义一个结构体指针;

	struct student
	{
		char *name;
		int age;
	}*p;

-- "." 和 "->"优先级 : 这两个运算符都是从左到右运算, 都是右结合; "." 和 "->" 优先级比 "*" , "++" 优先级要高; 这两个运算符 与 () [] 是四个优先级最高的运算符;

-- ++p->age 分析 : 是对结构体中的 age 进行自增操作;

-- *p->name 分析 : 获取 结构体中的 name 字符串的值(注意不是指针|地址);

-- *p++->name 分析 : 先获取 name 字符串的值, 再将p自增;

结构体函数示例 :

/*************************************************************************
    > File Name: method_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: 2014年03月24日 星期一 14时46分16秒
 ************************************************************************/

#include<stdio.h>

/*声明一个结构体类型, 其成员变量是普通变量*/
struct student
{
	char *name;
	int age;
};

/*声明一个结构体类型, 其成员变量是结构体*/
struct class
{
	struct student s1;
	struct student s2;
};

/*
 * 传递 2 个结构体的成员变量
 * 在函数中创建结构体并返回
 */
struct student create_student(char *name, int age)
{
	struct student s1 = {name, age};
	return s1;
}

/*
 * 传递 2 个结构体变量
 */
struct class create_class(struct student s2, struct student s3)
{
	struct class c1 = {s2, s3};
	return c1;
}

/*
 * 传入一个结构体指针
 * 通过指针访问结构体的方法 :
 *   (*结构体指针变量).成员变量 访问;
 *   结构体指针变量 -> 成员变量 访问;
 */
void printf_struct_pointer(struct student *s)
{
	printf("student : (*s).name = %s, (*s).age = %d \n", (*s).name, (*s).age);
	printf("student : s->name = %s, s->age = %d \n", s->name, s->age);
}

int main(int argc, char **argv)
{
	/*使用函数获取一个结构体, 传入结构体的值*/
	struct student s1 = create_student("Tom", 11);
	printf("student : name = %s, age = %d \n", s1.name, s1.age);

	/*创建一个成员变量是结构体的结构体, 并打印结构体数据*/
	struct class c1 = create_class(create_student("Jack", 12), create_student("CJ", 13));
	printf("c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c1.s1.name, c1.s1.age, c1.s2.name, c1.s2.age);

	/*声明结构体指针*/
	struct student *p = &s1;
	printf_struct_pointer(p);
	return 0;
}

执行结果

octopus@octopus-Vostro-270s:~/code/c/struct$ gcc method_struct.c
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out
student : name = Tom, age = 11
c1 : s1 : name = Jack, age = 12 ; s2 : name = CJ, age = 13
student : (*s).name = Tom, (*s).age = 11
student : s->name = Tom, s->age = 11 

3. 结构体数组

(1) 结构体数组声明初始化

声明结构体数组 :

-- 声明结构体的时候声明结构体数组 : 格式为 : struct 结构标记 {} 数组名[];

-- 使用结构标记声明结构体数组 : 格式为 : struct 结构标记 数组名[];

结构体数组声明初始化 :

-- 逐个元素初始化 : 数组名[] = {{结构体1}, {结构体2}};

-- 总体初始化 : 数组名[] = {常量1, 常量2 ...};

结构体初始化 :

/*************************************************************************
    > File Name: array_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: 2014年03月24日 星期一 16时40分15秒
 ************************************************************************/

#include<stdio.h>

/*
 * 声明结构体
 * 同时也声明结构体类型数组
 * 为数组初始化
 * 直接将每个结构体成员的值依次列出即可
 */
struct student
{
	char *name;
	int age;
} team1[] =
{
	"Tom", 12,
	"Jack", 13
};

int main(int argc, char **argv)
{
	int i;
	/*将每个结构体初始化, 赋值, 每个结构体初始化内容使用 花括号括起来*/
	struct student team2[] = {{"CJ", 34}, {"KI", 32}};
	for(i = 0; i < 2; i++)
	{
		printf("team1 : team1[i].name = %s, team1[i].age = %d \n", team1[i].name, team1[i].age);
	}

	for(i = 0; i < 2; i++)
	{
		printf("team2 : team2[i].name = %s, team2[i].age = %d \n", team2[i].name, team2[i].age);
	}

	return 0;
}

执行结果

octopus@octopus-Vostro-270s:~/code/c/struct$ gcc array_struct.c
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out
team1 : team1[i].name = Tom, team1[i].age = 12
team1 : team1[i].name = Jack, team1[i].age = 13
team2 : team2[i].name = CJ, team2[i].age = 34
team2 : team2[i].name = KI, team2[i].age = 32 

(2) 结构体数组示例程序

需求 : 实现一个统计 C 语言关键字出现次数;

代码 :

/*************************************************************************
    > File Name: word_count.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: 2014年03月24日 星期一 17时12分32秒
 ************************************************************************/

#include<stdio.h>
#include<ctype.h>
#include<string.h>

#define MAXWORD 10

/*
 * 定义结构体类型 key
 * 该类型结构体中存储一个 字符串(关键字) 和 int 数据(关键字出现次数)
 * 同时声明一个结构体数组
 * 对结构体数组进行初始化
 *
 *
 */
struct key
{
        char *word;
        int count;
}key_count[] =
{
        "auto", 0,
        "break", 0,
        "case", 0,
        "char", 0,
        "const", 0,
        "continue", 0,
        "default", 0,
        "void", 0,
        "volatitle", 0,
        "while", 0
};

int main(int argc, char **argv)
{
        int n;
        char word[MAXWORD];

        /*循环接收字符串, 如果字符串与结构体数组中匹配, 结构体的count ++*/
        while(getword(word, MAXWORD) != EOF)
                if(isalpha(word[0]))
                        if((n = binsearch(word, key_count, 10)) >= 0)
                                key_count[n].count++;

        /*打印大于0的关键字 及 个数*/
        for (n = 0; n < 10; n ++)
                if(key_count[n].count > 0)
                        printf("%2d %s\n", key_count[n].count, key_count[n].word);

        return 0;
}

/*
 * 重要api解析 :
 *  int getc(FILE *stream) 从标准输入流中读取字符
 *  int ungetc(int c, FILE *stream) 将字符退回到标准输入流中
 *  int isspace(int c) 判断字符是否是 空格 '\f' '\r' '\n' '\t' '\v'
 *  int isalpha(int c) 判断是否是字母
 */
int getword(char *word, int lim)
{
        int c, getc(FILE*), ungetc(int, FILE*);
        char *wp = word;

        /*过滤空格, 如果输入的不是 空, 就继续向下执行*/
        while(isspace(c = getc(stdin)));

        /*如果输入的不是结束符, 那么 wp指针, 先取值, 之后地址自增*/
        if(c != EOF)
                *wp++ = c;

        /*如果输入的不是字母, 直接返回, 关键字里面没有数字开头的*/
        if(!isalpha(c))
        {
                *wp = '\0';
                return c;
        }

        /*
         * 循环条件 a. 接收的最大字符个数 lim, 每读取一个字符, 该变量自减
         * 当该变量自减为0时停止循环接收字符串
         * 循环条件 b. 当读取到的字符 不是 字母 或者数字的时候, 停止循环
         */
        for(; --lim > 0; wp++)
        {
                if(!isalnum(*wp = getc(stdin)))
                {
                        ungetc(*wp, stdin);
                        break;
                }
        }

        *wp = '\0';
        return word[0];
}

/*
 * 参数解析 : word 是要查找的字符串 tab 字符串数组 n 字符串大小
 */
int binsearch(char *word, struct key tab[], int n)
{
        /*
         * cond 判断 查找的字符串 是在mid 坐标之前 (<0) 之后(>0) 或者就是mid坐标
         *
         * 如果查找的不是正好中间的变量符合, 就返回 -1 失败
         */
        int cond, low, high, mid;

        low = 0;
        high = n - 1;
        /*
         * 要找的值的下标在low 和 high之间
         * mid 是 low 和 high 的中值
         * 如果 word 小于中值下标 将比较范围 缩小
         */
        while(low <= high)
        {
                mid = (low + high) / 2;
                if((cond = strcmp(word, tab[mid].word)) < 0)
                        high = mid - 1;
                else if(cond > 0)
                        low = mid + 1;
                else
                        return mid;
        }
        return -1;
}

执行结果 :

[root@ip28 struct]# gcc word_count.c
[root@ip28 struct]# ./a.out
auto
break
break
char
 1 auto
 2 break
 1 char

宏定义方法 : 获取结构体数组大小;

-- sizeof 方法 : sizeof (对象) | sizeof (类型名称) 可以获取对象 或者 类型占用的存储空间, 其返回值是 size_t 类型的, 定义在stddef.h 头文件中;

-- 使用类型测量 :

#define KEYS (sizeof(key_count) / sizeof(struct key))

-- 使用对象测量

#define KEYS (sizeof(key_count) / sizeof(struct key_count[0])

4. 指向结构体指针

(1) 使用指针方式实现上面的关键字统计程序

使用指针进行二分查找 :

-- 使用下标找中值 : 在之前找中值时通过 mid = (low + high)方法, 这样做可行是因为 low 从0开始的;

-- 如果是指针情况 : mid low high 都是指针, 那么就不能使用上面的那种方法了, 使用 mid = low + (high - low) / 2;.

-- 指针操作情况的 high 和 low : 其中 low 是首元素的 首地址, high 是 尾元素的尾地址, 只有这样 它们的差的 0.5 倍才是准确的中值的首地址;

指针指向数组注意点 : 不要生成非法的指针, 指针不能指向数组之外的元素;

-- &key_count[-1] : 这个指针时非法的;

-- &key_count[n] : 对数组的最后一个元素后面的第一个元素进行 & 运算时合法的, 其它操作都是非法的;

示例程序 :

/*************************************************************************
    > File Name: pointer_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: Tue 25 Mar 2014 12:31:08 AM CST
 ************************************************************************/

#include<stdio.h>
#include<ctype.h>
#include<string.h>

#define MAXWORD 20
/*计算结构体数组的大小*/
#define KEYS (int)(sizeof(key_count) / sizeof(struct key))

struct key
{
	char *word;
	int count;
} key_count[] =
{
	"auto", 0,
	"break", 0,
	"case", 0,
	"char", 0,
	"const", 0
};

int getword(char *, int);
struct key *binsearch(char*, struct key*, int);

int main(int argc, char **argv)
{
	char word[MAXWORD];
	struct key *p; /*存放查找方法返回的结构体指针, 该指针指向数组中查找到元素的下标*/

	while(getword(word, MAXWORD) != EOF)
		if(isalpha(word[0]))
			if((p = binsearch(word, key_count, KEYS)) != NULL)
				p->count++;

	for(p = key_count; p < key_count + KEYS; p++)
		if(p->count > 0)
			printf("%2d %s \n", p->count, p->word);

	return 0;
}

/*
 * 没有循环控制变量的 for 循环, 在内部通过条件 break
 */
int getword(char *word, int max)
{
	int c, getc(FILE*), ungetc(int, FILE*);
	char *wp = word;

	/*处理第一个字符, 第一个字符不是 空 不是 EOF 再进行下面的操作*/
	while(isspace(c = getc(stdin)));
	if(c != EOF)
		*wp++ = c;
	if(!isalpha(c))
	{
		*wp = '\0';
		return c;
	}

	/*循环接收字符串, 字符串接收到非标识符 或者 到达个数上限停止循环*/
	for(; --max > 0; wp++)
		if(!isalnum(*wp = getc(stdin)))
		{
			ungetc(*wp, stdin);
			break;
		}
	*wp = '\0';
	return word[0];
}

/*
 * 注意点 :
 *   取两个地址的中值 : 一个数组n个元素, 其中值计算 是由 首元素的首地址 和 尾元素的尾地址计算的
 *   二分查找 :
 *     如果要把区间前移, 那么就需要将尾地址设置为 中间元素前一个元素的尾地址, 即中间元素的首地址
 *     如果要把区间后移, 那么就需要将首地址设置为 中间元素后一个元素的首地址, 即中间元素 + 1 的地址
 *
 * 指向结构体数组的指针 :
 *   struct key tab * 是指向结构体数组指针, 该指针可以操作结构体数组
 */
struct key *binsearch(char *word, struct key *tab, int n)
{
	int cond;
	struct key *low = &tab[0];	/*0元素的首地址*/
	struct key *high = &tab[n];	/*尾元素的尾地址*/
	struct key *mid;

	while(low < high)
	{
		/*计算中间值的地址*/
		mid = low + (high - low) / 2;
		if((cond = strcmp(word, mid->word)) < 0)
			high = mid;	/*mid 是 中间元素前一个元素的尾地址*/
		else if(cond > 0)
			low = mid + 1; /*这里low要成为mid后一个元素的首地址*/
		else
			return mid;
	}
	return NULL;
}

执行结果

octopus@octopus-Vostro-270s:~/code/c/struct$ gcc pointer_struct.c
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out
auto
case
auto
break
 2 auto
 1 break
 1 case 

(2) 结构体大小讨论

结构体数组指针算术运算 : struct key *p = word_count; 指针 p 指向 结构体数组, 如果 p + 1 , 结果是 p 地址 加上 结构体所占内存大小;

结构体大小 : 结构体的大小不是完全等于各个成员的长度之和, 对象之间有对齐要求;

-- 空穴 : 对象间对齐, 会产生空穴, 占有空间, 但是不存储数据;

示例 : 结构体中由一个 char 和 int , 占用的空间却是 8个字节, 它们的和是 5个字节;

/*************************************************************************
    > File Name: memory_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com
    > Created Time: 2014年03月25日 星期二 12时55分45秒
 ************************************************************************/

#include<stdio.h>

struct word
{
	char c;
	int i;
};

int main(int argc, char **argv)
{
	printf("sizeof(struct word) = %d \n", sizeof(struct word));
	return 0;
}

执行结果

octopus@octopus-Vostro-270s:~/code/c/struct$ gcc memory_struct.c
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out
sizeof(word) = 8

.

作者 : 万境绝尘

转载请注明出处 : http://www.hanshuliang.com/?post=30

.

【嵌入式开发】C语言 结构体相关 的 函数 指针 数组的更多相关文章

  1. C语言结构体中的函数指针

      这篇文章简单的叙述一下函数指针在结构体中的应用,为后面的一系列文章打下基础 本文地址:http://www.cnblogs.com/archimedes/p/function-pointer-in ...

  2. 对嵌入式开发C语言结构体的一点总结

    今天冬至居然不上班,公司的良心啊!这回有心情写博客和日志了,好了,废话不多说.直接看下文: 鉴于嵌入式开发过程中,C语言结构体的使用当然是必不可少.话说,基础什么的比你会更牛逼的算法更重要,基础不牢, ...

  3. C语言 结构体相关 函数 指针 数组

    . 作者 : 万境绝尘 转载请注明出处 : http://www.hanshuliang.com/?post=30 . 结构体概述 : 结构体是 多个 变量的集合, 变量的类型可以不同; -- 可进行 ...

  4. 复习C语言系列二:动态调用函数指针数组

    a:用call_fun_ptr调用afun(),b:用call_fun_ptr调用bfun() a 请输入给您指定的函数输调用的参数 afun_par ------------------------ ...

  5. 嵌入式开发—C语言面试题

    嵌入式开发—C语言面试题 源地址:http://blog.csdn.net/xdx2ct1314/article/details/7358929   1. 用预处理指令#define 声明一个常数,用 ...

  6. C#委托与C语言函数指针及函数指针数组

    C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用 ...

  7. c语言.函数指针数组

    函数指针: 一个指向函数的指针.一般用函数名表示. 函数指针数组:元素为函数指针的数组.转移表.c语言中函数不可以定义为数组,只能通过定义函数指针来操作. #include<stdio.h> ...

  8. C语言结构体及typedef关键字定义结构体别名和函数指针的应用

    结构体(struct)的初始化 struct autonlist { char *symbol; struct nlist nl[2]; struct autonlist *left, *right; ...

  9. C语言 结构体中的零长度数组

    /* C语言零长度数组大小和取值问题 */ #include <stdio.h> #include <stdlib.h> #include <string.h> s ...

随机推荐

  1. 实体类双向映射进行Json序列化时出现无限循环的解决问题

    1.@JsonIgnoreProperties 指定的字段不会被序列化,如下则ExamPaper的directory字段不会被序列化 @OneToMany(mappedBy = "direc ...

  2. 解决使用BottomSheetDialog时状态栏变黑的问题

    问题描述 当使用support里的design 库里的BottomSheetDialog时,在6.0的机器上当对话框弹出时系统状态栏会变黑,如下图所示: 一开始以为是我用的姿势不对,试过对style配 ...

  3. 聚沙成塔-linux 常用命令

    批量更改文件后缀名 find . -depth -name "*.scss" -exec sh -c 'mv "$1" "${1%.scss}.les ...

  4. CentOS7.2安装Weblogic12c出现的问题

    Weblogic12c安装到步骤:Prerequisite  Checks 时,会进行操作系统版本的校验,即checking  operating  system  certification. 此处 ...

  5. Python实现数据库一键导出为Excel表格

    依赖 Python2711 xlwt MySQLdb 数据库相关 连接 获取字段信息 获取数据 Excel基础 workbook sheet 案例 封装 封装之后 测试结果 总结 数据库数据导出为ex ...

  6. CRM 2013 Script Loading Deep Dive

    关于CRM中脚本的加载次序梳理的很不错,可以看看 https://community.dynamics.com/crm/b/develop1/archive/2013/11/02/crm-2013-s ...

  7. windows系统下安装和使用ROS的解决方案 (1 win_ros 2 rosserial_windows)

    具体请参考官网: 1  http://wiki.ros.org/win_ros 2  https://github.com/ros-windows/win_ros 3  http://wiki.ros ...

  8. Swift函数柯里化(Currying)简谈

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 下面简单说说Swift语言中的函数柯里化.简单的说就是把接收多 ...

  9. [ExtJS5学习笔记]第五节 使用fontawesome给你的extjs5应用增加字体图标

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/38458411本文作者:sushengmiyan-------------------- ...

  10. Hadoop:hadoop fs、hadoop dfs与hdfs dfs命令的区别

    http://blog.csdn.net/pipisorry/article/details/51340838 'Hadoop DFS'和'Hadoop FS'的区别 While exploring ...