商丘家具网站建设,做翻译 英文网站,如何实现企业网站推广的系统性,长沙网站设计服务前言
在Android音视频开发中#xff0c;网上知识点过于零碎#xff0c;自学起来难度非常大#xff0c;不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》#xff0c;结合我自己的工作学习经历#xff0c;我准备写一个音视频系列blog。C/C是音视频必…前言
在Android音视频开发中网上知识点过于零碎自学起来难度非常大不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》结合我自己的工作学习经历我准备写一个音视频系列blog。C/C是音视频必备编程语言我准备用几篇文章来快速回顾C语言。本文是音视频系列blog的其中一个 对应的要学习的内容是快速回顾C语言的结构、联合和枚举指针的高级应用声明文件操作。 音视频系列blog
音视频系列blog 点击此处跳转查看 目录 1 结构、联合和枚举
1.1 结构变量
C语言中的结构Structure是一种用户自定义的数据类型它允许你将不同类型的数据项组合在一起形成一个逻辑上相关的数据单元。结构可以包含多个成员也称为字段每个成员可以是不同的数据类型。结构在处理复杂数据时非常有用可以使代码更清晰、模块化和可读性强。
以下是创建和使用结构变量的示例
#include stdio.h// 定义一个结构
struct Person {char name[50];int age;float height;
};int main() {// 声明一个结构变量struct Person person1;// 初始化结构变量的成员strcpy(person1.name, John);person1.age 30;person1.height 175.5;// 访问结构变量的成员printf(Name: %s\n, person1.name);printf(Age: %d\n, person1.age);printf(Height: %.2f\n, person1.height);return 0;
}在上述示例中我们首先使用struct关键字定义了一个名为Person的结构该结构有三个成员name一个字符串数组、age一个整数和height一个浮点数。然后在main函数中我们声明了一个名为person1的结构变量并使用点操作符.来访问和设置结构的成员。
此外C语言还支持通过typedef关键字来为结构类型定义别名以提高代码的可读性
#include stdio.htypedef struct {char name[50];int age;float height;
} Person;int main() {Person person1;strcpy(person1.name, John);person1.age 30;person1.height 175.5;printf(Name: %s\n, person1.name);printf(Age: %d\n, person1.age);printf(Height: %.2f\n, person1.height);return 0;
}结构在C语言中用于表示和操作复杂的数据如员工记录、图书信息、学生数据等。通过使用结构可以将相关的数据项组织在一起使代码更加结构化和易于维护。 1.2 结构类型
C语言中的结构Structure是一种用户自定义的数据类型它允许你将不同类型的数据项组合在一起形成一个逻辑上相关的数据单元。结构可以包含多个成员也称为字段每个成员可以是不同的数据类型。结构在处理复杂数据时非常有用可以使代码更清晰、模块化和可读性强。
在C语言中可以使用struct关键字来定义结构类型。结构的定义格式如下
struct 结构类型名 {数据类型 成员名1;数据类型 成员名2;// ... 其他成员
};以下是一个示例定义了一个名为Person的结构类型
struct Person {char name[50];int age;float height;
};然后可以通过该结构类型创建结构变量
struct Person person1;为了提高可读性通常会使用typedef关键字来为结构类型定义别名以便在声明结构变量时更方便地使用别名。例如
typedef struct {char name[50];int age;float height;
} Person;现在可以直接使用Person作为结构类型名
Person person1;通过结构类型可以创建包含不同类型数据的复合数据类型从而更有效地表示和操作复杂的数据。例如可以用结构表示学生信息、图书记录、员工数据等。使用结构可以将相关的数据项组织在一起使代码更加模块化和易于维护。 1.3 嵌套的数组和结构
C语言允许在数组和结构中进行嵌套即在数组或结构中嵌套其他数组或结构。这种嵌套可以用于表示更复杂的数据结构如二维数组、多维数组、结构数组等。下面是关于嵌套数组和嵌套结构的一些示例
嵌套数组示例
二维数组 二维数组是数组的数组可以用于表示矩阵等数据结构。
int matrix[3][3] {{1, 2, 3},{4, 5, 6},{7, 8, 9}
};数组中嵌套结构 可以在数组中嵌套结构用于表示一组具有相似属性的数据。
struct Point {int x;int y;
};struct Point points[5] {{1, 2},{3, 4},{5, 6},{7, 8},{9, 10}
};嵌套结构示例
结构中嵌套结构 可以在结构中嵌套其他结构用于表示更复杂的数据。
struct Date {int day;int month;int year;
};struct Person {char name[50];int age;struct Date birthdate;
};struct Person person1 {Alice,25,{15, 6, 1998}
};结构数组中嵌套结构 可以在结构数组中嵌套其他结构用于表示多个具有复杂属性的实体。
struct Book {char title[100];char author[50];
};struct Library {struct Book books[100];int numBooks;
};struct Library myLibrary {{ {Book1, Author1}, {Book2, Author2} },2
};通过嵌套数组和结构你可以创建更复杂的数据结构用于表示各种实际问题中的数据关系。这种嵌套可以让你更有效地组织和操作数据提高代码的可读性和维护性。需要注意在访问嵌套的成员时需要使用适当的点操作符或下标操作符。 1.4 联合
在C语言中联合Union是一种特殊的数据类型它允许在同一内存位置存储不同的数据类型但同一时间只能存储其中的一个成员。联合的大小等于其最大成员的大小。与结构不同联合的各个成员共享同一块内存空间。
以下是联合的定义和用法示例
#include stdio.hunion Data {int i;float f;char str[20];
};int main() {union Data data;data.i 10;printf(data.i: %d\n, data.i);data.f 3.14;printf(data.f: %.2f\n, data.f);strcpy(data.str, Hello, Union!);printf(data.str: %s\n, data.str);printf(Size of union Data: %lu bytes\n, sizeof(data));return 0;
}在上面的示例中我们定义了一个名为Data的联合它包含三个成员i整数类型、f浮点数类型和str字符串数组类型。我们可以看到联合的各个成员共享同一块内存因此在修改一个成员的值后其他成员的值也会受到影响。
需要注意以下几点
联合只能同时存储一个成员的值存储新的成员值会覆盖原有的值。联合的大小等于其最大成员的大小。联合的成员共享同一块内存因此对一个成员的修改可能会影响其他成员。联合常用于节省内存或在特定情况下实现多种数据类型的表示。
联合虽然有其独特的用途但需要谨慎使用特别是在不清楚各成员访问顺序的情况下。因为联合的成员共享内存使用不当可能导致数据混乱或错误。 1.5 枚举
C语言中枚举Enumeration是一种用户自定义的数据类型用于创建一组具有命名值的常量。枚举常常用于表示一组相关的符号常量例如表示月份、星期几等。枚举的每个值称为枚举常量Enumerator。
以下是枚举的定义和用法示例
#include stdio.h// 定义一个枚举类型
enum Weekday {Monday, // 默认值为0Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};int main() {enum Weekday today Wednesday;if (today Wednesday) {printf(Today is Wednesday.\n);} else {printf(Today is not Wednesday.\n);}return 0;
}在上面的示例中我们定义了一个名为Weekday的枚举类型其中包含了一组星期几的枚举常量。默认情况下枚举常量的值从0开始递增但可以通过显式赋值来修改枚举常量的值。在main函数中我们声明了一个名为today的枚举变量并将其赋值为Wednesday然后通过条件判断来判断今天是否为星期三。
需要注意以下几点
枚举类型是一个自定义的数据类型它可以包含一组常量值。枚举常量的默认值从0开始递增但你也可以显式地为枚举常量赋值。枚举常量的作用域在整个枚举类型内部。枚举变量可以在声明时指定类型名也可以直接使用枚举常量。
枚举类型在C语言中非常有用可以使代码更加可读性强同时提供了一种更有意义的方式来表示一组相关的常量值。 2 指针的高级应用
2.1 动态存储分配
C语言中的动态存储分配是一种高级的指针应用它允许在程序运行时动态地分配和释放内存空间以便灵活地管理数据结构和资源。这对于处理未知数量的数据或避免静态内存分配的限制非常有用。在动态存储分配中主要使用了以下几个函数malloc()、calloc()、realloc() 和 free()。 malloc() 用于分配一块指定大小的内存空间。它接受一个参数表示要分配的字节数返回分配的内存的首地址。如果分配失败返回NULL。 int *ptr;
ptr (int *)malloc(5 * sizeof(int)); // 分配包含5个整数的内存空间
if (ptr NULL) {printf(Memory allocation failed.\n);return 1;
} calloc() 用于分配一块指定数量和大小的连续内存空间初始化为零。它接受两个参数表示元素的数量和每个元素的大小返回分配的内存的首地址。 int *ptr;
ptr (int *)calloc(5, sizeof(int)); // 分配包含5个整数的内存空间并初始化为零
if (ptr NULL) {printf(Memory allocation failed.\n);return 1;
}realloc() 用于重新分配已分配内存的大小。它接受两个参数第一个参数是之前分配的内存的指针第二个参数是新的大小。它可能会在原地重新分配或者将数据移到新的内存位置。 int *newPtr;
newPtr (int *)realloc(ptr, 10 * sizeof(int)); // 将之前分配的内存重新分配为包含10个整数的内存空间
if (newPtr NULL) {printf(Memory reallocation failed.\n);free(ptr);return 1;
} else {ptr newPtr;
}free() 用于释放之前分配的内存空间以便将其返回给系统。释放内存后指针变量仍然保存着已释放的内存地址但访问它将产生未定义的行为。 free(ptr); // 释放之前分配的内存空间使用动态存储分配你可以根据需要在程序运行时分配和释放内存提高内存的使用效率和灵活性。但要小心内存泄漏和悬挂指针等问题确保在适当的时候释放已分配的内存。 2.2 动态分配字符串
在C语言中使用指针和动态内存分配可以有效地处理字符串尤其是在不知道字符串长度的情况下。动态分配字符串内存允许你根据实际需要分配和释放内存从而更灵活地处理字符串操作。
以下是动态分配字符串的示例
#include stdio.h
#include stdlib.h
#include string.hint main() {char *name;// 动态分配内存以容纳字符串name (char *)malloc(50 * sizeof(char));if (name NULL) {printf(Memory allocation failed.\n);return 1;}// 输入字符串printf(Enter your name: );scanf(%s, name);// 输出字符串printf(Hello, %s!\n, name);// 释放动态分配的内存free(name);return 0;
}在上述示例中我们首先使用malloc()函数动态分配了一块内存以容纳字符串然后使用scanf()函数输入字符串并使用printf()函数输出字符串。最后我们使用free()函数释放动态分配的内存。
需要注意以下几点
在使用malloc()分配字符串内存时需要为字符串长度留出足够的空间通常是字符串长度加上一个空字符的大小\0表示字符串的结尾。动态分配的字符串内存可以使用指针来访问和操作。一定要在使用完字符串后使用free()函数释放动态分配的内存以避免内存泄漏。使用动态分配字符串内存时要确保不会访问未初始化或未分配的内存区域以避免未定义的行为。
动态分配字符串内存允许你根据实际输入和操作的需要适应不同长度的字符串提高了灵活性和内存的有效使用。 2.3 动态分配数组
在C语言中通过指针和动态内存分配可以创建动态数组即在程序运行时根据需要分配和释放内存来存储数组元素。这使得你可以灵活地处理不确定大小的数组避免了静态数组的大小限制。
以下是动态分配数组的示例
#include stdio.h
#include stdlib.hint main() {int *numbers;int size, i;// 输入数组大小printf(Enter the size of the array: );scanf(%d, size);// 动态分配内存以容纳数组numbers (int *)malloc(size * sizeof(int));if (numbers NULL) {printf(Memory allocation failed.\n);return 1;}// 输入数组元素printf(Enter %d integers:\n, size);for (i 0; i size; i) {scanf(%d, numbers[i]);}// 输出数组元素printf(Array elements: );for (i 0; i size; i) {printf(%d , numbers[i]);}printf(\n);// 释放动态分配的内存free(numbers);return 0;
}
在上述示例中我们首先输入了数组的大小然后使用malloc()函数动态分配内存以容纳数组元素。随后我们输入数组元素输出数组元素并使用free()函数释放动态分配的内存。
需要注意以下几点
动态分配数组内存时要根据数组元素的类型和数量来分配足够的内存空间。动态数组的元素可以使用指针进行访问和操作。动态分配的数组内存应在使用完毕后使用free()函数释放以避免内存泄漏。动态数组可以根据实际需要分配不同大小的内存提高了内存的利用率和灵活性。
动态分配数组内存允许你根据实际的输入和操作灵活地创建不同大小的数组适应不同场景下的需求。 2.4 释放存储空间
在C语言中手动释放已分配的内存空间是十分重要的以避免内存泄漏未释放的内存和资源浪费。当你使用动态内存分配函数如malloc()、calloc()、realloc()分配内存后务必在不再需要使用这块内存时通过调用free()函数来释放它。
以下是释放存储空间的示例
#include stdio.h
#include stdlib.hint main() {int *numbers;// 分配内存numbers (int *)malloc(5 * sizeof(int));if (numbers NULL) {printf(Memory allocation failed.\n);return 1;}// 使用内存for (int i 0; i 5; i) {numbers[i] i * 10;}// 释放内存free(numbers);return 0;
}在上述示例中我们使用malloc()函数分配了一块内存然后使用循环为数组赋值。最后在程序结束前我们使用free()函数释放了已分配的内存。
需要注意以下几点
free()函数只能用于释放通过动态内存分配函数如malloc()、calloc()、realloc()分配的内存。使用free()来释放静态或自动分配的内存或释放同一块内存多次都会导致未定义的行为。释放内存后不要再尝试使用指向已释放内存的指针这可能会导致悬挂指针Dangling Pointer问题。内存泄漏是指在程序运行时未释放不再使用的内存这会导致内存消耗不断增加。务必确保在不再需要使用内存时释放它以保持程序的健壮性和性能。
有效地释放存储空间是编程中的一个重要方面帮助你有效管理系统资源避免内存泄漏问题以及提高程序的稳定性和性能。 2.5 受限指针
C语言中的受限指针Restricted Pointers是C99标准引入的一种指针类型用于表示指针的使用范围和行为约束。受限指针可以帮助编译器优化代码并增强代码的可读性和可维护性。它们通常在指针的生命周期中具有特定的限制和用法。
使用受限指针可以告诉编译器某个指针变量仅在某些限定的情况下才会被修改这有助于编译器进行更多的优化。受限指针通常用于在编译期间检查指针的使用情况从而提高代码的安全性和可靠性。
以下是受限指针的一个简单示例
#include stdio.hvoid increment(int *restrict ptr) {*ptr 1;
}int main() {int num 10;increment(num);printf(num: %d\n, num);return 0;
}
在上面的示例中increment函数使用了受限指针。restrict关键字告诉编译器ptr指针是一个受限指针函数内部不会修改除*ptr之外的任何内存。这使得编译器可以进行更多的优化因为它可以假设其他指针不会指向*ptr指向的内存位置。这有助于提高代码的性能。
需要注意以下几点
受限指针只能用于指针函数的形参用于约束指针的使用范围。restrict关键字只在C99标准及以后版本中有效。在使用受限指针时务必确保不会在函数中修改其他指针所指向的内存以充分发挥优化效果。
虽然受限指针可以提高代码性能和优化但在使用时需要注意其约束和限制以避免产生不正确的结果。 2.6 灵活数组成员
C语言中的灵活数组成员Flexible Array Members是C99标准引入的特性允许在结构中定义一个大小可变的数组作为最后一个成员。这使得你可以在结构中创建具有不定大小的数组从而在处理某些数据结构时更加灵活。
以下是灵活数组成员的一个简单示例
#include stdio.h
#include stdlib.hstruct DynamicArray {int length;int data[]; // 灵活数组成员大小可变
};int main() {int size 5;struct DynamicArray *array malloc(sizeof(struct DynamicArray) size * sizeof(int));if (array NULL) {printf(Memory allocation failed.\n);return 1;}array-length size;for (int i 0; i size; i) {array-data[i] i * 10;}printf(Array elements: );for (int i 0; i array-length; i) {printf(%d , array-data[i]);}printf(\n);free(array);return 0;
}在上面的示例中我们定义了一个包含灵活数组成员的结构DynamicArray。在结构定义中data[]是一个没有指定大小的数组。然后我们使用malloc()函数分配了足够的内存以容纳整个结构和数组元素。通过这种方式我们可以在不同场景下创建具有不同大小的灵活数组。
需要注意以下几点
灵活数组成员只能作为结构的最后一个成员且结构定义中只能有一个灵活数组成员。灵活数组成员的大小是在运行时根据需要进行动态分配的。在使用灵活数组成员时需要分配足够的内存以容纳整个结构和数组元素以及结构中的其他成员。释放灵活数组成员时需要使用free()函数释放整个结构的内存。
灵活数组成员允许你在结构中创建大小可变的数组适应不同大小的数据提高了数据结构的灵活性和可用性。 3 声明
3.1 声明的语法
在C语言中声明用于告诉编译器有关变量、函数或类型的信息从而在代码中引入这些实体而不需要实际定义它们。声明通常用于头文件中以便在多个源文件中共享信息。以下是C语言中一些常见声明的语法示例
变量声明 用于声明变量的名称和类型但不分配内存。
// 声明整数变量
int count;// 声明字符变量
char letter;// 声明指针变量
float *ptr;
函数声明 用于声明函数的原型包括函数名称、参数列表和返回类型。
// 声明函数原型
int add(int a, int b);// 声明不带参数的函数原型
void greet(void);
数组声明 用于声明数组的名称和类型。
// 声明整数数组
int numbers[10];// 声明字符数组字符串
char message[50];
类型声明 用于为用户自定义类型结构、枚举等创建声明。
// 声明结构类型
struct Point {int x;int y;
};// 声明枚举类型
enum Color { Red, Green, Blue };
外部变量声明 用于在一个源文件中引用另一个源文件中定义的外部变量。
// 在当前文件中声明外部变量
extern int globalVar;
需要注意以下几点
声明不分配内存或存储空间它只是为编译器提供信息以便正确解析代码。声明可以在任何地方进行但通常最好将它们放在头文件中以便在多个源文件中共享。变量和函数的定义包含实际分配内存或实现代码的部分而声明只是提供了名称和类型信息。在函数原型中可以省略参数名称只保留参数类型例如 int add(int, int);。在不同的情况下声明的语法可能会有所不同根据上下文合理使用。
在大型程序中正确使用声明可以帮助减少编译错误提高代码的可读性和可维护性。 3.2 存储类型
C语言中的存储类型Storage Class用于描述变量、函数和数据对象的生命周期、作用域和可见性等特性。C语言提供了多种存储类型以适应不同的编程需求。以下是C语言中常见的存储类型
自动存储类Automatic Storage Class 默认的存储类用于描述局部变量。它在函数内部声明的局部变量的默认存储类。当程序控制流离开变量的作用域时自动变量会被销毁。
void function() {int a; // 自动存储类
}
寄存器存储类Register Storage Class 请求编译器将变量存储在寄存器中以便提高访问速度。编译器可以选择将部分变量存储在寄存器中。但是由于寄存器数量有限不一定所有请求都会被满足。
register int x; // 寄存器存储类
静态存储类Static Storage Class 用于描述具有持久性的局部变量它在程序的整个生命周期内保持其值。静态变量在首次执行它们的声明语句时初始化并在整个程序执行过程中保持值。
static int count 0; // 静态存储类
外部存储类External Storage Class 用于描述全局变量和外部函数。外部变量在多个文件之间共享它们可以在一个文件中定义在其他文件中声明以便在整个程序中使用。
extern int globalVar; // 外部存储类
线程存储类Thread Storage Class 用于多线程程序指示变量在每个线程中具有单独的存储。
_Thread_local int data; // 线程存储类
空存储类No Storage Class 用于描述类型但不指定存储类。通常在类型声明中使用。
typedef int Age; // 空存储类用于类型别名
不同存储类的使用取决于程序的需求。理解这些存储类的特性和用法将有助于正确地管理变量的生命周期、作用域和可见性以及优化代码的性能和可读性。 3.3 类型限定符
C语言中的类型限定符用于修改变量的存储类别和访问权限以及指定变量的性质和使用方式。C语言提供了以下几种常见的类型限定符
const const 用于声明一个只读变量表示变量的值不能被修改。
const int x 10; // 声明一个只读整数变量
volatile volatile 用于声明一个变量是易变的表示变量的值可能会在未经预期的情况下被修改例如在中断服务程序中。
volatile int sensorValue; // 声明一个易变的整数变量
restrict restrict 用于声明指针表示指针是唯一引用对象的指针编译器可以对这种指针进行更好的优化。
void updateArray(int *restrict dest, const int *restrict src, size_t n);
_Atomic _Atomic 用于声明一个原子类型的变量支持多线程并发访问时的原子操作。
_Atomic int counter; // 声明一个原子整数变量
_Thread_local _Thread_local 用于声明一个线程本地变量使变量在每个线程中具有单独的副本。
_Thread_local int threadLocalData; // 声明一个线程本地整数变量
这些类型限定符可以根据程序的需求来使用以改变变量的特性、可见性和存储行为。例如const 可用于确保变量的值不会被意外修改volatile 可用于处理不受控制的变量访问restrict 可以帮助编译器进行更好的优化等。理解并正确使用这些类型限定符可以提高代码的安全性、可靠性和性能。 3.4 声明符
C语言中的声明符Declarator用于描述变量或函数的名称以及与之相关的一些属性。声明符是用于创建变量或函数的声明的一部分它告诉编译器有关实体的类型、存储类和其他信息。以下是一些常见的声明符形式 直接声明符Direct Declarator 用于声明变量、数组和函数直接指定名称和类型。 变量声明type variableName; int x; 数组声明type arrayName[size]; int numbers[10]; 函数声明returnType functionName(parameters); int add(int a, int b); 指针声明符Pointer Declarator 用于声明指向其他类型的指针。 int *ptr; // 声明一个指向整数的指针 数组声明符Array Declarator 用于声明数组包括数组的维度。 int matrix[3][3]; // 声明一个3x3的整数数组 函数声明符Function Declarator 用于声明函数包括参数列表和返回类型。 int (*funcPtr)(int, int); // 声明一个函数指针 函数指针声明符Function Pointer Declarator 用于声明指向函数的指针。 int (*operation)(int, int); // 声明一个指向函数的函数指针 类型限定符声明符Type Qualifier Declarator 用于声明带有类型限定符的变量。 const int *readOnlyPtr; // 声明一个指向只读整数的指针 类型限定符函数声明符Type Qualifier Function Declarator 用于声明带有类型限定符的函数。 int sum(const int array[], int size); // 声明一个带有只读参数的函数
声明符的形式因所声明实体的类型和属性而异。在C语言中声明符是用于创建变量和函数声明的重要组成部分帮助编译器正确解析和生成代码。 3.5 初始化式
C语言中的初始化式Initializer用于在声明变量时为其赋初值。初始化式可以用于各种类型的变量包括基本数据类型、数组、结构和联合等。以下是一些常见的初始化式形式 基本数据类型的初始化 初始化整数变量 int x 10; 初始化浮点数变量 float pi 3.14159; 初始化字符变量 char letter A; 数组的初始化 初始化整数数组 int numbers[5] {1, 2, 3, 4, 5}; 部分初始化数组剩余元素会被自动初始化为0 int partialInit[10] {1, 2, 3}; 结构的初始化 初始化结构变量 struct Point {int x;int y;
};struct Point p {10, 20}; 联合的初始化 初始化联合变量 union Data {int num;float pi;
};union Data d {3.14159}; 指针的初始化 初始化指向整数的指针 int x 10;
int *ptr x; 初始化指向字符串的指针 char *message Hello, world!; 复合初始化 可以通过嵌套使用大括号进行复合初始化用于初始化数组和结构中的嵌套元素。 struct Rectangle {struct Point topLeft;struct Point bottomRight;
};struct Rectangle rect {{0, 0}, {100, 200}};需要注意以下几点
初始化式是在变量声明时提供初值的一种方式用于初始化变量的内容。对于未显式初始化的变量C语言会自动将其初始化为默认值例如整数为0浮点数为0.0指针为NULL字符为’\0’。复合初始化对于数组和结构非常有用可以在一条语句中初始化多个元素。初始化式可以使用在各种不同类型的变量声明中包括局部变量和全局变量。
正确的初始化可以帮助你避免使用未定义的内存内容并提供初始值以便在变量声明后立即使用。 3.6 内联函数
C语言内联函数Inline Function是一种编译器的优化手段用于在编译时将函数的调用代码替换为函数体的实际代码从而减少函数调用的开销。内联函数通常用于需要频繁调用的短小函数以提高程序的性能。内联函数的使用方式和语法如下
内联函数声明 在函数声明前加上关键字 inline。
inline int add(int a, int b) {return a b;
}内联函数调用 编译器会尝试将内联函数的调用替换为函数体。
int result add(5, 3); // 可能被替换为 int result 5 3;需要注意以下几点
内联函数通常适用于简单的、执行时间短的函数例如数学运算、比较操作等。内联函数的调用可能会增加代码的大小因为函数体会被复制到每个调用点。编译器会根据函数的复杂性和调用频率来决定是否真正内联函数。使用内联函数的前提是函数的定义必须在调用点之前以便编译器知道函数的实际代码。内联函数不一定比普通函数更快它的效果取决于具体情况。在一些情况下编译器会自动进行优化而不需要显式使用内联函数。
需要注意的是内联函数只是对编译器的建议编译器可以选择是否真正内联函数。使用内联函数时应当在性能和代码大小之间进行权衡适用于需要频繁调用的短小函数以提高程序的性能。 4 输入输出
4.1 流
4.1.1 文件指针
在C语言中文件指针File Pointer是一种用于访问文件的数据结构。文件指针可以用于在文件中定位并操作数据包括读取和写入。使用文件指针你可以打开文件、定位文件中的特定位置以及进行文件的读写操作。
C语言标准库提供了一组函数来处理文件指针的操作其中最常用的函数包括 FILE 数据类型 C语言中使用 FILE 数据类型来表示文件指针。它是一个不透明的结构体类型用于管理文件的信息。 fopen 函数 用于打开文件并返回一个指向该文件的指针。它接受两个参数文件名和打开模式。 FILE *fptr fopen(myfile.txt, r); // 以只读模式打开文件 fclose 函数 用于关闭已打开的文件。关闭文件后对文件的读写操作将不再有效。 fclose(fptr); // 关闭文件指针 读取操作 C语言提供了多个函数用于从文件中读取数据。 fgetc: 从文件中读取一个字符。fgets: 从文件中读取一行字符串。fread: 从文件中读取指定数量的字节。 写入操作 C语言提供了多个函数用于向文件中写入数据。 fputc: 将一个字符写入文件。fputs: 将一个字符串写入文件。fwrite: 将指定数量的字节写入文件。 定位函数 用于在文件中定位文件指针的位置。 fseek: 移动文件指针到指定的位置。ftell: 返回文件指针的当前位置。 错误处理 C语言提供了 feof 和 ferror 函数用于检测文件读写的错误。
文件指针的使用可以帮助你读取和写入文件中的数据进行文件的操作和管理。在操作文件时务必确保正确地打开和关闭文件以避免资源泄漏和数据丢失。 4.1.2 标准流和重定向
C语言标准流Standard Streams是在C语言中用于输入和输出的三个默认流它们分别是标准输入流stdin、标准输出流stdout和标准错误流stderr。这些流与键盘、屏幕和错误信息相关联允许程序与用户交互并进行输入输出操作。标准流通常通过文件指针进行访问。
重定向是一种将标准流与其他文件或设备连接的机制。通过重定向你可以将标准输入流、标准输出流或标准错误流重定向到文件中从而实现文件的输入输出而不仅限于键盘和屏幕。以下是有关C语言标准流和重定向的一些重要信息
标准输入流stdin 默认情况下关联于键盘输入可以通过重定向将其关联到文件。标准输出流stdout 默认情况下关联于屏幕输出可以通过重定向将其关联到文件。标准错误流stderr 默认情况下关联于屏幕输出用于显示错误信息可以通过重定向将其关联到文件。重定向操作符 在命令行中使用特定的符号来进行重定向操作。 : 用于将标准输出重定向到文件中。: 用于将标准输出追加到文件中。: 用于将文件内容作为标准输入。2: 用于将标准错误重定向到文件中。2: 用于将标准错误追加到文件中。
示例
./myprogram output.txt # 将程序的标准输出重定向到output.txt文件
./myprogram input.txt # 将input.txt文件内容作为程序的标准输入
./myprogram 2 error.txt # 将程序的标准错误重定向到error.txt文件
使用 freopen 函数进行重定向 在C语言程序中可以使用 freopen 函数来实现重定向操作。
#include stdio.hint main() {freopen(output.txt, w, stdout); // 将标准输出重定向到output.txt文件printf(This is redirected output.\n);return 0;
}
通过重定向你可以将程序的输入输出与文件关联实现更灵活的数据处理和文件操作。 4.1.3 文本文件与二进制文件
C语言中的文件可以分为两种主要类型文本文件和二进制文件。这两种文件类型的区别在于它们存储和表示数据的方式。 文本文件 文本文件是以文本形式存储的文件其中的数据以可读的字符表示通常使用ASCII编码。文本文件包含普通文本字符例如字母、数字、标点符号等。文本文件可以被普通文本编辑器打开和编辑。 示例textfile.txt Hello, this is a text file.
Line 2 of the text file. 二进制文件 二进制文件是以二进制形式存储的文件其中的数据以字节表示可以包含任何类型的数据例如图像、音频、视频等。二进制文件的内容不是人类可读的需要特定的程序来解析和处理。二进制文件通常更紧凑适用于存储和传输非文本数据。 示例binaryfile.bin 01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01100010 01101001 01101110 01100001 01110010 01111001 00100000 01100101 01111010 01100001 01101101 01110000 01101100 01100101 00101110
在C语言中你可以使用标准库函数来读写文本文件和二进制文件。 读写文本文件 FILE *file fopen(textfile.txt, r); // 打开文本文件进行读取
// 使用 fscanf 读取文本文件内容
fclose(file); 读写二进制文件 FILE *file fopen(binaryfile.bin, rb); // 打开二进制文件进行读取
// 使用 fread 读取二进制文件内容
fclose(file); 写入文本文件 FILE *file fopen(textfile.txt, w); // 打开文本文件进行写入
// 使用 fprintf 写入文本内容
fclose(file); 写入二进制文件 FILE *file fopen(binaryfile.bin, wb); // 打开二进制文件进行写入
// 使用 fwrite 写入二进制内容
fclose(file);
可以根据需求选择使用文本文件还是二进制文件来存储数据具体的选择取决于所处理数据的性质以及需要。 4.2 文件操作
4.2.1 打开文件
在C语言中你可以使用标准库函数来打开文件以便进行读取和写入操作。主要用到的函数是 fopen 函数。下面是关于如何在C语言中打开文件的一些重要信息
#include stdio.hint main() {FILE *file; // 声明一个文件指针// 打开文件进行读取r 表示只读模式file fopen(myfile.txt, r);if (file NULL) {printf(File could not be opened.\n);return 1;}// 文件读取操作// 关闭文件fclose(file);return 0;
}
在上面的示例中我们使用了 fopen 函数来打开一个名为 “myfile.txt” 的文件并指定了只读模式 r。如果文件打开失败fopen 函数将返回一个空指针NULL。在读写操作完成后使用 fclose 函数关闭文件。
你可以使用不同的模式来打开文件以适应不同的操作需求
r: 只读模式Read mode文件必须存在。w: 写入模式Write mode如果文件存在则清空内容如果不存在则创建文件。a: 追加模式Append mode如果文件存在则在末尾追加数据如果不存在则创建文件。rb: 二进制读取模式Binary read mode。wb: 二进制写入模式Binary write mode。ab: 二进制追加模式Binary append mode。
在打开文件后你可以使用其他标准库函数如 fscanf、fgets、fread、fprintf、fputs 等来进行读写操作。完成文件操作后务必使用 fclose 函数关闭文件以确保资源正确释放。
需要注意的是文件操作可能会受到操作系统权限的限制确保你有足够的权限来读写目标文件。 4.2.2 模式
在C语言中文件操作通过使用标准库函数来实现。这些函数位于 stdio.h 头文件中提供了多种文件操作模式。以下是常用的文件操作模式及其说明
“r” (Read): 以只读模式打开文件。如果文件不存在打开操作将失败如果文件存在数据可以被读取。
FILE *file fopen(filename.txt, r);“w” (Write): 以写入模式打开文件。如果文件不存在将创建一个新文件如果文件存在文件中的内容将被清空。
FILE *file fopen(filename.txt, w);“a” (Append): 以追加模式打开文件。如果文件不存在将创建一个新文件如果文件存在数据将被追加到文件末尾。
FILE *file fopen(filename.txt, a);“rb” (Read Binary): 以二进制只读模式打开文件。与 “r” 类似但以二进制模式处理文件。
FILE *file fopen(filename.bin, rb);“wb” (Write Binary): 以二进制写入模式打开文件。与 “w” 类似但以二进制模式处理文件。
FILE *file fopen(filename.bin, wb);
“ab” (Append Binary): 以二进制追加模式打开文件。与 “a” 类似但以二进制模式处理文件。
FILE *file fopen(filename.bin, ab);
“r” (Read and Write): 以读写模式打开文件。文件必须存在可以读取和写入数据。
FILE *file fopen(filename.txt, r);“w” (Write and Read): 以读写模式打开文件。如果文件不存在将创建一个新文件如果文件存在文件中的内容将被清空可以读取和写入数据。
FILE *file fopen(filename.txt, w);“a” (Append and Read): 以读写追加模式打开文件。如果文件不存在将创建一个新文件如果文件存在数据将被追加到文件末尾可以读取和写入数据。
FILE *file fopen(filename.txt, a);这些模式提供了不同的文件操作选项您可以根据需要选择合适的模式来操作文件。注意在操作文件后应该使用 fclose() 函数来关闭文件以释放资源。 4.2.3 关闭文件
在C语言中要关闭已打开的文件您需要使用 fclose() 函数。这个函数接受一个文件指针作为参数然后将该文件指针所表示的文件关闭释放与该文件关联的资源。
以下是 fclose() 函数的使用方法
#include stdio.hint main() {FILE *file fopen(filename.txt, r);if (file NULL) {printf(Failed to open the file.\n);return 1;}// 在这里进行文件读写操作// 关闭文件fclose(file);return 0;
}请注意以下几点
在打开文件后进行了文件读写等操作后应该使用 fclose() 来关闭文件以确保资源得到释放。在关闭文件之后不应再尝试使用已关闭的文件指针进行读写操作否则可能会导致未定义的行为。检查 fopen() 是否成功打开文件。如果文件打开失败例如文件不存在或没有读取权限文件指针将为 NULL在尝试关闭文件之前应该进行错误检查。虽然在程序终止时操作系统会关闭已打开的文件但是最好的实践是在您不再需要文件时显式地使用 fclose() 函数关闭文件。
如果您在程序执行期间打开了多个文件每个文件都需要使用 fclose() 分别关闭。 4.2.4 为打开的流附加文件
在C语言中您可以使用不同的文件打开模式来附加内容到已经存在的文件中。为了在已打开的流文件指针中附加内容您可以使用以下打开模式
“a” (Append): 使用这个模式打开文件会将数据追加到文件的末尾。如果文件不存在将创建一个新文件。
FILE *file fopen(filename.txt, a);
if (file NULL) {printf(Failed to open the file.\n);return 1;
}
// 写入或追加数据到文件
fprintf(file, This is new content.\n);
// 关闭文件
fclose(file);
“ab” (Append Binary): 以二进制追加模式打开文件。与 “a” 类似但以二进制模式处理文件。
FILE *file fopen(filename.bin, ab);
if (file NULL) {printf(Failed to open the file.\n);return 1;
}
// 写入或追加数据到文件
fwrite(data, sizeof(data[0]), sizeof(data) / sizeof(data[0]), file);
// 关闭文件
fclose(file);
“a” (Append and Read): 以读写追加模式打开文件。如果文件不存在将创建一个新文件。可以在文件末尾追加数据并在需要时进行读取。
FILE *file fopen(filename.txt, a);
if (file NULL) {printf(Failed to open the file.\n);return 1;
}
// 写入或追加数据到文件
fprintf(file, This is new content.\n);
// 读取数据
char buffer[100];
fseek(file, 0, SEEK_SET); // 将文件指针设置回文件开头
while (fgets(buffer, sizeof(buffer), file) ! NULL) {printf(%s, buffer);
}
// 关闭文件
fclose(file);
以上示例中使用不同的打开模式来附加内容到文件中。请根据您的实际需求选择适当的模式。在操作文件后不要忘记使用 fclose() 函数来关闭文件。 4.2.5 从命令行获取文件名
在C语言中您可以使用 main() 函数的参数来从命令行获取文件名。命令行参数以字符串数组的形式传递给 main() 函数其中第一个参数是程序的名称后续参数是由空格分隔的命令行参数。
以下是如何从命令行获取文件名的示例代码
#include stdio.hint main(int argc, char *argv[]) {if (argc ! 2) {printf(Usage: %s filename\n, argv[0]);return 1;}char *filename argv[1];printf(File name: %s\n, filename);// 在这里可以使用 filename 进行文件操作return 0;
}在这个示例中argc 是命令行参数的数量argv 是一个指向指针的数组每个指针指向一个命令行参数字符串。通常argv[0] 是程序的名称argv[1] 是第一个命令行参数即文件名。
运行这个程序时您可以在命令行中传递文件名作为参数例如
./program_name filename.txt这将输出
File name: filename.txt然后您可以使用 filename 变量来进行文件操作如打开、读取或写入文件。请注意在实际使用中应该添加适当的错误检查和文件操作代码。 4.2.6 临时文件
C语言中可以使用标准库函数来创建临时文件这在需要在程序运行时临时存储数据时非常有用。临时文件通常在程序结束后会被自动删除因此适合用于存储临时性的数据或中间结果。
您可以使用 tmpfile() 函数来创建一个临时文件并返回一个指向该临时文件的文件指针。下面是一个示例
#include stdio.hint main() {FILE *tempFile tmpfile();if (tempFile NULL) {printf(Failed to create temporary file.\n);return 1;}fprintf(tempFile, This is temporary data.\n);// 这里可以进行其他操作如读取或写入数据// 关闭临时文件文件会在程序结束时自动删除fclose(tempFile);return 0;
}
在这个示例中tmpfile() 函数创建一个临时文件您可以像操作普通文件一样使用返回的文件指针进行读写操作。临时文件会在程序结束时自动被删除。
如果您需要在特定的目录中创建临时文件可以使用 tmpnam() 函数或 tmpnam_r() 函数来生成一个唯一的临时文件名。然后您可以使用生成的文件名来创建临时文件。注意tmpnam() 可能会在多线程环境下存在安全问题建议使用 tmpnam_r() 或其他安全的替代方法。 4.2.7 文件缓冲
在C语言中文件缓冲是一种用于提高文件读写性能的机制。文件缓冲将文件数据存储在内存中以减少频繁的磁盘访问操作从而提高了文件操作的效率。C标准库提供了三种文件缓冲类型
全缓冲 (Fully Buffered): 当缓冲区被填满或调用 fflush() 函数时数据被写入磁盘。这是默认的文件缓冲类型适用于大部分文件。
setvbuf(file, NULL, _IOFBF, BUFSIZ);
行缓冲 (Line Buffered): 当缓冲区被填满、遇到换行符 \n 或调用 fflush() 函数时数据被写入磁盘。适用于需要逐行写入的文件如终端设备。
setvbuf(file, NULL, _IOLBF, BUFSIZ);
无缓冲 (Unbuffered): 每次写入都会立即写入磁盘适用于需要实时写入的文件如标准错误流。
setvbuf(file, NULL, _IONBF, BUFSIZ);
其中setvbuf() 函数用于设置文件缓冲类型具体的参数解释如下
第一个参数是文件指针。第二个参数通常为 NULL表示使用默认缓冲大小。第三个参数可以是 _IOFBF全缓冲、_IOLBF行缓冲或 _IONBF无缓冲。第四个参数是缓冲区的大小可以根据需要进行设置。
以下是一个示例演示如何使用不同的文件缓冲类型
#include stdio.hint main() {FILE *file;// 全缓冲file fopen(full_buffered.txt, w);setvbuf(file, NULL, _IOFBF, BUFSIZ);fprintf(file, This is a fully buffered file.\n);fclose(file);// 行缓冲file fopen(line_buffered.txt, w);setvbuf(file, NULL, _IOLBF, BUFSIZ);fprintf(file, This is a line buffered file.\n);fclose(file);// 无缓冲file fopen(unbuffered.txt, w);setvbuf(file, NULL, _IONBF, BUFSIZ);fprintf(file, This is an unbuffered file.\n);fclose(file);return 0;
} 4.2.8 其他文件操作
除了前面提到的文件打开、关闭和缓冲相关的操作C语言还提供了许多其他文件操作函数用于进行文件的读取、写入、定位以及错误处理等操作。以下是一些常见的文件操作函数
读取文件内容 fgetc(FILE *stream): 从文件中读取一个字符。fgets(char *str, int n, FILE *stream): 从文件中读取一行文本。fread(void *ptr, size_t size, size_t count, FILE *stream): 从文件中读取指定数量的字节数据。 写入文件内容 fputc(int c, FILE *stream): 将一个字符写入文件。fputs(const char *str, FILE *stream): 将字符串写入文件。fwrite(const void *ptr, size_t size, size_t count, FILE *stream): 将指定数量的字节数据写入文件。 文件定位 fseek(FILE *stream, long int offset, int whence): 移动文件指针到指定位置。ftell(FILE *stream): 获取当前文件指针的位置。 刷新文件缓冲 fflush(FILE *stream): 刷新文件缓冲将缓冲区数据写入文件。 检查文件结尾 feof(FILE *stream): 检查文件是否已经到达结尾。 清除文件错误状态 clearerr(FILE *stream): 清除文件的错误状态。 文件错误处理 perror(const char *str): 打印错误信息。errno: 表示最近一次错误代码的全局变量。 重命名和删除文件 rename(const char *oldname, const char *newname): 重命名文件。remove(const char *filename): 删除文件。 检查文件是否存在 使用系统特定的函数例如access() 在Unix/Linux中来检查文件是否存在。
这些函数提供了广泛的功能可用于进行文件的读写、定位、错误处理等操作。请根据您的具体需求选择适当的函数进行文件操作。在使用这些函数时始终记得进行错误检查以确保文件操作能够正常执行。 4.3 字符的输入/输出
在C语言中可以使用标准库函数来进行字符的输入和输出操作。以下是常用的字符输入和输出函数
字符输入 getchar(): 从标准输入键盘获取一个字符。getch() (非标准函数): 从终端获取一个字符不需要按下回车键。getchar_unlocked() (非标准函数): 与 getchar() 类似但不进行线程锁定适用于单线程环境。
#include stdio.hint main() {char ch;printf(Enter a character: );ch getchar();printf(You entered: %c\n, ch);return 0;
}
字符输出 putchar(int c): 输出一个字符到标准输出屏幕。putch() (非标准函数): 输出一个字符到终端不需要换行。
#include stdio.hint main() {char ch A;putchar(ch);putchar(\n); // 换行return 0;
}
这些函数可以用于处理单个字符的输入和输出操作。在实际应用中您可以将这些函数与循环结构结合使用从而实现更复杂的字符处理逻辑。注意字符输入函数通常会在用户按下回车键后才会返回而不需要特殊操作的字符输出函数可以直接将字符打印到终端。 4.4 行的输入/输出
在C语言中行的输入和输出涉及到字符串的处理通常使用标准库函数来进行。以下是一些常用的行输入和输出函数
行输入 fgets(char *str, int n, FILE *stream): 从文件中读取一行文本包括换行符最多读取 n-1 个字符。它会将读取的内容存储到字符串 str 中并在末尾添加一个 null 终止字符。
#include stdio.hint main() {char buffer[100];printf(Enter a string: );fgets(buffer, sizeof(buffer), stdin);printf(You entered: %s, buffer);return 0;
}行输出 puts(const char *str): 输出一个字符串到标准输出屏幕并自动添加换行符。
#include stdio.hint main() {char str[] Hello, world!;puts(str);return 0;
}这些函数允许您处理包含换行符的文本行。fgets() 函数会读取整行文本包括换行符而 puts() 函数会将字符串输出并自动添加换行符。
请注意fgets() 会在达到指定的最大字符数或读取到换行符时停止读取所以需要适当处理换行符。另外puts() 输出字符串时会自动添加换行符因此无需手动添加。 4.5 块的输入/输出
在C语言中块的输入和输出通常涉及到二进制数据的读写而不是像行输入输出那样处理文本数据。您可以使用标准库函数来进行块的输入和输出操作。
以下是常用的二进制块输入和输出函数
块输入 fread(void *ptr, size_t size, size_t count, FILE *stream): 从文件中读取指定数量的字节数据并存储到内存中的缓冲区 ptr 中。size 是每个数据项的大小count 是要读取的数据项数量。
#include stdio.hint main() {int data[5];FILE *file fopen(data.bin, rb);if (file NULL) {printf(Failed to open the file.\n);return 1;}fread(data, sizeof(data[0]), 5, file);for (int i 0; i 5; i) {printf(Data[%d]: %d\n, i, data[i]);}fclose(file);return 0;
}块输出 fwrite(const void *ptr, size_t size, size_t count, FILE *stream): 将内存中的数据块写入文件。ptr 是包含数据的指针size 是每个数据项的大小count 是要写入的数据项数量。
#include stdio.hint main() {int data[] { 10, 20, 30, 40, 50 };FILE *file fopen(data.bin, wb);if (file NULL) {printf(Failed to open the file.\n);return 1;}fwrite(data, sizeof(data[0]), 5, file);fclose(file);return 0;
}这些函数允许您以二进制方式读取和写入数据块。通过适当设置 size 和 count您可以处理不同类型和大小的数据块。 4.6 文件定位
在C语言中您可以使用文件定位函数来在文件中移动文件指针的位置以便进行读写操作。文件定位函数允许您以字节为单位进行精确定位。
以下是常用的文件定位函数
fseek() 函数用于设置文件指针的位置。
int fseek(FILE *stream, long int offset, int whence);stream: 文件指针。offset: 相对于 whence 的偏移量。正数表示向后移动负数表示向前移动。whence: 定位基准可以是以下常量之一 SEEK_SET: 从文件开头开始计算偏移。SEEK_CUR: 从当前位置开始计算偏移。SEEK_END: 从文件末尾开始计算偏移。
ftell() 函数获取当前文件指针的位置。
long int ftell(FILE *stream);下面是一个使用 fseek() 和 ftell() 的示例
#include stdio.hint main() {FILE *file fopen(data.txt, r);if (file NULL) {printf(Failed to open the file.\n);return 1;}fseek(file, 10, SEEK_SET); // 移动到文件开头的第 10 个字节long int position ftell(file); // 获取当前文件指针位置printf(Current position: %ld\n, position);fclose(file);return 0;
}在此示例中我们打开了一个文件使用 fseek() 将文件指针移动到文件开头的第 10 个字节然后使用 ftell() 获取当前文件指针的位置。
文件定位函数允许您在文件中随意移动文件指针以便进行读写操作。请注意如果您在一个已经打开的文件上进行移动务必确保进行错误检查以处理可能的错误情况。 4.7 字符串的输入/输出
在C语言中您可以使用标准库函数来进行字符串的输入和输出操作。以下是常用的字符串输入和输出函数
字符串输入 scanf() 函数用于从标准输入键盘读取格式化的输入可以读取字符串但可能会受到空格等字符的影响。
#include stdio.hint main() {char str[100];printf(Enter a string: );scanf(%s, str);printf(You entered: %s\n, str);return 0;
}gets() 函数用于从标准输入键盘读取一行字符串包括空格但不进行越界检查。
#include stdio.hint main() {char str[100];printf(Enter a string: );gets(str);printf(You entered: %s\n, str);return 0;
}fgets() 函数用于从文件中读取一行字符串包括空格也可以用于从标准输入读取。
#include stdio.hint main() {char str[100];printf(Enter a string: );fgets(str, sizeof(str), stdin);printf(You entered: %s, str);return 0;
}字符串输出 printf() 函数用于将格式化的字符串输出到标准输出屏幕。
#include stdio.hint main() {char str[] Hello, world!;printf(%s\n, str);return 0;
}puts() 函数用于将字符串输出到标准输出屏幕自动添加换行符。
#include stdio.hint main() {char str[] Hello, world!;puts(str);return 0;
}这些函数允许您在程序中输入和输出字符串。gets() 函数存在缓冲区溢出的安全问题不推荐使用。使用 fgets() 函数可以更安全地读取一行字符串。在使用这些函数时请确保适当处理换行符和空格等特殊字符。
文章转载自: http://www.morning.seoqun.com.gov.cn.seoqun.com http://www.morning.wschl.cn.gov.cn.wschl.cn http://www.morning.qzqjz.cn.gov.cn.qzqjz.cn http://www.morning.zxybw.cn.gov.cn.zxybw.cn http://www.morning.qwwcf.cn.gov.cn.qwwcf.cn http://www.morning.htbsk.cn.gov.cn.htbsk.cn http://www.morning.fkyqt.cn.gov.cn.fkyqt.cn http://www.morning.pqrhb.cn.gov.cn.pqrhb.cn http://www.morning.rqfzp.cn.gov.cn.rqfzp.cn http://www.morning.c7496.cn.gov.cn.c7496.cn http://www.morning.zydr.cn.gov.cn.zydr.cn http://www.morning.jkfyt.cn.gov.cn.jkfyt.cn http://www.morning.vnuwdy.cn.gov.cn.vnuwdy.cn http://www.morning.thwhn.cn.gov.cn.thwhn.cn http://www.morning.bzqnp.cn.gov.cn.bzqnp.cn http://www.morning.jhtrb.cn.gov.cn.jhtrb.cn http://www.morning.srrzb.cn.gov.cn.srrzb.cn http://www.morning.ldmtq.cn.gov.cn.ldmtq.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.fpryg.cn.gov.cn.fpryg.cn http://www.morning.xfyjn.cn.gov.cn.xfyjn.cn http://www.morning.ftwlay.cn.gov.cn.ftwlay.cn http://www.morning.bbjw.cn.gov.cn.bbjw.cn http://www.morning.tbkqs.cn.gov.cn.tbkqs.cn http://www.morning.mqgqf.cn.gov.cn.mqgqf.cn http://www.morning.rmltt.cn.gov.cn.rmltt.cn http://www.morning.kjrlp.cn.gov.cn.kjrlp.cn http://www.morning.xzqzd.cn.gov.cn.xzqzd.cn http://www.morning.jykzy.cn.gov.cn.jykzy.cn http://www.morning.hbnwr.cn.gov.cn.hbnwr.cn http://www.morning.dmldp.cn.gov.cn.dmldp.cn http://www.morning.qfqld.cn.gov.cn.qfqld.cn http://www.morning.qfkxj.cn.gov.cn.qfkxj.cn http://www.morning.nrwr.cn.gov.cn.nrwr.cn http://www.morning.nlkjq.cn.gov.cn.nlkjq.cn http://www.morning.trbxt.cn.gov.cn.trbxt.cn http://www.morning.ypnxq.cn.gov.cn.ypnxq.cn http://www.morning.rmpkn.cn.gov.cn.rmpkn.cn http://www.morning.npbgj.cn.gov.cn.npbgj.cn http://www.morning.jthjr.cn.gov.cn.jthjr.cn http://www.morning.bsjxh.cn.gov.cn.bsjxh.cn http://www.morning.youngbase.cn.gov.cn.youngbase.cn http://www.morning.wfkbk.cn.gov.cn.wfkbk.cn http://www.morning.wskn.cn.gov.cn.wskn.cn http://www.morning.youprogrammer.cn.gov.cn.youprogrammer.cn http://www.morning.yfpnl.cn.gov.cn.yfpnl.cn http://www.morning.zwppm.cn.gov.cn.zwppm.cn http://www.morning.ryfq.cn.gov.cn.ryfq.cn http://www.morning.sfswj.cn.gov.cn.sfswj.cn http://www.morning.fddfn.cn.gov.cn.fddfn.cn http://www.morning.xhklb.cn.gov.cn.xhklb.cn http://www.morning.fpqsd.cn.gov.cn.fpqsd.cn http://www.morning.fqsxf.cn.gov.cn.fqsxf.cn http://www.morning.ybqlb.cn.gov.cn.ybqlb.cn http://www.morning.cfynn.cn.gov.cn.cfynn.cn http://www.morning.drswd.cn.gov.cn.drswd.cn http://www.morning.sbrpz.cn.gov.cn.sbrpz.cn http://www.morning.hlshn.cn.gov.cn.hlshn.cn http://www.morning.nxzsd.cn.gov.cn.nxzsd.cn http://www.morning.rwzc.cn.gov.cn.rwzc.cn http://www.morning.jfqqs.cn.gov.cn.jfqqs.cn http://www.morning.tdhxp.cn.gov.cn.tdhxp.cn http://www.morning.xwlmg.cn.gov.cn.xwlmg.cn http://www.morning.ywqsk.cn.gov.cn.ywqsk.cn http://www.morning.fjscr.cn.gov.cn.fjscr.cn http://www.morning.tldfp.cn.gov.cn.tldfp.cn http://www.morning.snlxb.cn.gov.cn.snlxb.cn http://www.morning.tkyry.cn.gov.cn.tkyry.cn http://www.morning.zdsdn.cn.gov.cn.zdsdn.cn http://www.morning.dhdzz.cn.gov.cn.dhdzz.cn http://www.morning.qfkxj.cn.gov.cn.qfkxj.cn http://www.morning.yqkxr.cn.gov.cn.yqkxr.cn http://www.morning.wtlyr.cn.gov.cn.wtlyr.cn http://www.morning.mxgpp.cn.gov.cn.mxgpp.cn http://www.morning.ktskc.cn.gov.cn.ktskc.cn http://www.morning.rydhq.cn.gov.cn.rydhq.cn http://www.morning.fglzk.cn.gov.cn.fglzk.cn http://www.morning.rdlxh.cn.gov.cn.rdlxh.cn http://www.morning.pumali.com.gov.cn.pumali.com http://www.morning.swzpx.cn.gov.cn.swzpx.cn