一 基础语法 1.1 整形
类型
存储大小
值范围
char
1 字节
-128 到 127 或 0 到 255
unsigned char
1 字节
0 到 255
signed char
1 字节
-128 到 127
int
2 或 4 字节
-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int
2 或 4 字节
0 到 65,535 或 0 到 4,294,967,295
short
2 字节
-32,768 到 32,767
unsigned short
2 字节
0 到 65,535
long
4 字节
-2,147,483,648 到 2,147,483,647
unsigned long
4 字节
0 到 4,294,967,295
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main () { char i=99 ; printf ("%d\n" ,i); printf ("%c\n" ,i); unsigned char i1=99 ; printf ("%d\n" ,i1); printf ("%c\n" ,i1); unsigned short i1=65535 ; long i3=65536 ; printf ("%d" ,i3); int i9=9999 ; printf ("%d\n" ,sizeof (i9)); printf ("%d\n" ,sizeof (int )); return 0 ; }
1.2 浮点型
类型
存储大小
值范围
精度
float
4 字节
1.2E-38 到 3.4E+38
6 位有效位
double
8 字节
2.3E-308 到 1.7E+308
15 位有效位
long double
16 字节
3.4E-4932 到 1.1E+4932
19 位有效位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int main () { printf ("float 存储最大字节数 : %d \n" , sizeof (float )); printf ("double 存储最大字节数 : %d \n" , sizeof (double )); printf ("long double 存储最大字节数 : %d \n" , sizeof (long double )); float f1=365.123456789F ; printf ("%f---%e\n" ,f1,f1); double f2=365.123456789 ; printf ("%f---%e\n" ,f2,f2); long double f3=365.123456789L ; printf ("%Lf---%Le\n" ,f3,f3); return 0 ; }
1.3 常量 1 2 3 4 5 6 7 #include <stdio.h> int main () { const int MAX_VALUE =99 ; printf ("%d\n" ,MAX_VALUE ); return 0 ; }
1.4 运算符
A 的值为 10,变量 B 的值为 20
运算符
描述
实例
+
把两个操作数相加
A + B 将得到 30
-
从第一个操作数中减去第二个操作数
A - B 将得到 -10
*
把两个操作数相乘
A * B 将得到 200
/
分子除以分母
B / A 将得到 2
%
取模运算符,整除后的余数
B % A 将得到 0
++
自增运算符,整数值增加 1
A++ 将得到 11
–
自减运算符,整数值减少 1
A– 将得到 9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main () { int a = 10 ; int b = 20 ; int c; c = a + b; printf ("a+b结果是 %d\n" , c); c = a - b; printf ("a-b结果是 %d\n" , c); c = a * b; printf ("a * b 结果是%d\n" , c); c = b / a; printf ("b / a的值是 %d\n" , c); c = 10 % 3 ; printf ("10 % 3取整除的值是 %d\n" , c); c = a++; printf ("赋值后再加 的值是 %d,a的值为:%d\n" , c, a); c = a--; printf ("赋值后再减 1的值是 %d,a的值为:%d\n" , c, a); return 0 ; }
运算符
描述
==
检查两个操作数的值是否相等,如果相等则条件为真。
!=
检查两个操作数的值是否相等,如果不相等则条件为真。
>
检查左操作数的值是否大于右操作数的值,如果是则条件为真。
<
检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>=
检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<=
检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。
运算符
描述
&&
称为逻辑与运算符。如果两个操作数都非零,则条件为真。
||
称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。
!
称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> #include <stdbool.h> int main () { bool isTrue = true ; int a = 10 ; int b = 20 ; if (a && b) { printf ("条件为真\n" ); } return 0 ; }
运算符
描述
=
简单的赋值运算符,把右边操作数的值赋给左边操作数
+=
加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数
-=
减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数
*=
乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数
/=
除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数
%=
求模且赋值运算符,求两个操作数的模赋值给左边操作数
<<=
左移且赋值运算符
>>=
右移且赋值运算符
&=
按位与且赋值运算符
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { int a = 21 ; int c; c += a; printf ("%d\n" , c); c *= a; printf ("%d\n" , c); return 0 ; }
运算符
描述
实例
sizeof()
返回变量的大小。
sizeof(a) 将返回 4,其中 a 是整数。
&
返回变量的地址。
&a; 将给出变量的实际地址。
*
指向一个变量。
*a; 将指向一个变量。
? :
条件表达式
如果条件为真 ? 则值为 X : 否则值为 Y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdbool.h> int main () { int a = 4 ; printf ("%d\n" , sizeof (a)); int *ptr = &a; printf ("a 的值是 %d,ptr的值是%p\n" , a, ptr); a = 10 ; int b = (a == 1 ) ? 20 : 30 ; printf ("b 的值是 %d\n" , b); return 0 ; }
1.5 if 判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <stdbool.h> int main () { int num; printf ("输入一个数字 : " ); scanf ("%d" , &num); if (num > 90 ) { printf ("优秀" ); } else if (num > 60 && num < 90 ) { printf ("及格" ); } else { printf ("不及格" ); } return 0 ; }
1.6 循环
循环类型
描述
while 循环
当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环
多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环
除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <stdio.h> #include <stdbool.h> int main () { int num = 0 ; for (; ; ) { printf ("我是死循环" ); }; return 0 ; }
1.7 函数 1 2 3 4 返回值类型 函数名( 参数类型 形参 ) { 函数体; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int add (int a, int b) { return a + b; } int main () { int a = add(10 , 10 ); printf ("%d" , a); return 0 ; }
1.8 字符和字符串 在C语言中没有字符串。
用 字符数组 创造出字符串出来(每个字符占1个字节)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> int main () { char a = 'a' ; printf ("%c\n" , a); char s[]="justin" ; char s1[]= {'j' ,'u' ,'s' ,'t' ,'i' ,'n' ,'\0' }; printf ("%s\n" , s); printf ("%s\n" , s1); return 0 ; }
1.9 数组 对于数组 来说,内部元素是挨个存放,内存地址相邻。
它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,是一系列相同类型的变量
1 2 char v2[]= {'j' ,'u' ,'s' ,'t' ,'i' ,'n' ,'\0' };int v3[3 ] = {11 ,22 ,33 };
元素固定
类型固定(每个元素在内存中占据长度相同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> int main () { char v3[] = "justin" ; printf ("第0个位置值:%c,内存地址:%p \n" , v3[0 ], &v3[0 ]); printf ("第1个位置值:%c,内存地址:%p \n" , v3[1 ], &v3[1 ]); printf ("第2个位置值:%c,内存地址:%p \n" , v3[2 ], &v3[2 ]); int a[]={1 ,2 ,3 }; printf ("%d\n" ,a[0 ]); printf ("%d\n" ,a[1 ]); printf ("%d\n" ,a[2 ]); printf ("%d" ,sizeof (a) / sizeof (a[0 ])); return 0 ; }
1 2 3 4 5 6 7 8 9 # include <stdio.h> int main () { int v3[] = {11 , 22 , 33 , 44 , 55 , 66 }; printf ("第0个位置值:%d,内存地址:%p \n" , v3[0 ], &v3[0 ]); printf ("第1个位置值:%d,内存地址:%p \n" , v3[1 ], &v3[1 ]); printf ("第2个位置值:%d,内存地址:%p \n" , v3[2 ], &v3[2 ]); return 0 ; }
二 指针 指针是存储变量内存地址的变量
2.1 定义指针类型变量和取变量地址 1 2 3 4 5 6 7 8 #include <stdio.h> int main () { int v1 = 666 ; int *v2 = &v1; printf ("v2的值为:%p" , v2); return 0 ; }
2.2 指针类型变量解引用 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { int v1 = 666 ; int *v2 = &v1; printf ("v2的值为:%p\n" , v2); printf ("v2解引用后:%d\n" ,*v2); return 0 ; }
2.3 修改变量的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { int v1 = 666 ; int *v2 = &v1; printf ("v2的值为:%p\n" , v2); printf ("v2解引用后:%d\n" ,*v2); v1 = 999 ; printf ("v1的值为:%p\n" , v1); printf ("v2的值为:%p\n" , v2); printf ("v2解引用后:%d\n" ,*v2); return 0 ; }
2.4 指针的零值和长度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int main () { long *v2 = NULL ; printf ("%p\n" , v2); printf ("%d\n" , sizeof (v2)); if (!v2) { printf ("指针为空" ); } return 0 ; }
2.5 通过指针修改原变量值 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int a=10 ; int *v2 = &a; *v2=99 ; printf ("a的值为:%d\n" ,a); printf ("v2的值为:%p\n" ,v2); return 0 ; }
2.6 指针类型参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> void changPointA (int *p) { *p = 99 ; } void changA (int a) { a = 99 ; } int main () { int a = 10 ; int *v2 = &a; printf ("v2的值为:%p\n" , v2); changA(a); printf ("v2的值为:%p\n" , v2); printf ("a的值为:%d\n" , a); return 0 ; }
2.7 数组的指针(指针运算) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 int main() { int a[3 ] = {11 , 22 , 33 }; int *p = &a; printf("数组的指针值为:%p\n" , p); printf("取数组第0个元素方式一:%d\n" ,a[0 ]); printf("取数组第0个元素方式二:%d\n" ,*p); printf("取数组第1个元素方式一:%d\n" ,a[1 ]); printf("取数组第1个元素方式二:%d\n" ,*(++p)); return 0 ; } // 指针数组和数组指针 int main() { int a[3 ] = {11 , 22 , 33 }; int *p = &a; printf("数组的指针:%p\n" , p); int i = 10 ; int x = 20 ; int y = 30 ; int *p1[] = {&i, &x, &y}; printf("指针数组:%d" , **p1); return 0 ; }
2.8 指针的指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int main () { int a = 100 ; int *p1 = &a; int **p2 = &p1; int ***p3 = &p2; printf ("p3的值为:%p\n" , p3); printf ("p2的值为:%p\n" , *p3); printf ("p1的值为:%p\n" , **p3); printf ("a的值为:%d\n" , ***p3); return 0 ; }
2.9 字符串案例 字符串格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main () { char a[6 ]; char *p = &a; sprintf (p, "%c" , 'j' ); p += 1 ; sprintf (p, "%c" , 'u' ); p += 1 ; sprintf (p, "%c" , 's' ); p += 1 ; sprintf (p, "%c" , 't' ); p += 1 ; sprintf (p, "%c" , 'i' ); p += 1 ; sprintf (p, "%c" , 'n' ); printf ("值为:%s" , a); return 0 ; }
判断字符串包含关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <string.h> int main () { char name[] = "justin is handsome" ; char *res = strstr (name, "is" ); if (!res) { printf ("不存在" ); } else { printf ("存在,从位置 %p 匹配成功的\n" , res); } return 0 ; }
字符串相加-复制字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <string.h> #include <stdlib.h> int main () { char name[]="justin" ; char role[]="teacher" ; char *s = malloc (strlen (name) + strlen (role) + 1 ); strcpy (s, name); strcat (s, role); printf (s); return 0 ; }
序号
函数 & 作用
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 的第一次出现的位置。
三 结构体 一种用户自定义的数据类型,它允许存储不同类型的数据项
结构体中的数据成员可以是基本数据类型(如 int、float、char 等),也可以是其他结构体类型、指针类型等
3.1 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> #include <string.h> #include <stdlib.h> struct Person { char name[30 ]; int age; }; int main () { struct Person p1 = {"justin" , 18 }; struct Person p2 = {"彭于晏" , 18 }; struct Person p3 = {"古天乐" , 18 }; printf ("p1的名字为:%s\n" , p1.name); printf ("p2的名字为:%s\n" , p2.name); printf ("p3的年龄为:%d\n" , p1.age); struct Person *pointPerson = &p3; printf ("p3的年龄为:%d\n" , (*pointPerson).age); printf ("p3的姓名为:%s\n" , pointPerson->name); return 0 ; }
3.2 单向链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> struct Node { int data; struct Node *next ; }; int main () { struct Node v3 = {33 }; struct Node v2 = {22 , &v3}; struct Node v1 = {11 , &v2}; printf ("v1的数据为:%d\n" , v1.data); printf ("v1的下一个元素数据为:%d\n" , v1.next->data); printf ("v1的下一个元素的下一个数据为:%d\n" , v1.next->next->data); return 0 ; }
3.3 双向链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> struct Person { int data; struct Person *next ; struct Person *prev ; }; int main () { struct Person p3 = { 33 }; struct Person p2 = { 22 }; struct Person p1 = { 11 }; p1.next = &p2; p2.next = &p3; p2.prev = &p1; p3.prev = &p2; printf ("p1的值: %d\n" , p1.data); printf ("p2的值: %d\n" , p1.next->data); printf ("p3的值: %d\n" , p1.next->next->data); printf ("p3的值: %d\n" , p3.data); printf ("p2的值: %d\n" , p3.prev->data); printf ("p1的值: %d\n" , p3.prev->prev->data); return 0 ; }
3.4 双向环状链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # include <stdio.h> struct Person { int data; struct Person *next ; struct Person *prev ; }; int main () { struct Person p3 = { 33 }; struct Person p2 = { 22 }; struct Person p1 = { 11 }; p1.next = &p2; p1.prev = &p3; p2.next = &p3; p2.prev = &p1; p3.prev = &p2; p3.next = &p1; printf ("p1的值: %d\n" , p1.data); printf ("p2的值: %d\n" , p1.next->data); printf ("p3的值: %d\n" , p1.next->next->data); printf ("p1的值: %d\n" , p1.next->next->next->data); printf ("p2的值: %d\n" , p1.next->next->next->next->data); printf ("p3的值: %d\n" , p1.next->next->next->next->next->data); return 0 ; }
四 预处理和头文件 4.1 预处理 预处理,在程序编译之前会先运行的。
1 2 3 4 5 6 7 8 9 10 11 # include <stdio.h> # define ME 200 # define SIZE 18 int main () { int data = 19 ; printf ("%d-%d-%d \n" , ME, SIZE, data); return 0 ; }
1 2 3 4 5 6 7 8 9 # include <stdio.h> # define ADD(x1, x2)(x1+x2+100) int main () { int data = ADD(11 , 22 ); printf ("结果:%d \n" , data); return 0 ; }
1 2 3 4 5 6 7 8 9 # include <stdio.h> # define DB(x1, x2)(x1*x2) int main () { int data = DB(2 + 1 , 4 ); printf ("结果:%d \n" , data); return 0 ; }
4.2 头文件 1 2 3 4 项目目录 ├── main.c ├── utils.c └── utils.h
1 2 3 4 5 int add (int a,int b) { return a+b; }
1 2 3 4 5 6 7 8 9 10 # include <stdio.h> # include "utils.h" int main () { int data = add(100 ,200 ); printf ("结果:%d \n" , data); return 0 ; }
比如后期,我们要进行JNI开发时,我们会在自己的c文件用去引入C语言中提供、JNI中提供一些头文件,才能使用头文件中的功能。
4.3 Python源码中头文件的使用 1 2 3 4 5 6 7 8 9 10 11 12 https://www.python.org/downloads/source/ https://github.com/python/cpython/tree/main/Objects https://github.com/python/cpython/blob/main/Objects/listobject.c https://github.com/python/cpython/blob/main/Include/listobject.h
__END__