今日内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# xml持久化
# okhttp拦截器
-拦截器使用
-拦截器总结
-拦截器逆向重点
# retrofit
-retrofit使用
-retrofit使用逆向重点
# 逆向自己app

# c语言介绍
# 第一个c HelloWorld
# c编辑器安装
-乱码问题
# C语言基础
-c语言之整形
-c语言之浮点型
-运算符
-if判断
-循环
-函数
-字符和字符串
-数组

零 xml持久化

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
# 后端获取的数据(如:token等),保存到手机中,一般保存在xml中

# 保存到手机上:`/data/data/包名`
adb shell
su
cd /data/data
cd 包名
cd shared_prefs
ls
cat sp_token.xml



# 保存
SharedPreferences sp = getSharedPreferences("sp_token", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("token","111111");
editor.commit();


# 删除
SharedPreferences sp = getSharedPreferences("sp_token", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove("token");

#读取
SharedPreferences sp = getSharedPreferences("sp_token", MODE_PRIVATE);
String token = sp.getString("token","");

注意:后期逆向时经常使用,放在xml中的一般都是app刚启动时、刚登录时。


一 拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 问题
在安卓端发送请求时,每次都要携带固定的数据(请求头中携带),以后不需要每次都手动添加,只需要写一次,以后只要发请求就会携带,

# 解决
使用拦截器

# 使用步骤
1 定义拦截器--》可以定义多个-->
-方式一:写一个类,继承Interceptor,重写intercept方法
-方式二:直接实例化并得到对象
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("ctime", "").addHeader("sign", "").build();
Response response = chain.proceed(request);
return response;
}
};

2 在发送请求时,使用拦截器
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

1.1 实战

MainActivity.java

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
41
42
43
44
45
46
47
public void loginForm() {
// 获取输入框内容
String username = String.valueOf(txt_user.getText());
String password = String.valueOf(txt_pwd.getText());
String sign = Utils.md5(username + "justin");
String ctime = String.valueOf(System.currentTimeMillis() / 1000); // 字符串类型时间戳
Toast t = Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_LONG);
// 创建一个新线程,发送网络请求
new Thread() {
@Override
public void run() {
// 使用方式一
CommonInterceptor interceptor=new CommonInterceptor();
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

// 使用方式二:
// OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
// @Override
// public Response intercept(Chain chain) throws IOException {
// // 1 获取当前时间,获取签名
// String ctime=String.valueOf(System.currentTimeMillis() / 1000);
// String sign= Utils.md5(ctime);
// // 2 把当前时间和签名添加到请求头中
// Request request = chain.request().newBuilder().addHeader("ctime", ctime).addHeader("sign", sign).build();
// // 3 继续执行下面的拦截器
// Response response = chain.proceed(request);
// //4 把返回的响应对象返回回去
// return response;
// }
// }).build();
FormBody form = new FormBody.Builder().add("username", username).add("password", Utils.md5(password)).add("sign", sign).build();
Request req = new Request.Builder().url("http://192.168.1.12:8080/login").post(form).build();
Call call = client.newCall(req);
try {
Response res = call.execute();
ResponseBody body = res.body();// 响应体内容,不是字符串类型,ResponseBody类型
String dataString = body.string(); // ResponseBody类型转成字符串类型
CommonResponse obj = new Gson().fromJson(dataString, CommonResponse.class);
t.show(); //
} catch (IOException e) {
Log.e("MainActivity", e.toString()); // 如果出错,打印日志
}
}
}.start();


}

interceptor/CommonInterceptor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class CommonInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
// 1 获取当前时间,获取签名
String ctime=String.valueOf(System.currentTimeMillis() / 1000);
String sign= Utils.md5(ctime);
// 2 把当前时间和签名添加到请求头中
Request request = chain.request().newBuilder().addHeader("ctime", ctime).addHeader("sign", sign).build();
// 3 继续执行下面的拦截器
Response response = chain.proceed(request);
//4 把返回的响应对象返回回去
return response;
}
}

1.2 安卓项目常用做法

定义一个静态变量

1
2
3
4
5
6
7
import com.justin.demo.interceptor.CommonInterceptor;

import okhttp3.Interceptor;

public class Common {
public static Interceptor interceptor=new CommonInterceptor();
}

以后用直接使用,不需要每次都实例化

1
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(Common.interceptor).build();

1.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
# 逆向时,如何定位拦截器
1 根据关键字进行定位(Interceptor):
参数不在业务逻辑而是在拦截器中
关键字搜索 Interceptor ,很可能就是算法生成的位置
2 通过hook定位有哪些拦截器,通过Hook addInterceptor 看执行多少次,就能确定有多少拦截器
hook的通用的脚本
scr = """
Java.perform(function () {
var Builder = Java.user("okhttp3.OkHttpClient.Builder");
Builder.addInterceptor.implementation = function(interceptor){
console.log(interceptor, JSON.stringify(interceptor))
}
});
"""


# 原理解释
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
# 在OkHttpClient内部有内部类,Builder,实例化得到对象后,调用Builder类对象的addInterceptor方法,最后调用Builder类对象的build方法,完成实例化OkHttpClient对象
# 所以我们要hook:okhttp3.OkHttpClient.Builder 类,的addInterceptor方法
class OkHttpClient{
class Builder{
fun addInterceptor(Interceptor)
}
fun build(): OkHttpClient = OkHttpClient(this)

}

二 retrofit

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
41
42
43
44
45
46
47
48
49
50
51
#  retrofit介绍
retrofit是对okhttp的封装,底层使用了okhttp,让我们发送网络请求更简单

# 使用步骤:
1 build.gradle中引入retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
2 编写接口,声明网络请求
public interface HttpReq {
# 向/api/v1/post 发送POST请求,编码是UrlEncoded,传入userName和password,但被组装成 name=xx&pwd=xx
@POST("/api/v1/post")
@FormUrlEncoded
Call<ResponseBody> postLogin(@Field("name") String userName, @Field("pwd") String password);

# 向/api/v2/xxx发送get请求,传入age,拼接成 /api/v2/xxx?age=19
@GET("/api/v2/xxx")
Call<ResponseBody> getInfo(@Query("age") String age);

# 向/post/users 发送POST请求,编码是json格式 {name:xxxx,age:123}
@POST("/post/users")
Call<ResponseBody> postLoginJson(@Body RequestBody body);
}

3 发送请求
new Thread() {
@Override
public void run() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.1.12:8080/").build();
HttpReq req = retrofit.create(HttpReq.class);
Call<ResponseBody> call = req.postLogin("justin","123");
try {
ResponseBody responseBody = call.execute().body();
String responseString = responseBody.string();
Log.e("Retrofit返回的结果", responseString);

} catch (Exception e) {
e.printStackTrace();
}
}
}.start();

4 简写成
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.1.12:8080/").build();
try {
ResponseBody responseBody = retrofit.create(HttpReq.class).postLogin("justin","123").execute().body();
String responseString = responseBody.string();
Log.e("Retrofit返回的结果", responseString);

} catch (Exception e) {
e.printStackTrace();
}

2.1 实践

HttpReq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.justin.demo;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;

public interface HttpReq {
@POST("/login")
@FormUrlEncoded
Call<ResponseBody> postLogin(@Field("username") String userName, @Field("password") String password, @Field("sign") String sign);
//向/api/v2/xxx发送get请求,传入age,拼接成 /api/v2/xxx?age=19
@GET("/film")
Call<ResponseBody> getFilm(@Query("name") String name);
}

MainActivity

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
public void loginForm() {
// 获取输入框内容
String username = String.valueOf(txt_user.getText());
String password = String.valueOf(txt_pwd.getText());
String sign = Utils.md5(username + "justin");
Toast t = Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_LONG);
// 创建一个新线程,发送网络请求
new Thread() {
@Override
public void run() {
// 使用拦截器
//OkHttpClient client = new OkHttpClient.Builder().addInterceptor(Common.interceptor).build();
//Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.1.12:8080/").client(client).build();
// 不使用拦截器
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.1.12:8080/").build();
try {
ResponseBody responseBody = retrofit.create(HttpReq.class).postLogin(username,password,sign).execute().body();
String responseString = responseBody.string();
Log.e("Retrofit返回的结果", responseString);
t.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();


}

2.2 逆向

1
2
-1 定位代码:搜索URL,可以定位到接口的位置(除去域名和端口)  /login
例如:合伙人app,搜索: /v1/partnerLogin/login

image-20230713164603608

image-20240517163122312

image-20240517163130103

三 逆向自己的app

1
2
3
# 1 编译app
# 2 使用charles抓包
# 3 使用jadx打开

image-20240517163138630

image-20240517163149413

四 C语言编译器安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 学习c语言的原因
一般公司的apk,基于Java实现的加密。
- jadx反编译java,分析代码

NB公司的的apk,基于Java+C语言实现加密(JNI开发)。
- jadx反编译java,分析代码
- ida反编译c语言,分析代码

## c语言动态链接库so文件反编译工具
在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 编译器安装

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
# C语言是编译型语言,编写好代码之后,需要由编译器编译后成不同平台的可执行文件才能执行,常见的编译器GCC、MSVC、Clang

# mac,linux: 默认自带gcc,不需要额外安卓,mac如果没有就去直接安装xcode。
xcode: https://developer.apple.com/xcode/

# win:MinGW
MInGW全称为:Minimalist GNU on Windows.将经典的开源C语言编译器GCC移植到了Windows平台下,并且包含了Win32API,因此可以将源代码编译为在Windows中运行的可执行程序

而且还可以使用一些Windows不具备的,Linux平台下的开发工具。概括来讲:MinGW 是GCC的Windows版本

# MinGW-w64与MinGW的区别:
MinGW只能编译生产32位可执行程序;
MinGW-w64可以编译成64bit或者32bit可执行程序

# MinGW-w64安装,参照教程
https://zhuanlan.zhihu.com/p/355510947
https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/
# 我们安装 MinGW
-下载:https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/
-下载不成功,用这个地址:https://sourceforge.net/projects/mingw/files/latest/download

# 安装路径加入环境变量,测试



# 编写代码 main.c

# include <stdio.h>

int main(int argc, char const *argv[]) {

printf("hello world\n");

return 0;
}

# 编译并运行(在终端)
gcc main.c
a.exe

image-20240517163205632

image-20240517163214152

image-20240517163225904

image-20240517163237436

image-20240517163248655

image-20240517163259723

image-20240517163312782

image-20240517163325552

image-20240517163333584

image-20240517163342193

image-20240517163350944

image-20240517163403215

image-20240517163410651

4.2 IDE安装

1
2
3
4
5
# 我们使用jetbrains公司的CLion作为IDE
#下载地址:(破解方案同pycharm和idea)
https://www.jetbrains.com/clion/download/other.html

# 一路下一步安装,成功后打开,创建项目

image-20240517163421708

image-20240517163428732

image-20240517163437968

4.3 clion中文乱码问题

4.3.1 解决方案一

1
按住 Ctrl+Shift+Alt+/ 选中Registry...

image-20240517163446525

image-20240517163454720

4.3.2 解决方案二

image-20240517163502939

image-20240517163510025

五 基础语法

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
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);
//short i=32767; // 默认有符号,整数最大32767
//signed short i=32767; // 默认有符号,整数最大32767
unsigned short i1=65535; // 有符号,正数最大 65535

//int i2=65536;
long i3=65536;
printf("%d",i3);

// 各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主
// int 在64位机器上,占4字节
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 位有效位
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));
// %f代表一般计数法输出,%e代表指数计数法输出
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 常量

1
2
3
4
5
6
7
#include <stdio.h>
int main() {
const int MAX_VALUE =99;
//MAX_VALUE =199;
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
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++; // 赋值后再加 1 ,c 为 10,a 为 11
printf("赋值后再加 的值是 %d,a的值为:%d\n", c, a);
c = a--; // 赋值后再减 1 ,c 为 11 ,a 为 10
printf("赋值后再减 1的值是 %d,a的值为:%d\n", c, a);
return 0;
}
// a++ 与 ++a 的区别
  • 关系运算符
运算符 描述
== 检查两个操作数的值是否相等,如果相等则条件为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。
  • 逻辑运算符
运算符 描述
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。
|| 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。
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;
}

5.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;
}

5.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;
// while循环
// while (num<10){
// printf("%d\n",num);
// num++;
// }

//do while 循环
// do {
// printf("%d\n", num);
// num++;
// } while (num < 10);

// for 循环
// for (int i = 0; i < 10; ++i) {
// printf("%d\n", i);
// }

//死循环
for (; ; ) {
printf("我是死循环");
};
return 0;
}

5.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;
}

5.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;
}

5.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[3]; 定义没有初始化
//int a[3]={1,2,3}; // 定义并初始化
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}; // 每个整型4字节
printf("第0个位置值:%d,内存地址:%p \n", v3[0], &v3[0]); // 值所在的内存地址 0x00000
printf("第1个位置值:%d,内存地址:%p \n", v3[1], &v3[1]); // 0x00004
printf("第2个位置值:%d,内存地址:%p \n", v3[2], &v3[2]); // 0x00008
return 0;
}

__END__