#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//一、函数指针与回调函数
//函数指针
//eg1.
//int max(int x, int y)
//{
// return x > y ?x: y;
//}
//int main()
//{
// //定义指针函数
// int (*p)(int, int) = &max;
// int a, b, c, d;
// //输入
// printf("请输入三个数");
// scanf("%d %d %d", &a, &b, &c);
// //引用函数
// d = p(p(a, b), c);
// printf("最大的数为:%d", d);
// return 0;
//}
//回调函数:函数指针作为某个函数的参数
//eg2.
//void populate_array(int* array, size_t arraysize, int (*getNextValue)(void))//回调函数
//{
// for (size_t i = 0; i < arraysize; i++)
// array[i] = getNextValue();
//}
//获取随机值
//int getNextRandomValue(void)
//{
// return rand();
//}
//
//int main(void)
//{
// int myarray[10];
// populate_array(myarray, 10, getNextRandomValue);/* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
// for (int i = 0; i < 10; i++)
// {
// printf("%d\n", myarray[i]);
// }
// printf("\n");
// return 0;
//}
//二、字符串
//操作字符串的函数
/*
1.strcpy(s1,s2); 复制字符串s2到字符串s1;
2.strcat(s1,s2); 连接字符串s2到字符串s1的末尾
3.strlen(s1); 返回字符串s1的长度
....
*/
//eg3.
//int main()
//{
// char str1[14] = "runoob";
// char str2[14] = "google";
// char str3[14];
// int len;
//
// strcpy(str3, str1);//把str1复制到str3
// printf("复制后str3的值:%s\n", str3);
//
// strcat(str1, str2);//把str2连接到str1的末尾
// printf("连接后的值:%s\n", str1);
//
// len = strlen(str1);
// printf("连接后str1的长度: %d\n", len);
//
// return 0;
//}
//三、结构体
//定义结构
/*
struct tag {
member-list
member-list
member-list
...
} variable-list ;
*/
/*
tag 是结构体标签。
member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。下面是声明 Book 结构的方式:
*/
/*
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
*/
//结构体变量的初始化
// eg4.
//struct Books
//{
// char title[50];
// char author[50];
// char subject[100];
// int book_id;
//}book = {"C语言","runnoob","编程语言",123456};
//
//int main()
//{
// printf("title: %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
// return 0;
//}
访问结构成员
/*
为了访问结构的成员,我们使用成员访问运算符(.)。
成员访问运算符是结构变量名称和我们要访问的结构成员
之间的一个句号。您可以使用 struct 关键字来定义结构
类型的变量。下面的实例演示了结构的用法:*/
//eg5.
//struct Books
//{
// char title[50];
// char author[50];
// char subject[100];
// int book_id;
//};
//
//void printBook(struct Books* book);//函数声明
//int main()
//{
// //声明book1 book2
// struct Books book1;
// struct Books book2;
// //输入book1 book2 信息
// strcpy(book1.title, "C Programming");
// strcpy(book1.author, "Nuha Ali");
// strcpy(book1.subject, "C Programming Tutorial");
// book1.book_id = 9495407;
//
// strcpy(book2.title, "Telecom Billing");
// strcpy(book2.author, "Zara Ali");
// strcpy(book2.subject, "Telecom Billing Tutorial");
// book2.book_id = 6495700;
//
// //通过传book1的地址来输出book1的信息
// printBook(&book1);
// //通过传book2的地址来输出book2的信息
// printBook(&book2);
//
// return 0;
//}
//
//void printBook(struct Books *book)
//{
// printf("Book title : %s\n", book->title);
// printf("Book author: %s\n", book->author);
// printf("Book subject:%s\n", book->subject);
// printf("Book book_id: %d\n", book->book_id);
//}
//四、共用体
/*简介:共用体是一种特殊的数据类型,允许您在相同的内存位置存储
不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候
只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
*/
//定义共用体
/*
为了定义共用体,您必须使用 union 语句,方式与定义结构类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下:
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,
最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的。下面定义一个名为 Data 的共用体类型,有三个成员 i、f 和 str:
union Data
{
int i;
float f;
char str[20];
} data;
现在,Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串。这意味着一个变量(相同的内存位置)可以存储多个多种类型的数据。您
可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。
共用体占用的内存应足够存储共用体中最大的成员。例如,在上面的实例中,Data 将占用 20 个字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。
*/
//eg6:验证共用体的内存大小
//union Data
//{
// int i;
// float f;
// char str[20];
//};
//int main()
//{
// union Data data;
// printf("Memory size occupied by data: %d\n", sizeof(data));
// return 0;
//}
//访问共用体成员
//eg7.
//union Data
//{
// int i;
// float f;
// char str[20];
//};
//int main()
//{
// union Data data;
// //给共用体成员赋值
// data.i = 10;
// data.f = 220.5;
// strcpy(data.str, "C programming");
// printf("data.i : %d\n", data.i);//1919950915
// printf("data.f : %d\n", data.f);//1610612736
// printf("data.str: %s\n", data.str);//C programming
//
// return 0;
//}
/*
在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,
这也是 str 成员能够完好输出的原因。现在让我们再来看一个相同的实例,这次我们在同一时间
只使用一个变量,这也演示了使用共用体的主要目的:*/
//eg8.
//union Data
//{
// int i;
// float f;
// char str[20];
//};
//
//int main()
//{
// union Data data;
//
// data.i = 10;
// printf("data.i : %d\n", data.i);// 10
//
// data.f = 220.5;
// printf("data.f : %f\n", data.f);// 220.500000
//
// strcpy(data.str, "C Programming");
// printf("data.str : %s\n", data.str);// C Programming
//
// return 0;
//}
//在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。
//五、位域
/*
如果程序的结构中包含多个开关量,只有 TRUE/FALSE 变量,如下:
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status;
这种结构需要 8 字节的内存空间,但在实际上,在每个变量中,我们只存储 0 或 1。在这种情况下,
C 语言提供了一种更好的利用内存空间的方式。如果您在结构内使用这样的变量,您可以定义变量的宽
度来告诉编译器,您将只使用这些字节。例如,上面的结构可以重写成:
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
现在,上面的结构中,status 变量将占用 4 个字节的内存空间,但是只有 2 位被用来存储值。如果您
用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将使用 4 个字节,但只要您再多用一个变量,
如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。
*/
//eg9. 验证
定义简单结构
//struct
//{
// unsigned int widthValidated;
// unsigned int heightValidated;
//}status1;
定义位域结构
//struct
//{
// unsigned int widthValidataed : 1;
// unsigned int heighValidate : 1;
//}status2;
输出两个结构大小 比较
//int main()
//{
// printf("status1的结构大小: %d\n", sizeof(status1));
// printf("status2的结构大小: %d\n", sizeof(status2));
//
// return 0;
//}
//位域声明
/*
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,
只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结
构,称为"位域"或"位段"。
所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,
允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
典型的实例:
用1位二进位存放一个开关量时,只有 0 和 1 两种状态。
读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。
位域的定义和位域变量的说明
位域定义与结构定义相仿,其形式为:
struct 位域结构名
{
位域列表
};
其中位域列表的形式为:
type [member_name] : width ;
下面是有关位域中变量元素的描述:
元素 描述
type 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name 位域的名称。
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度。
带有预定义宽度的变量被称为位域。位域可以存储多于 1 位的数,例如,需要一个变量来存储从 0 到 7 的值,您可以定义一个宽度为 3 位的位域,如下:
struct
{
unsigned int age : 3;
} Age;
上面的结构定义指示 C 编译器,age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成。
struct bs{
int a:8;
int b:2;
int c:6;
}data;
data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。
让我们再来看一个实例:
struct packed_struct {
unsigned int f1:1;
unsigned int f2:1;
unsigned int f3:1;
unsigned int f4:1;
unsigned int type:4;
unsigned int my_int:9;
} pack;
在这里,packed_struct 包含了 6 个成员:四个 1 位的标识符 f1..f4、一个 4 位的 type 和一个 9 位的 my_int。
*/
//eg10.
//struct
//{
// unsigned int age : 3;
//}Age;
//
//int main()
//{
// Age.age = 4;
// printf("Sizeof(Age): %d\n", sizeof(Age));
// printf("Age.age: % d\n", Age.age);//4
//
// Age.age = 7;
// printf("Age.age: %d\n", Age.age);//7
//
// Age.age = 8;//二进制为1000有四位,所以会出错。
// printf("Age.age: %d\n", Age.age);//0
//
// return 0;
//}
//位域的使用
/*
位域的使用和结构成员的使用相同,其一般形式为:
位域变量名.位域名
位域变量名->位域名
位域允许用各种格式输出。
*/
//eg11.
//int main()
//{
// struct bs {
// unsigned a : 1;
// unsigned b : 3;
// unsigned c : 4;
// }bit,*pbit;
// //给位域赋值,应注意赋值不能超过该位域的允许范围
// bit.a = 1;
// bit.b = 7;
// bit.c = 15;
// printf("%d,%d,%d\n", bit.a, bit.b, bit.c);
// //把位域变量bit的地址送给指针变量pbit
// pbit = &bit;
// pbit->a = 0;//用指针方式给位域 a 重新赋值,赋为 0
// pbit->b &= 3;//使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3)
// pbit->c |= 1;//使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15
// printf("%d,%d,%d\n", pbit->a, pbit->b, pbit->c);
// return 0;
//}
//六、C Typedef : 可以用以为类型取一个新的名字
//eg12;对结构体使用 typedef 来定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义结构变量
//typedef struct Books
//{
// char title[50];
// char author[50];
// char subject[100];
// int book_id;
//}Book;
//int main()
//{
// Book book;
//
// //赋值
// strcpy(book.title, "C 教程");
// strcpy(book.author, "Runoob");
// strcpy(book.subject, "编程语言");
// book.book_id = 12345;
//
// printf("书标题: %s\n", book.title);
// printf("书作者: %s\n", book.author);
// printf("书类目: %s\n", book.subject);
// printf("书 id: %d\n", book.book_id);
//
// return 0;
//}
//typedef与#define的区别
/*
#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:
typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
*/
//七、C的输入与输出
//getchar() & putchar() 函数
/*
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
*/
//eg13:getchar()与putchar()
//int main()
//{
// int c;
//
// printf("Enter a value:");
// c = getchar();
//
// printf("\nYou entered:");
// putchar(c);
// printf("\n");
//
// return 0;
//}
//当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并只会读取一个单一的字符
//gets()与puts()函数
/*
char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。
*/
//eg14:gets()函数与puts()函数
//int main()
//{
// char str[100];
//
// printf("请输入一个字符:");
// gets(str);
//
// printf("您输入的字符:");
// puts(str);
// return 0;
//}
//当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并读取一整行直到该行结束
//scanf()和printf()函数
/*
int scanf(const char *format, ...) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。
*/
//eg15.
//int main()
//{
// char str[100];
// int i;
//
// printf("Enter a value:");
// scanf("%s %d", str, &i);
//
// printf("\nYou entered: %s %d ", str, i);
// printf("\n");
//
// return 0;
//}
/*
在这里,应当指出的是,scanf() 期待输入的格式与您给出的 %s 和 %d 相同,这意味着您必须提供有效的输入,
比如 "string integer",如果您提供的是 "string string" 或 "integer integer",它会被认为是错误的输入。
另外,在读取字符串时,只要遇到一个空格,scanf() 就会停止读取,所以 "this is test" 对 scanf() 来说是三个字符串。
*/
//八、C 文件读写 ×,,
//int main()
//{
// FILE* fp = NULL;
//
// fp = fopen("/Temp/test.txt", "w+");
// fprintf(fp, "This is testing for fprintf...\n");
// fputs("This is testing for fputs...\n", fp);
// fclose(fp);
//}
//九、C 预处理器
//预定义宏
/*
宏 描述
__DATE__ 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
__TIME__ 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
__FILE__ 这会包含当前文件名,一个字符串常量。
__LINE__ 这会包含当前行号,一个十进制常量。
__STDC__ 当编译器以 ANSI 标准编译时,则定义为 1。
*/
//eg16.
//main()
//{
// printf("File :%s\n", __FILE__);
// printf("Date :%s\n", __DATE__);
// printf("Time :%s\n", __TIME__);
// printf("Line :%d\n", __LINE__);
// printf("ANSI :%d\n", __STDC_HOSTED__);
//}
//预处理器运算符
// 宏延续运算符(\):一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)
// 字符串常量化运算符(#);在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。
//eg17.
//#define message_for(a,b) \
// printf(#a " and " #b ": we love you!\n")
//
//int main(void)
//{
// message_for(Carole, Debra);
// return 0;
//}
//eg18.
//#define tokenpaster(n) printf("token" #n " = %d",token##n)
//
//int main(void)
//{
// int token34 = 40;
//
// tokenpaster(34);
//
// return 0;
//}
//defined()运算符
//eg19.
//#if !defined (MESSAGE)
//#define MESSAGE "You wish!"
//#endif
//int main()
//{
// printf("Here is the message : %s\n", MESSAGE);
// return 0;
//}
//参数化的宏
//CPP 一个强大的功能是可以使用参数化的宏来模拟函数。
//eg20.
//#define MAX(x,y)((x)>(y)?(x):(y))
//
//int main(void)
//{
// printf("Max between 20 an 10 is %d\n", MAX(10, 20));
// return 0;
//}