四 C语言编译器安装
| |
| 一般公司的apk,基于Java实现的加密。 |
| - jadx反编译java,分析代码 |
| |
| NB公司的的apk,基于Java+C语言实现加密(JNI开发)。 |
| - jadx反编译java,分析代码 |
| - ida反编译c语言,分析代码 |
| |
| |
| 在C语言中,生成的可执行文件或共享库(.so文件)可以被反汇编和反编译,以还原源代码或了解其实现细节。下面是一些常用的C语言so文件反编译工具: |
| |
| IDA Pro:IDA Pro是一款强大的逆向工程工具,支持多种平台和架构,包括C语言。它提供了反汇编、反编译、调试等功能,能够帮助你还原C语言so文件的源代码。但需要注意的是,IDA Pro是商业软件,需要购买授权。 |
| |
| Ghidra:Ghidra是一款由美国国家安全局(NSA)开发的开源逆向工程工具。它支持多种平台和架构,并提供反汇编、反编译、脚本编写等功能,可以用于分析和还原C语言so文件的代码。 |
| |
| Radare2:Radare2是一个开源的逆向工程框架,提供了反汇编、反编译、调试等功能。它可以用于分析和还原C语言so文件的源代码,支持多种平台和架构。 |
| |
| Hopper Disassembler:Hopper Disassembler是一款反汇编工具,支持多种平台和架构。它可以将C语言so文件反汇编为汇编代码,并提供可视化界面和一些高级分析功能。 |
4.1 编译器安装
| |
| |
| |
| xcode: https://developer.apple.com/xcode/ |
| |
| |
| MInGW全称为:Minimalist GNU on Windows.将经典的开源C语言编译器GCC移植到了Windows平台下,并且包含了Win32API,因此可以将源代码编译为在Windows中运行的可执行程序 |
| |
| 而且还可以使用一些Windows不具备的,Linux平台下的开发工具。概括来讲:MinGW 是GCC的Windows版本 |
| |
| |
| MinGW只能编译生产32位可执行程序; |
| MinGW-w64可以编译成64bit或者32bit可执行程序 |
| |
| |
| https://zhuanlan.zhihu.com/p/355510947 |
| https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ |
| |
| -下载:https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/ |
| -下载不成功,用这个地址:https://sourceforge.net/projects/mingw/files/latest/download |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int main(int argc, char const *argv[]) { |
| |
| printf("hello world\n"); |
| |
| return 0; |
| } |
| |
| |
| gcc main.c |
| a.exe |
| |
4.2 IDE安装
| |
| |
| https://www.jetbrains.com/clion/download/other.html |
| |
| |
4.3 clion中文乱码问题
4.3.1 解决方案一
| 按住 Ctrl+Shift+Alt+/ 选中Registry... |


4.3.2 解决方案二


五 基础语法
5.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 |
| #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; |
| } |
5.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 位有效位 |
| #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; |
| } |
5.3 常量
| #include <stdio.h> |
| int main() { |
| const int MAX_VALUE =99; |
| |
| printf("%d\n",MAX_VALUE ); |
| return 0; |
| } |
5.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 |
| #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; |
| } |
| |
运算符 |
描述 |
== |
检查两个操作数的值是否相等,如果相等则条件为真。 |
!= |
检查两个操作数的值是否相等,如果不相等则条件为真。 |
> |
检查左操作数的值是否大于右操作数的值,如果是则条件为真。 |
< |
检查左操作数的值是否小于右操作数的值,如果是则条件为真。 |
>= |
检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 |
<= |
检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 |
运算符 |
描述 |
&& |
称为逻辑与运算符。如果两个操作数都非零,则条件为真。 |
|| |
称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 |
! |
称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 |
| #include <stdio.h> |
| #include <stdbool.h> |
| |
| int main() { |
| bool isTrue = true; |
| int a = 10; |
| int b = 20; |
| if (a && b) { |
| printf("条件为真\n"); |
| } |
| return 0; |
| } |
运算符 |
描述 |
= |
简单的赋值运算符,把右边操作数的值赋给左边操作数 |
+= |
加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 |
-= |
减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 |
*= |
乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 |
/= |
除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 |
%= |
求模且赋值运算符,求两个操作数的模赋值给左边操作数 |
<<= |
左移且赋值运算符 |
>>= |
右移且赋值运算符 |
&= |
按位与且赋值运算符 |
| #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 |
| #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; |
| } |
| |
5.5 if 判断
| #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; |
| } |
5.6 循环
循环类型 |
描述 |
while 循环 |
当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 |
for 循环 |
多次执行一个语句序列,简化管理循环变量的代码。 |
do...while 循环 |
除了它是在循环主体结尾测试条件外,其他与 while 语句类似。 |
| #include <stdio.h> |
| #include <stdbool.h> |
| |
| int main() { |
| |
| int num = 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| for (; ; ) { |
| printf("我是死循环"); |
| }; |
| return 0; |
| } |
| |
5.7 函数
| 返回值类型 函数名( 参数类型 形参 ) |
| { |
| 函数体; |
| } |
| #include <stdio.h> |
| |
| |
| int add(int a, int b) { |
| return a + b; |
| } |
| |
| int main() { |
| |
| int a = add(10, 10); |
| printf("%d", a); |
| return 0; |
| } |
| |
5.8 字符和字符串
在C语言中没有字符串。
用 字符数组 创造出字符串出来(每个字符占1个字节)。
| #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; |
| } |
5.9 数组
对于数组来说,内部元素是挨个存放,内存地址相邻。
它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,是一系列相同类型的变量
| char v2[]= {'j','u','s','t','i','n','\0'}; |
| int v3[3] = {11,22,33}; |
- 元素固定
- 类型固定(每个元素在内存中占据长度相同)
| #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; |
| } |
| # 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; |
| } |
六 指针
指针是存储变量内存地址的变量

6.1 定义指针类型变量和取变量地址
| #include <stdio.h> |
| int main() { |
| int v1 = 666; |
| int *v2 = &v1; |
| printf("v2的值为:%p", v2); |
| |
| return 0; |
| } |
6.2 指针类型变量解引用
| #include <stdio.h> |
| |
| |
| int main() { |
| int v1 = 666; |
| int *v2 = &v1; |
| printf("v2的值为:%p\n", v2); |
| printf("v2解引用后:%d\n",*v2); |
| return 0; |
| } |
6.3 修改变量的值
| #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; |
| } |
| |
6.4 指针的零值和长度
| #include <stdio.h> |
| |
| |
| int main() { |
| |
| |
| |
| long *v2 = NULL; |
| printf("%p\n", v2); |
| printf("%d\n", sizeof(v2)); |
| if (!v2) { |
| printf("指针为空"); |
| } |
| |
| return 0; |
| } |
| |
6.5 通过指针修改原变量值
| #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; |
| } |
| |
6.6 指针类型参数
| #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; |
| } |
| |
6.7 数组的指针(指针运算)
| |
| |
| 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; |
| } |
| |
6.8 指针的指针
| #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; |
| } |
| |
6.9 字符串案例
字符串格式化
| #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; |
| } |
| |
判断字符串包含关系
| #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; |
| } |
| |
字符串相加-复制字符串
| #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 等),也可以是其他结构体类型、指针类型等
7.1 基本使用
| #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; |
| } |
| |
7.1 单向链表

| #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; |
| } |
| |
7.3 双向链表


| #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; |
| } |
| |
7.4 双向环状链表

| # 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; |
| } |
八 预处理和头文件
8.1 预处理
预处理,在程序编译之前会先运行的。
| # include <stdio.h> |
| |
| # define ME 200 |
| # define SIZE 18 |
| |
| int main() { |
| int data = 19; |
| |
| printf("%d-%d-%d \n", ME, SIZE, data); |
| return 0; |
| } |
| # include <stdio.h> |
| |
| # define ADD(x1, x2)(x1+x2+100) |
| |
| int main() { |
| int data = ADD(11, 22); |
| printf("结果:%d \n", data); |
| return 0; |
| } |
| # include <stdio.h> |
| |
| # define DB(x1, x2)(x1*x2) |
| |
| int main() { |
| int data = DB(2 + 1, 4); |
| printf("结果:%d \n", data); |
| return 0; |
| } |
8.2 头文件
| 项目目录 |
| ├── main.c |
| ├── utils.c |
| └── utils.h |
| |
| |
| int add(int a,int b){ |
| return a+b; |
| } |
| |
| |
| # include <stdio.h> |
| # include "utils.h" |
| |
| int main() { |
| int data = add(100,200); |
| printf("结果:%d \n", data); |
| return 0; |
| } |
比如后期,我们要进行JNI开发时,我们会在自己的c文件用去引入C语言中提供、JNI中提供一些头文件,才能使用头文件中的功能。
8.3 Python源码中头文件的使用
| 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 |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2021-10-21 Linux的bg和fg命令