C语言学习之路(基础篇)—— 复合类型(自定义类型)
创始人
2024-02-21 03:03:38

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

结构体

1) 概述

数组:描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算。

结构体:将多个相同或不同类型的数据存在在一块连续的内存空间中。

有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐,数据不便于管理。

语言中给出了另一种构造数据类型——结构体。
在这里插入图片描述

2) 结构体变量的定义和初始化

定义结构体变量的方式:

  • 先声明结构体类型再定义变量名
  • 在声明类型的同时定义变量
  • 直接定义结构体类型变量(无类型名)

在这里插入图片描述

结构体类型和结构体变量关系:

  • 结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元。
  • 结构体变量:系统根据结构体类型(内部成员状况)为之分配空间。

示例1:定义一个结构体数据类型

// 定义一个结构体数据类型
// 关键字 struct 代表这个是一个结构体类型,
// stu   是这个结构的名字
// 整个结构体的类型是   struct  stu
// 结构体类型struct  stu   {}中是结构体的成员,一个有3个成员,每个成员的类型可以是
// 任意的类型
// 定义结构体类型时,{}后面记得加分号
// 注意定义结构struct  stu,它只是一个类型,一个模板,没有空间,不可以给结构体成员赋值
struct stu
{int id;int age;char name[128];
}; 
int main()
{return 0;
}

示例2:定义结构体变量

// 第一种常用定义方式
struct stu
{int id;int age;char name[128];
}a; //定义类型时,同时定义了一个结构体变量,相当于struct  stu a;struct stu2
{int id;int age;char name[128];
}a,b; //定义类型时,同时定义了两个结构体变量,相当于struct  stu a,b;
// 第二种常用定义方式(用的最多)
struct stu
{int id;int age;char name[128];
};
struct  stu c;
int main()
{return 0;
}
// 第三种常用定义方式(用的很少)
struct
{int id;int age;char name[128];
}a;

示例3:结构体变量的初始化

//结构体类型的定义
struct stu
{	int id;int age;char name[128];
};//先定义类型,再定义变量(常用)
struct stu s1 = { 1, 18, "cdtaogang" };//定义类型同时定义变量
struct stu2
{	int id;int age;char name[128];
}s2 = { .age=22 }; // 给部分成员初始化,其他成员内容为0struct
{	int id;int age;char name[128];
}s3 = { "yuri", 25 };

3) 结构体成员的使用

示例1:通过结构体变量操作结构体成员,使用点域.操作

#define _CRT_SECURE_NO_WARNINGS
#include 
#include // 定义一个结构体数据类型
struct stu
{int id;int age;char name[128];
}a; //定义类型时,同时定义了一个结构体变量,相当于struct  stu a;
struct  stu b;
int main()
{	//struct  stu c = {1,20,"cdtaogang"};//struct  stu c = { .age=20 };//给部分成员初始化,其他成员内容为0struct  stu d;//如何给结构体成员赋值(如何操作结构体成员)//通过结构体变量操作结构体成员,使用点域.操作d.id = 2;d.age = 20;//d.name = "cdtaogang"; // error 数组名是一个常量,不能被赋值strcpy(d.name , "cdtaogang"); printf("%d %d %s\n",d.id,d.age,d.name);return 0;
}

示例2:通过结构体的地址操作结构体成员,使用指向->操作

#define _CRT_SECURE_NO_WARNINGS
#include 
#include // 定义一个结构体数据类型
struct stu
{int id;int age;char name[128];
}a; //定义类型时,同时定义了一个结构体变量,相当于struct  stu a;
struct  stu b;
int main()
{	struct  stu d;// 通过结构体的地址操作结构体成员,使用->(&d)->id = 3;(&d)->age = 21;strcpy((&d)->name, "cdtaogang");printf("%d %d %s\n", (&d)->id, (&d)->age, (&d)->name);return 0;
}

在这里插入图片描述

4) 结构体数组

结构体数组:是一个数组,数组的每一个元素都是结构体

在这里插入图片描述

示例:定义一个结构体数组

#include struct stu
{	int id;int age;char name[128];
};int main()
{	// 定义一个结构体数组,结构体数组有5个元素,每个元素是struct stu类型struct stu data[5] = { {1,20,"laowang"},{2,21,"lihao"},{3,22,"cdtaogang"},{4,23,"zhaoqiang"},{5,24,"wangwu"} };for (int i = 0; i < sizeof(data)/sizeof(data[0]); i++){printf("%d %d %s\n", data[i].id, data[i].age, data[i].name);}return 0;
}
输出结果
1 20 laowang
2 21 lihao
3 22 cdtaogang
4 23 zhaoqiang
5 24 wangwu

案例:统计学生成绩

#include //统计学生成绩
struct stu2
{int num;char name[20];char sex;float score;
};int main()
{//定义一个含有5个元素的结构体数组并将其初始化struct stu2 boy[5] = {{ 101, "Li ping", 'M', 45 },{ 102, "Zhang ping", 'M', 62.5 },{ 103, "He fang", 'F', 92.5 },{ 104, "Cheng ling", 'F', 87 },{ 105, "Wang ming", 'M', 58 } };int i = 0;int c = 0;float ave, s = 0;for (i = 0; i < 5; i++){s += boy[i].score;	//计算总分if (boy[i].score < 60){c += 1;		//统计不及格人的分数}}printf("s=%f\n", s);//打印总分数ave = s / 5;					//计算平均分数printf("average=%f\ncount=%d\n\n", ave, c); //打印平均分与不及格人数for (i = 0; i < 5; i++){printf(" name=%s,  score=%f\n", boy[i].name, boy[i].score);// printf(" name=%s,  score=%f\n", (boy+i)->name, (boy+i)->score);}return 0;
}

在这里插入图片描述

5) 结构体套结构体

示例:结构体套结构体

#define _CRT_SECURE_NO_WARNINGS
#include 
#include struct stu
{int id;int age;char name[128];
};struct cls_stu
{/*int id;int age;char name[128];*/struct stu s;char subject[128];
};int main()
{struct cls_stu cs;cs.s.id = 1;cs.s.age = 20;strcpy(cs.s.name, "cdtaogang");strcpy(cs.subject, "c/c++");printf("id=%d, age=%d, name=%s, subject=%s", cs.s.id, cs.s.age, cs.s.name, cs.subject);return 0;
}
输出结果
id=1, age=20, name=cdtaogang, subject=c/c++

6) 结构体赋值

示例1:第一种方法

#define _CRT_SECURE_NO_WARNINGS
#include 
#include struct stu
{int id;int age;char name[128];
};void memcpy_str(struct stu *x, struct stu* y)
{memcpy(x, y, sizeof(*x));}int main()
{struct stu a;struct stu b = { 1, 22, "cdtaogang" };//memcpy(&a, &b, sizeof(a));memcpy_str(&a, &b); //将上面改为函数调用实现形参改变实参的值printf("%d %d %s\n", a.id, a.age, a.name);return 0;
}

示例2:第二种方法

int main()
{struct stu a;struct stu b = { 1, 22, "cdtaogang" };// 第一种方法//memcpy(&a, &b, sizeof(a));//memcpy_str(&a, &b);// 第二种方法a.id = b.id;a.age = b.age;strcpy(a.name, b.name);printf("%d %d %s\n", a.id, a.age, a.name);return 0;
}

示例3:第三种方法

int main()
{struct stu a;struct stu b = { 1, 22, "cdtaogang" };// 第一种方法//memcpy(&a, &b, sizeof(a));//memcpy_str(&a, &b);// 第二种方法/*a.id = b.id;a.age = b.age;strcpy(a.name, b.name);*/// 第三种方法// a = b 内核实现的原理就是memcpy(&a, &b, sizeof(a));a = b; // 相同类型的变量是可以相互赋值printf("%d %d %s\n", a.id, a.age, a.name);return 0;
}

在这里插入图片描述

7) 结构体和指针*

7.1 指向普通结构体变量的指针

示例:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include struct stu
{int id;int age;char name[128];
};int main()
{//struct stu* p; // error 野指针 没有初始化,指向随机// 第一种方式struct stu s;struct stu* p = &s;(*p).id = 1;(*p).age = 20;strcpy((*p).name,"cdtaogang");printf("(*p).id=%d, (*p).age=%d, (*p).name=%s", (*p).id, (*p).age, (*p).name);return 0;
}
输出结果
(*p).id=1, (*p).age=20, (*p).name=cdtaogang

7.2 堆区结构体变量

示例:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include struct stu
{int id;int age;char name[128];
};int main()
{//struct stu* p; // error 野指针 没有初始化,指向随机// 第一种方式/*struct stu s;struct stu* p = &s;*/// 第二种方式struct stu* p = malloc(sizeof(struct stu));p->id = 1;p->age = 20;strcpy(p->name, "cdtaogang");printf("p->id=%d, p->age=%d, p->name%s\n", p->id, p->age, p->name);printf("(*p).id=%d, (*p).age=%d, (*p).name=%s", (*p).id, (*p).age, (*p).name);free(p);p = NULL;return 0;
}
输出结果
p->id=1, p->age=20, p->namecdtaogang
(*p).id=1, (*p).age=20, (*p).name=cdtaogang

在这里插入图片描述

7.3 结构体套一级指针

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include struct std
{	int age;char* name; //一级指针
};int main() 
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 18;p->name = "cdtaogang";printf("%d %s\n", p->age, p->name);return 0;
}

在这里插入图片描述

输出结果
18 cdtaogang

示例2:

struct std
{	int age;char* name; //一级指针
};int main() 
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 18;//p->name = "cdtaogang";strcpy(p->name, "cdtaogang"); // error 野指针 p没有指向 相当于 char *name; strcpy(name, "cdtaogang")一个道理printf("%d %s\n", p->age, p->name);return 0;
}

在这里插入图片描述

在这里插入图片描述

示例3:

struct std
{	int age;char* name; //一级指针
};int main() 
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 18;//p->name = "cdtaogang"; // okp->name = (char*)malloc(128);strcpy(p->name, "cdtaogang"); // error 野指针 p没有指向printf("%d %s\n", p->age, p->name);return 0;
}

在这里插入图片描述

输出结果
18 cdtaogang

示例4:简化分析p->name = "cdtaogang";strcpy(p->name, "cdtaogang");两种方式,这里的name在以下示例以p来表示

// 正确示例
int main()
{char* p;p = "cdtaogang";
}

在这里插入图片描述

// 错误示例
int main()
{int* p;strcpy(p, "cdtaogang");// 将cdtaogang的内容拷贝到p所指向的空间(地址)
}

在这里插入图片描述

// 正确示例
int main()
{char* p;p = (int*)malloc(128);strcpy(p, "cdtaogang");// 将cdtaogang的内容拷贝到p所指向的空间(地址)
}

在这里插入图片描述

示例5:结构体套结构体一级指针

struct s
{int a;
};
struct std
{	int age;char* name; //一级指针struct s* num; // 指针需要申请开辟空间//struct s num2; // 变量不需要申请开辟空间
};int main()
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 20;p->name = (char*)malloc(128);strcpy(p->name, "cdtaogang");//p->num2.a = 100;  // num2是变量用.不能用->p->num->a = 200; // error  num是野指针,不能直接赋值给num所指向的空间printf("%d %s\n", p->age, p->name);return 0;
}

在这里插入图片描述

解决方法一样申请开辟空间即可

struct s
{int a;
};
struct std
{	int age;char* name; //一级指针struct s* num; // 指针需要申请开辟空间//struct s num2; // 变量不需要申请开辟空间
};int main()
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 20;p->name = (char*)malloc(128);strcpy(p->name, "cdtaogang");//p->num2.a = 100;  // num2是变量用.不能用->//p->num->a = 200; // error  num是野指针,不能直接赋值给num所指向的空间p->num = (struct s*)malloc(sizeof(struct s));p->num->a = 200;printf("%d %s %d\n", p->age, p->name, p->num->a);return 0;
}

在这里插入图片描述

释放空间,从里往外释放,有多少个malloc就释放free多少个(p的空间是定义的时候就有的 ,而p指向的空间是malloc之后才有的,即才有了age、name、num的空间而name和num指向的空间是malloc之后才有的

int main()
{struct std* p = (struct std*)malloc(sizeof(struct std));p->age = 20;p->name = (char*)malloc(128);strcpy(p->name, "cdtaogang");//p->num2.a = 100;  // num2是变量用.不能用->//p->num->a = 200; // error  num是野指针,不能直接赋值给num所指向的空间p->num = (struct s*)malloc(sizeof(struct s));p->num->a = 200;printf("%d %s %d\n", p->age, p->name, p->num->a);free(p->name);free(p->num);free(p);return 0;
}

在这里插入图片描述

8) 结构体做函数参数*

8.1 结构体普通变量做函数参数

示例:结构体普通变量做函数参数

#define _CRT_SECURE_NO_WARNINGS
#include
#include //结构体类型的定义
struct stu
{char name[50];int age;
};//函数参数为结构体普通变量
void set_stu(struct stu tmp)
{strcpy(tmp.name, "cdtaogang");tmp.age = 18;printf("tmp.name = [%s], tmp.age = [%d]\n", tmp.name, tmp.age);
}int main()
{struct stu s = { 0 };set_stu(s); //值传递printf("s.name = [%s], s.age = [%d]\n", s.name, s.age);return 0;
输出结果
tmp.name = [cdtaogang], tmp.age = [18]
s.name = [], s.age = [0]

8.2 结构体指针变量做函数参数

示例:结构体指针变量做函数参数

#define _CRT_SECURE_NO_WARNINGS
#include
#include //结构体类型的定义
struct stu
{char name[50];int age;
};//函数参数为结构体指针变量
void set_stu_pro(struct stu* tmp)
{strcpy(tmp->name, "cdtaogang");tmp->age = 18;printf("tmp->name = [%s], tmp->age = [%d]\n", tmp->name, tmp->age);
}int main()
{struct stu s = { 0 };set_stu_pro(&s); //地址传递printf("s.name = [%s], s.age = [%d]\n", s.name, s.age);return 0;
}
输出结果
tmp->name = [cdtaogang], tmp->age = [18]
s.name = [cdtaogang], s.age = [18]

8.3 结构体数组名做函数参数

示例:结构体数组名做函数参数

#define _CRT_SECURE_NO_WARNINGS
#include
#include struct stu
{int id;char name[128];};
//void set_tmp(struct stu tmp[5], int n)
void set_tmp(struct stu *p, int n)
{for (int i = 0; i < n; i++){	// 第一种方式用点域(*(p + i)).id = 22;p[i].id = 22; // 上面的简写// 第二种方式用指向//(p + i)->id = 22;char buf[128];sprintf(buf, "%s%d", "cdtaoang", i+1);strcpy(p[i].name, buf);printf("p[%d].id = %d, p[%d].name = %s\n", i, p[i].id, i, p[i].name);}
}int main()
{struct stu tmp[5] = { 0 };int n = sizeof(tmp) / sizeof(tmp[0]);set_tmp(tmp, n); // tmp = &tmp[0]printf("\n");for (int i = 0; i < n; i++){printf("tmp[%d].id = %d, tmp[%d].name = %s\n", i, tmp[i].id, i, tmp[i].name);}return 0;
}

在这里插入图片描述

在这里插入图片描述

8.4 const修饰结构体指针形参变量

示例:const修饰结构体指针形参变量

#includestruct stu
{int id;char name[128];
};int main()
{struct stu a;struct stu b;struct stu const *p = &a;// p->id = 10; // error  const修饰的是*,不能通过指针p去修改p指向的那块空间里面内容(指向)struct stu * const p2 = &a;p2->id = 20; // ok//p2 = &b; // error const修饰的是指针变量p, 不能修改变量p本身的内容(指向)return 0;
}

共用体(联合体)

  • 联合union是一个能在同一个存储空间存储不同类型数据的类型;
  • 联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
  • 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
  • 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
  • 共用体变量的地址和它的各成员的地址都是同一地址。

1) 共用体的定义和初始化

示例1:定义一个共用体数据类型

union test
{char a;short b;int c;
};

示例2:定义共用体变量,跟结构体定义方式一样

union test
{char a;short b;int c;
}tmp;
union test
{char a;short b;int c;
};
union test tmp;
union
{char a;short b;int c;
}tmp;

示例3:共用体变量的初始化(共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖

#include union test
{char a;short b;int c;
};int main()
{	union test tmp;tmp.a = 0x01;tmp.b = 0x0a0b;tmp.c = 0x01020304;printf("tmp.a = %x\n", tmp.a); // 04printf("tmp.b = %x\n", tmp.b); // 0304printf("tmp.c = %x\n", tmp.c); // 01020304return 0;
}

在这里插入图片描述

#include union test
{char a;short b;int c;
};int main()
{	union test tmp;tmp.a = 0x01;tmp.c = 0x01020304;tmp.b = 0x0a0b;printf("tmp.a = %x\n", tmp.a); // 0bprintf("tmp.b = %x\n", tmp.b); // 0a0bprintf("tmp.c = %x\n", tmp.c); // 01020a0breturn 0;
}

在这里插入图片描述
示例4:共用体的大小

printf("sizeof(tmp)=%d", sizeof(tmp));  //sizeof(tmp)=4 即 sizeof(int)=4 联合体所占的内存长度等于其最长成员的长度倍数

2) 共用体判断大小端

小端: 低位存低地址,高位存高地址(大型服务器)
大端: 低位存高地址,高位存低地址(小型计算机)

在这里插入图片描述

示例:共用体判断大小端

#include union test
{short b;char buf[2];
};int main()
{union test tmp;tmp.b = 0x0102;if (tmp.buf[0] == 0x01){printf("大端\n");}else{printf("小端\n");}return 0;
}

在这里插入图片描述

输出结果
小端

枚举

枚举: 将枚举类型的变量的值一一列举出来,变量的值只限于列举出来的值的范围内。

枚举类型定义:

enum  枚举名
{枚举值表
};
  • 在枚举值表中应列出所有可用值,也称为枚举元素。
  • 枚举值是常量,不能在程序中用赋值语句再对它赋值。
  • 举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …

1) 枚举定义

示例1:为了避免需要多次定义宏,C语言提供了枚举

#define _CRT_SECURE_NO_WARNINGS
#include
#define Mon 1
#define Tue 2
#define Wed 3int main()
{	int num;scanf("%d", &num);if (Mon == num){printf("游泳\n");}else if (Tue == num){printf("跑步\n");}else if (Wed == num){printf("游戏\n");}return 0;
}
// 枚举定义
// 默认枚举里面的值从0开始
enum week
{Mon,Tue,Wed
};int main()
{	printf("%d %d %d", Mon, Tue, Wed); // 0 1 2return 0;
}

2) 枚举赋值

示例2:枚举赋值

enum week
{Mon=1,Tue,Wed
};int main()
{	printf("%d %d %d", Mon, Tue, Wed); // 1 2 3return 0;
}
enum week
{Mon,Tue=5,Wed
};int main()
{	printf("%d %d %d", Mon, Tue, Wed); // 0 5 6return 0;
}

3) 定义枚举变量

示例1:先定义枚举类型,再定义枚举变量

enum week
{Mon,Tue=5,Wed
};int main()
{	enum week day = Wed; //给枚举变量 day 赋值,值就是Wed枚举元素printf("%d %d %d", Mon, Tue, Wed); // 0 5 6return 0;
}

示例2:定义枚举类型的同时定义枚举变量

enum week
{Mon,Tue=5,Wed
}day;// 表示 定义了一个枚举类型 enum week,同时定义了一个变量 day(类型是 enum week)int main()
{	day = Wed; //给枚举变量 day 赋值,值就是某个枚举元素printf("%d %d %d", Mon, Tue, Wed); // 0 5 6return 0;
}

示例3:省略枚举名称,直接定义枚举变量

```c
enum
{Mon,Tue=5,Wed
}day;//这样使用枚举,该枚举类型只能使用一次

案例:使用枚举实现#define false 0 #define true 1

#include enum weekday
{sun = 2, mon, tue, wed, thu, fri, sat
} ;enum bool
{flase, true
};int main()
{	// 先定义枚举类型,再定义枚举变量enum weekday a, b, c;a = sun;b = mon;c = tue;printf("%d,%d,%d\n", a, b, c);enum bool flag;flag = true;if (flag == 1){printf("flag为真\n");}return 0;
}

在这里插入图片描述

typedef

typedefC语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型

  • #define不同,typedef仅限于数据类型,而不是能是表达式或具体的值
  • #define发生在预处理,typedef发生在编译阶段

示例1:使用typedef起别名

#include typedef int u32;struct stu
{int id;int age;
};
typedef struct stu ST;
int main()
{int a = 10;u32 b = 10;ST tmp;tmp.id = 2;tmp.age = 20;printf("%d %d\n", sizeof(a), sizeof(b));printf("%d %d\n", tmp.id, tmp.age);return 0;
}
输出结果
4 4
2 20

示例2:宏与typedef起别名

#include 
#define CHAR char
typedef char CHAR32;int main()
{CHAR a;CHAR32 b;a = 10;b = 20;printf("%d %d\n", sizeof(a), sizeof(b));return 0;
}
输出结果
1 1

示例3:使用宏起别名存在的问题和风险

#include 
#define CHAR char*
typedef char* CHAR32;int main()
{CHAR x, y;  // char *x,y;CHAR32 j, k; // char *j,*k;printf("%d %d\n", sizeof(x), sizeof(y));printf("%d %d\n", sizeof(j), sizeof(k));return 0;
}
输出结果
4 1
4 4

在实际编程中,取别名广泛应用,如下图所示查看VS编译器中size_t类型其实就是int类型

在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...