c语言_基础
1.环境设置/程序结构/基本语法
2.数据类型/变量/常量
3.存储类/运算符/判断/循环/函数/作用域规则
4.数组
5.enum(枚举)/字符串
(1)enum是一种弄基本的数据类型,用于定义一组具有离散值的常量。枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
枚举语法定义格式:
enum 枚举名 {枚举元素1,枚举元素2,...};
注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
枚举变量的定义
(1)先定义枚举类型,再定义枚举变量
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN }; enum DAY day;
(2)定义枚举类型的同时定义枚举变量
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN } day;
(3)省略枚举名称,直接定义枚举变量
enum { MON=1, TUE, WED, THU, FRI, SAT, SUN } day;
例1:
#include <stdio.h> enum season {spring, summer=3, autumn, winter}; int main() { enum season sea=spring; printf("%d",sea); return 0; }
例2:
#include <stdio.h> enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN }; int main() { enum DAY day; day = WED; printf("%d",day); return 0; }
枚举是连续的是可以进行遍历的:
#include <stdio.h> enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN } day; int main() { // 遍历枚举元素 for (day = MON; day <= SUN; day++) { printf("枚举元素:%d \n", day); } }
但是不连续是不可以进行遍历的:
enum { ENUM_0, ENUM_10 = 10, ENUM_11 };
例:在switch中的应用
#include <stdio.h> #include <stdlib.h> int main() { enum color { red=1, green, blue }; enum color favorite_color; /* 用户输入数字来选择颜色 */ printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): "); scanf("%u", &favorite_color); /* 输出结果 */ switch (favorite_color) { case red: printf("你喜欢的颜色是红色"); break; case green: printf("你喜欢的颜色是绿色"); break; case blue: printf("你喜欢的颜色是蓝色"); break; default: printf("你没有选择你喜欢的颜色"); } return 0; }
例:将枚举类型转换为整数
#include <stdio.h> #include <stdlib.h> int main() { enum day { saturday, sunday, monday, tuesday, wednesday, thursday, friday } workday; int a=1; enum day weekend; weekend = ( enum day ) a; //类型转换 //weekend = a; //错误 printf("weekend:%d",weekend); return 0; }
(2)字符串
字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。
例:
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
依据数组初始化规则,您可以把上面的语句写成以下语句:
char site[] = "RUNOOB";
例:
#include<stdio.h> int main() { char site[7]={'R','U','N','O','O','B','\0'}; printf("%s",site); return 0; }
C 中有大量操作字符串的函数:
(1)strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
(2)strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
(3)strlen(s1); 返回字符串 s1 的长度。
(4)strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
(5)strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
(6)strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
#include <stdio.h> #include <string.h> int main () { char str1[14] = "runoob"; char str2[14] = "google"; char str3[14]; int len ; /* 复制 str1 到 str3 */ strcpy(str3, str1); printf("strcpy( str3, str1) : %s\n", str3 ); /* 连接 str1 和 str2 */ strcat( str1, str2); printf("strcat( str1, str2): %s\n", str1 ); /* 连接后,str1 的总长度 */ len = strlen(str1); printf("strlen(str1) : %d\n", len ); return 0; }
6.结构体
简述
在C语言中允许用户指定这样一中数据结构,他是由不同类型的数据组合成为一个整体。便于后期引用。其中不同的数据类型是相互联系的,所以这样的数据结构称为结构体。
struct 结构体名
{成员列表};
结构体名的作用就是作为结构体类型的标志(我的理解:结构类似于高级算法中的类,而结构体名就是类名)。它又称为结构体标记,大括号内的是该结构体的各个成员,共同构成一个结构体。对各成员都应该进行类型的声明;如下(结构体中的成员列表也称为域表,第一个成员也称为结构体中的一个域)
struct 结构体
{
类型名 成员名;
}
示例1:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
定义结构体类型变量的方法
在结构体中存放数据时,可以采取以下三种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名
如上面已经定义了一个结构体类型 struct student ,可以用它来定义变量。如下:
struct student //结构体类型名
{
....
....
....
}student1,student2;//结构体变量名
(2)在声明类型的同时定义变量
示例:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1,student2;
(3)直接定义结构类型变量
如下:
struct
{
成员列表
}变量名列表;
结构体变量的引用
不能将一个结构体变量作为一个整体进行输入和输出,只能对变量中的各个成员分别进行输入和输出。引用结构体变量中的成员的方式为:
结构体变量名.成员名
结构体变量的初始化
如下:
#include <stdio.h>
struct student
{
long int num;
char name[20];
char sex;
char addr[30];
}a={89031,"Li lin",'M',"123 Beijing"};
void main()
{
printf("NO.:%ld\nname: %s\nsex: %c\naddress: %s\n",a.num, a.name, a.sex, a.addr);
}
结构体数组
(1)定义结构体数组
struct student
{
int mun;
char name[];
char sex;
int age;
float score;
char addr[30];
};
struct student stu[3];
(2)结构体数组初始化
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3]={{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"}};
(3)结构体数组应用举例
指向结构体类型数据的指针
(1)指向结构体类型变量的指针
(2)指向结构体数组的指针
(3)用结构体变量和指向结构体的指针作函数参数
用指针处理链表
(1)链表概述
(2)简单链表
(3)处理动态链表所需的函数
(4)建立动态链表
7.指针/函数指针与回调函数
如动态内存分配,没有指针是无法执行的。
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。
以下是有效的指针声明:
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch; /* 一个字符型的指针 */
例:
#include <stdio.h> int main () { int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */ printf("var 变量的地址: %p\n", &var ); /* 在指针变量中存储的地址 */ printf("ip 变量存储的地址: %p\n", ip ); /* 使用指针访问值 */ printf("*ip 变量的值: %d\n", *ip ); return 0; }
空指针:
#include <stdio.h> int main () { int *ptr = NULL; printf("ptr 的地址是 %p\n", ptr ); return 0; }
8.共用体
9.位域/typedef/输入输出
10.文件读写/预处理器/头文件
11.强制类型转换/错误处理
强制类型转换是把变量从一种类型转换为另一种数据类型。例如,如果您想存储一个 long 类型的值到一个简单的整型中,您需要把 long 类型强制转换为 int 类型。您可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型
(1)强制类型转换
例:
#include <stdio.h> int main() { int sum = 17, count = 5; double mean; mean = (double) sum / count; printf("Value of mean : %f\n", mean ); }
例:整数提升
编译器进行了整数提升,在执行实际加法运算时,把 'c' 的值转换为对应的 ascii 值。
#include <stdio.h> int main() { int i = 17; char c = 'c'; /* ascii 值是 99 */ int sum; sum = i + c; printf("Value of sum : %d\n", sum ); }
例:
#include <stdio.h> int main() { int i = 17; char c = 'c'; /* ascii 值是 99 */ float sum; sum = i + c; printf("Value of sum : %f\n", sum ); }
在这里,c 首先被转换为整数,但是由于最后的值是 float 型的,所以会应用常用的算术转换,编译器会把 i 和 c 转换为浮点型,并把它们相加得到一个浮点数。
(2)错误处理
C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息。
- perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
- strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
例:没有该文件
#include <stdio.h> #include <errno.h> #include <string.h> extern int errno ; int main () { FILE * pf; int errnum; pf = fopen ("unexist.txt", "rb"); if (pf == NULL) { errnum = errno; fprintf(stderr, "错误号: %d\n", errno); perror("通过 perror 输出错误"); fprintf(stderr, "打开文件错误: %s\n", strerror( errnum )); } else { fclose (pf); } return 0; }
例:被0除的错误
#include <stdio.h> #include <stdlib.h> int main() { int dividend = 20; int divisor = 0; int quotient; if( divisor == 0){ fprintf(stderr, "除数为 0 退出运行...\n"); exit(-1); } quotient = dividend / divisor; fprintf(stderr, "quotient 变量的值为 : %d\n", quotient ); exit(0); }
程序退出状态:
#include <stdio.h> #include <stdlib.h> main() { int dividend = 20; int divisor = 5; int quotient; if( divisor == 0){ fprintf(stderr, "除数为 0 退出运行...\n"); exit(EXIT_FAILURE); } quotient = dividend / divisor; fprintf(stderr, "quotient 变量的值为: %d\n", quotient ); exit(EXIT_SUCCESS); }
12.递归/可变参数
(1)递归指的是在函数的定义中使用函数自身的方法。
void recursion() { statements; ... ... ... recursion(); /* 函数调用自身 */ ... ... ... } int main() { recursion(); }
递归(阶乘/斐波那契数列)
例:阶乘
#include <stdio.h> double factorial(unsigned int i) { if(i <= 1) { return 1; } return i * factorial(i - 1); } int main() { int i = 15; printf("%d 的阶乘为 %f\n", i, factorial(i)); return 0; }
例:斐波那契
#include <stdio.h> int fibonaci(int i) { if(i == 0) { return 0; } if(i == 1) { return 1; } return fibonaci(i-1) + fibonaci(i-2); } int main() { int i; for (i = 0; i < 10; i++) { printf("%d\t\n", fibonaci(i)); } return 0; }
(2)可变参数
省略号 ... 表示可变参数列表
int func(int, ... ) { . . . } int main() { func(2, 2, 3); func(3, 2, 3, 4); }
请注意,函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
- 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
- 使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
- 使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
- 使用宏 va_end() 来清理赋予 va_list 变量的内存。
例:
#include <stdio.h> #include <stdarg.h> double average(int num,...) { va_list valist; double sum = 0.0; int i; /* 为 num 个参数初始化 valist */ va_start(valist, num); /* 访问所有赋给 valist 的参数 */ for (i = 0; i < num; i++) { sum += va_arg(valist, int); } /* 清理为 valist 保留的内存 */ va_end(valist); return sum/num; } int main() { printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5)); printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15)); }
13.内存管理
C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。
在 C 语言中,内存是通过指针变量来管理的。指针是一个变量,它存储了一个内存地址,这个内存地址可以指向任何数据类型的变量,包括整数、浮点数、字符和数组等。C 语言提供了一些函数和运算符,使得程序员可以对内存进行操作,包括分配、释放、移动和复制等。
(1)void *calloc(int num, int size);
在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。
(2)void free(void *address);
该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
(3)void *malloc(int num);
在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
(4)void *realloc(void *address, int newsize);
该函数重新分配内存,把内存扩展到 newsize。
注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
动态内存分配
知道大小的前提下:
如果您预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳 100 个字符,所以您可以定义数组,如下所示:
char name[100];
如果您预先不知道需要存储的文本长度,例如您想存储有关一个主题的详细描述。在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *description; strcpy(name, "Zara Ali"); /* 动态分配内存 */ description = (char *)malloc( 200 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student in class 10th"); } printf("Name = %s\n", name ); printf("Description: %s\n", description ); }
程序也可以使用 calloc() 来编写,只需要把 malloc 替换为 calloc 即可,如下所示:
calloc(200, sizeof(char));
当动态分配内存时,您有完全控制权,可以传递任何大小的值。而那些预先定义了大小的数组,一旦定义则无法改变大小。
重新调整内存的大小和释放内存
当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数 free() 来释放内存。
或者,您可以通过调用函数 realloc() 来增加或减少已分配的内存块的大小。让我们使用 realloc() 和 free() 函数,再次查看上面的实例:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *description; strcpy(name, "Zara Ali"); /* 动态分配内存 */ description = (char *)malloc( 30 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student."); } /* 假设您想要存储更大的描述信息 */ description = (char *) realloc( description, 100 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcat( description, "She is in class 10th"); } printf("Name = %s\n", name ); printf("Description: %s\n", description ); /* 使用 free() 函数释放内存 */ free(description); }
C 语言中常用的内存管理函数和运算符
-
malloc() 函数:用于动态分配内存。它接受一个参数,即需要分配的内存大小(以字节为单位),并返回一个指向分配内存的指针。
-
free() 函数:用于释放先前分配的内存。它接受一个指向要释放内存的指针作为参数,并将该内存标记为未使用状态。
-
calloc() 函数:用于动态分配内存,并将其初始化为零。它接受两个参数,即需要分配的内存块数和每个内存块的大小(以字节为单位),并返回一个指向分配内存的指针。
-
realloc() 函数:用于重新分配内存。它接受两个参数,即一个先前分配的指针和一个新的内存大小,然后尝试重新调整先前分配的内存块的大小。如果调整成功,它将返回一个指向重新分配内存的指针,否则返回一个空指针。
-
sizeof 运算符:用于获取数据类型或变量的大小(以字节为单位)。
-
指针运算符:用于获取指针所指向的内存地址或变量的值。
-
& 运算符:用于获取变量的内存地址。
-
* 运算符:用于获取指针所指向的变量的值。
-
-> 运算符:用于指针访问结构体成员,语法为 pointer->member,等价于 (*pointer).member。
-
memcpy() 函数:用于从源内存区域复制数据到目标内存区域。它接受三个参数,即目标内存区域的指针、源内存区域的指针和要复制的数据大小(以字节为单位)。
-
memmove() 函数:类似于 memcpy() 函数,但它可以处理重叠的内存区域。它接受三个参数,即目标内存区域的指针、源内存区域的指针和要复制的数据大小(以字节为单位)。
14.命令行参数