1、C语言学习
C语言是一门面向过程、抽象化的通用程序设计语言,广泛应用于底层开发。
0、头文件、源文件
.h文件为头文件,.c则为源文件
头文件可以包含进入源文件,这样就可以在源文件中调用头文件里面所定义的函数和变量了,可将源文件制作成头文件,方法就是将扩展名改成.h就可以了.
1)#include <string.h> 包含字符串处理函数的头文件,如常用的 :strlen strcpy strcmp strcat 等
2)#include<math.h> 包含math库,可以使用里面的各种数学函数,如幂函数、三角函数、指数函数等
3)#include <stdio.h> 加入了标准输入输出头文件,例如scanf,printf等
4)#include <stdint.h> 定义了int16_t 、 uint32_t 、 int64_t 等整型,在需要确定大小的整型时可以使用它们代替 short 、 unsigned long long 等
5)#include <assert.h> 其作用是提供assert宏定义,如果它的条件返回错误,则终止程序执行.
注:
C 语言中 include <> 与include "" 的区别?
#include < > 引用的是编译器的类库路径里面的头文件。
#include " " 引用的是你程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的类库路径的目录下找该头文件。
1、程序结构
C 程序主要包括以下部分:
预处理器指令、函数、变量、语句 & 表达式、注释。
例:
#include <stdio.h> //预处理器指令 int main() //主函数 { /* 我的第一个程序*/ //块注释 printf("Hello,World! \n"); //语句 & 表达式 return 0; //返回值 0 }
2、数据类型
整数类型、浮点类型、枚举类型等
整数类型:

浮点类型:

3、变量 与 常量
int i, j, k; // 变量定义
char c, ch;
float f = 0.1;
double d;
const int LENGTH = 314159E-5 ; //定义常量
例:
#include <stdio.h> //预处理器指令 #define LENGTH 9 //定义常量 static int x; //定义全局变量,当前文件 extern int y; //定义全局变量,所有文件 int addtwonum() { x = 1; // 赋值 y = 1; return x + y; } int main() //主函数 { int result; result = addtwonum()+LENGTH; //调用函数 printf("result = %d \n", result); return 0; }
4、运算符
算术运算符:
+ 加; - 减; * 乘; / 除; % 取余数; ++ 自增; -- 自减
注a++ 与 ++a区别(减法类似):
a = 10; c = a++; // 则c = 10; a = 11;
a = 10; c = ++a; // 则c = 11; a = 11;
关系运算符:
== 等于; != 不等于; > 大于; < 小于; >= 大于等于; <= 小于等于
逻辑运算符(判断语句):
&& 与 (全1为1;其余为0); || 或 (全0为0;其余为1);
! 非 (相异为1,相同为0)
位运算符:
& 与 (全1为1;其余为0); | 或 (全0为0;其余为1);
^ 非 (相异为1,相同为0)
赋值运算符:
C += A 相当于 C = C + A (加); C -= A 相当于 C = C – A (减)
C *= A 相当于 C = C * A (乘); C /= A 相当于 C = C / A (除)
C <<= 2 等同于 C = C << 2 (左移); C >>= 2 等同于 C = C >> 2(右移); 等等
5、判断语句
注:C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false。
主要语法:
If; if...else; if...else if...else;嵌套if语句;switch语句; 嵌套switch语句。
switch语句一般形式:
switch(表达式)
{
case 常量表达式1:语句1;
case 常量表达式2:语句2;
...
default:语句n+1;
}
意思是先计算表达式的值,再逐个和 case 后的常量表达式比较,若不等则继续往下比较,若一直不等,则执行 default 后的语句;若等于某一个常量表达式,则从这个表达式后的语句开始执行,并执行后面所有 case 后的语句。在每一 case 语句之后增加 break 语句,使每一次执行之后均可跳出 switch 语句,从而避免输出不应有的结果。
例:
#include <stdio.h> int main() { int a = 0; int b = 1; if (a || b ) { printf("1\n"); } else { printf("0\n"); } return 0; }
例:
#include <stdio.h> int main() { char grade = 'B'; //定义局部变量 switch(grade) { case 'A': printf("A \n"); break; case 'B': case 'C': printf("做得好 \n"); break; default: printf("无效的成绩 \n"); } printf("您的成绩是:%c \n",grade); return 0; }
输出结果:
做得好
您的成绩是 B
6、循环语句
主要语法:
while 循环; do...while 循环;for 循环;嵌套循环
循环控制语句:
break 语句;continue 语句;
1)while 循环
例:
#include <stdio.h> int main() { int a = 1; //定义局部变量 while(a < 5) { printf("a的值:%d \n",a); a++; if(a >3) { break; //使用 break 语句终止循环 } } return 0; }
输出结果:
a 的值: 1
a 的值: 2
a 的值: 3
2)do...while 循环
注:continue 会跳过当前循环中的代码,强迫开始下一次循环。
例:
#include <stdio.h> int main() { int a = 1; //定义局部变量 do { if(a == 3) { a = a+1; continue; //使用 continue 语句继续循环 } printf("a的值:%d \n",a); a++; } while(a<5); return 0; }
输出结果:
a 的值: 1
a 的值: 2
a 的值: 4
3)for 循环;嵌套循环
注:for( ; ; ) 为无限循环
#include <stdio.h> int main() { int i,j; //定义局部变量 for(i=1;i<=3;i++) { for(j=1;j<=i;j++) { printf("%d ",j); } printf("\n"); } return 0; }
输出结果:
1
1 2
1 2 3
7、函数
函数包含:返回类型、函数名称、参数、函数主体
变量的作用域规则:
局部变量:某个函数或块的内部声明的变量,只能被该函数或该代码块内部的语句使用。局部变量在函数外部是不可知的,保存在栈中。
全局变量:定义在函数外部,通常是在程序的顶部,全局变量在任意的函数内部都能访问,保存在内存的全局存储区中。
形式参数:被当作该函数内的局部变量,如果与全局变量同名它们会优先使用。
注:无返回如:void myfunction(){ }
例:
#include <stdio.h> int add_f(int num1, int num2); //函数声明 int b = 0; //全局变量声明 int main() { int a = 4; //定义局部变量 int b = 2; int w; w = add_f(a,b); printf("值为: %d\n",w); return 0; } int add_f(int num1, int num2) // 定义形参 { int result; //局部变量声明 result = num1 + num2; return result; }
输出结果:
值为: 6
8、数组
包括:
声明数组:double balance[10];
初始化数组:double balance[5] = {1.0, 2.0, 3.4, 7.0, 50.0};
注:所有的数组都是以 0 作为它们第一个元素的索引。
一维数组:int a[3] = {0,3,6};
计算一维数组的长度:int len = sizeof(a) / sizeof(a[0]);
多维数组:int a[3][4] = { {0, 1, 2, 3} , {4, 5, 6, 7} , {8, 9, 10, 11} }; //3行4列数组
计算二维数组的维度:
行数:int rows=sizeof(ar)/sizeof(ar[0]);
列数:int cols=sizeof(ar[1])/sizeof(ar[0][0]);
数组与指针:
例:
#include <stdio.h> int main () { int n[3] = {1,2,3}; //初始化数组元素,n 为包含3个整数的数组 int *p; int i,j; p = n; //将数组初地址赋值给指针 printf("使用 n 作为地址的数组值: \n"); for(i=0;i<3;i++) //初始化数组元素 { printf("n1 = %d \n",n[i]); printf("n2 = %d \n",*(n+i)); } printf("使用 p 指针作为地址的数组值: \n"); for(j=0;j<3;j++) //输出数组中每个元素 { printf("p =%d \n",*(p+j)); } return 0; }
输出结果:
均输出:1,2,3
9、枚举
枚举可以让数据更简洁,更易读。
格式:enum 枚举名 {枚举元素1,枚举元素2,……};
枚举变量的定义:
方法1:先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day; // 用于主函数中
注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
MON为1,TUE为2,以此类推。
方法2:定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
例:
#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); } day = TUE; printf("%d \n",day); return 0; }
输出结果:
1,2,3,4,5,6,7,2
例:
#include <stdio.h> #include <stdlib.h> enum color { red=1,green,blue }; int main () { enum color m_color; printf("请输入颜色(如1:红色): \n"); scanf("%d",&m_color); switch(m_color) { case red: printf("1 \n"); break; case green: printf("2 \n"); break; case blue: printf("3 \n"); break; default: printf("请重来 \n"); } return 0; }
10、指针
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,如:int a=1; printf("a 变量的地址: %p\n", &a);
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址,使用时需声明。如:
int *ip; /* 声明一个整型的指针 */
double *dp; /* 声明一个 double 型的指针 */
float *fp; /* 声明一个浮点型的指针 */
char *ch; /* 声明一个字符型的指针 */
主要操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。
注:*ip,ip为地址,*ip为该地址对应的数值。
例:
#include <stdio.h> int main () { int a=1; //声明实际变量 int *ip; //声明指针变量 ip = &a; //在指针变量中存储 a 的地址 printf("a变量的地址: %p\n", &a); printf("*ip变量的地址:%p\n", ip); printf("*ip变量的值: %d\n", *ip); return 0; }
输出:
a变量的地址:0028FF18
*ip变量的地址: 0028FF18
*ip变量的值: 1
注:在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯:int *ip = NULL; //声明指针变量为空指针
注:指针要指向一个合法的地址才可以赋值。
int *p; //p为指针,地址是未知的,需要注意初始化指定地址
可以由以下两种方法:
int a=3;
1) int *p = &a; 则后面语句中,*p 为具体值(可赋值 *p=2;),p为地址(不可随意更改,可引用,p=2为错误语句)
2) int *p; p = &a; 则后面语句中,*p 为具体值(可赋值 *p=2;),p为地址(不可随意更改,可引用,p=2为错误语句)
3) 若a为数组,则更改为:int *p = a; 或 int *p; p = a;
指针的算术运算:
可以对指针进行四种算术运算:++、--、+、-、==、<、>
例:
#include <stdio.h> const int MAX = 2; //声明常量 int main () { int a[]={1,2}; //声明实际变量 int i,*p; //声明指针变量 p = &a; for(i=0;i<MAX;i++) { printf("存储地址:a[%d]=%p\n",i, p); //打印地址 printf("存储值:a[%d]=%d\n", i,*p); //打印值 p++; //指针移动到下一个位置 } return 0; }
输出:
存储地址:a[0]= 0028FF10
存储值:a[0]=1
存储地址:a[1]= 0028FF14
存储值:a[1]=2
或:
int i=0; while(p<=&a[MAX-1]) { p++; //指针移动到下一个位置 i++; }
指针数组:
用来存储数组的数组指针。
例:
#include <stdio.h> const int MAX = 2; int main () { int a[]={1,2}; //声明实际变量 int i,*p[MAX]; //声明指针变量 for(i=0;i<MAX;i++) { p[i]=&a[i]; //在指针变量中存储 a 的地址 } for(i=0;i<MAX;i++) { printf("a[%d]=%d\n",i,*p[i]); } return 0; }
输出:
a[0]=1
a[1]=2
或(比较):
#include <stdio.h> const int MAX = 2; int main () { int *a[]={1,2}; //声明指针数组变量 for(i=0;i<MAX;i++) { printf("a[%d]=%d\n",i,a[i]); } return 0; }
例:
#include <stdio.h> const int MAX = 2; int main () { char *a[]={"zhao","qian"}; //声明指针数组变量 int i; for(i=0;i<MAX;i++) { printf("a[%d]=%s\n",i,a[i]); } return 0; }
输出:
a[0]= zhao
a[1]= qian
指向指针的指针:
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。
通常,一个指针包含一个变量的地址。
其中,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
一个指向指针的指针变量的声明:
如:int **var;
例:
#include <stdio.h> int main () { int a=1; //声明变量 int *p; //定义指针 int **pp; //定义指向指针的指针 p = &a; //获取 a 的地址 pp = &p; //使用运算符 & 获取 p 的地址 printf("%d\n",a); printf("%d\n",*p); printf("%d\n",**pp); return 0; }
输出:
1;1;1
传递指针给函数:
C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。
注:能接受指针作为参数的函数,也能接受数组作为参数。
例:
#include <stdio.h> double getAverage(int *arr,int size); //函数声明 int main () { int a[5]={1,2,3,4,5}; //声明变量 int b; b = getAverage(a,5); //传递一个数组作为指针的参数 printf("%d\n",b); return 0; } double getAverage(int *arr,int size) //定义函数 { int i,sum = 0; for(i=0;i<size;i++) { sum += arr[i]; } return sum; }
从函数返回指针:
C 允许您从函数返回指针,不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。
必须声明一个返回指针的函数,如下所示:
int * myFunction( )
{
……
}
例:
#include <stdio.h> int *getValue(); //函数声明 int main () { int *p; //声明指针变量 int i; p = getValue(); //调用函数,返回地址 for(i=0;i<3;i++) { printf("%d\n",*(p+i)); } return 0; } int *getValue() //定义函数 { static int a[]= {1,2,3}; //必须要有static return a; }
输出:
1;2;3
函数指针:
函数指针是指向函数的指针变量,函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针变量的声明:
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
例:
#include <stdio.h> int max(int x,int y); //函数声明 int main () { int (*p)(int, int) = & max; //声明函数指针变量, &可以省略 int a = 1,b = 2, c = 3, d; d = p(p(a,b),c); //与直接调用函数等价,d = max(max(a, b), c) printf("%d\n",d); return 0; } int max(int x,int y) { return x>y?x:y; }
回调函数:
函数指针作为某个函数的参数。
例:
#include <stdlib.h> #include <stdio.h> // 回调函数 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); for(int i = 0; i < 10; i++) { printf("%d ", myarray[i]); } printf("\n"); return 0; }
11、字符串
在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组,编译器自动把 '\0' 放在字符串的末尾。
如:
char greeting[] = "Hello";
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\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> int main () { char A[6]="hello"; char B[6]="world"; char C[12]; int len; strcpy(C,A); //复制字符串A到C printf("%s\n",C); strcat(A,B); //连接字符串A和B printf("%s\n",A); len = strlen(A); //计算字符串A的长度 printf("%d\n",len); return 0; }
结果:
Hello; HelloWorld ;10
12、结构体
C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。
定义结构:
1)结构体的标签被命名为SIMPLE
struct SIMPLE // 声明结构体
{
int a; //标准的变量定义
char b;
double c;
};
struct SIMPLE simple1; //声明simple1,类型为结构体SIMPLE
2) 结构体的标签被命名为SIMPLE
struct SIMPLE // 声明结构体
{
int a; //标准的变量定义
char b;
double c;
} simple1, simple2; //结构变量,可以指定一个或多个结构变量
例:
#include <stdio.h> struct Book // 声明结构体 { char title[50]; char author[50]; int book_id; }book = {"C语言", "TOM", 12}; int main () { printf("title:%s,author:%s,book_id:%d\n",book.title,book.author,book.book_id); return 0; }
输出:
title : C 语言,author: TOM,book_id: 12
结构作为函数参数:
可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。
例:
#include <stdio.h> #include <string.h> struct Book // 声明结构体 { char title[50]; char author[50]; int book_id; }; void printBook( struct Book book ); //函数声明 int main () { struct Book book; //声明 book,类型为Book结构体 strcpy( book.title,"C语言"); strcpy( book.author,"TOM"); book.book_id=12; printBook(book); //打印 return 0; } void printBook( struct Book book ) //结构体作为函数参数 { printf("title:%s,author:%s,book_id:%d\n",book.title,book.author,book.book_id); }
输出:
title : C 语言,author: TOM,book_id: 12
指向结构的指针:
可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似
如:
struct Books *struct_pointer; //结构体指针定义
struct_pointer = &Book1; //获取结构体的地址
struct_pointer->title; //结构体指针访问结构的成员,必须使用 -> 运算符
例:
#include <stdio.h> #include <string.h> struct Book // 声明结构体 { char title[50]; char author[50]; int book_id; }; void printBook( struct Book *book ); //函数声明 int main () { struct Book book1; //声明 book,类型为Book结构体 strcpy( book1.title,"C语言"); strcpy( book1.author,"TOM"); book1.book_id=12; printBook(&book1); //通过传 book1 的地址来输出信息 return 0; } void printBook( struct Book *book ) //结构体作为函数参数 { printf("title:%s,author:%s,book_id:%d\n",book->title,book->author,book->book_id); }
输出:
title : C 语言,author: TOM,book_id: 12
13、共用体
共用体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。注意:同一时间只用到一个成员。
如:
union Data
{
int i;
float f;
char str[20];
} data;
例:
#include <stdio.h> #include <string.h> union Data // 声明共用体 { int i; float j; char str[5]; }; int main () { union Data data; //声明 book,类型为Book结构体 data.i = 1; printf( "data.i : %d\n", data.i); //注意:同一时间只用到一个成员。 data.j = 1.1; printf( "data.j : %f\n", data.j); //注意:同一时间只用到一个成员。 strcpy(data.str,"Hello"); printf( "data.str : %s\n", data.str); //注意:同一时间只用到一个成员。 return 0; }
输出:
data.i : 10
data.f : 220.500000
data.str : C Programming
14、typedef 和 # define
1)C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。
如:
typedef unsigned char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:
BYTE b1, b2;
2)#define 是 C 指令,用于为各种数据类型定义别名。
如:
#define TRUE 1 //注:无分号;
#define FALSE 0
15、输入 & 输出
1)printf() 和 scanf()
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。
scanf() 函数用于从标准输入(键盘)读取并格式化;
printf() 函数发送格式化输出到标准输出(屏幕)。
例:
#include <stdio.h> int main() { float f; printf("请输入一个浮点数: "); scanf("%f",&f); //输入 printf("Value = %f", f); //打印 return 0; }
2)getchar() 和 putchar()
getchar(void) 函数从屏幕读取一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。
putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。
例:
#include <stdio.h> int main() { int c; printf("其请输入一个字符:"); c = getchar(); putchar(c); printf("\n"); return 0; }
3)gets() 和 puts()
gets(s) 函数从屏幕读取一行字符串s
puts(s) 函数把字符串s打印出来。
例:
#include <stdio.h> int main() { char str[100]; printf("其请输入一串字符:"); gets(str); puts(str); printf("\n"); return 0; }
16、文件读写
创建、打开、关闭文本文件或二进制文件。
1)打开文件、关闭文件、写入文件、读取文件
r 打开一个已有的文本文件,允许读取文件。
w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
注:+ 表示书写均可。
例:
#include <stdio.h> int main() { FILE *fp = NULL; fp = fopen("tmp/test.txt","w+"); //打开文件 fprintf(fp,"Hello\n"); //向文件写入Hello fputs("World!\n",fp); //向文件写入World! fclose(fp); //关闭文件 return 0; }
例:
#include <stdio.h> int main() { FILE *fp = NULL; char A[255], B255]; fp = fopen("tmp/test.txt","r"); //打开文件 fscanf(fp,"%s",A); //读取文件中的字符串直到空格到A中 printf("1: %s\n", A); fgets(B,255,(FILE*)fp); //读取文件中的下一个字符串直到空格到B中 printf("2: %s\n", B); fclose(fp); //关闭文件 return 0; }
输出:
1:Hello 2:World!
注:二进制读写用如下两个函数:
fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
17、递归
递归指的是在函数的定义中使用函数自身的方法。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。
注:使用递归是要付出代价的。与直接的语句(如while循环)相比,递归函数会耗费更多的运行时间,并且要占用大量的栈空间。
例:
#include <stdio.h> int factorial(unsigned int i) { if(i <= 1) { return 1; // 返回1,退出程序 } return i*factorial(i-1); } int main() { int i = 5; printf("乘积为:%d\n", factorial(i)); return 0; }
结果:
乘积为:120
参考文献
[1] https://www.runoob.com/cprogramming/c-tutorial.html
附件
运行环境安装: https://blog.csdn.net/y_universe/article/details/78151998

浙公网安备 33010602011771号