学习日志:call_test.c程序分析

1.完整代码

#include <stdio.h>
#include <stdlib.h>
int incr(int *p, int val) {
    int x = *p;
    int y = x + val;
    *p = y;	
    return x;
}

int call_incr2(int x) {
    int v1 = 15213;
    int v2 = incr(&v1, 3000);
	printf("v1= %d\n &v1= %p\n v2= %d\n &v2= %p\n", v1,&v1,v2,&v2);
	printf("x= %d\n &x= %p\n", x,&x);
    return x+v2;
}

int call_incr() {
    int v1 = 15213;
    int v2 = incr(&v1, 3000);
	printf("v1= %d\n &v1= %p\n v2= %d\n &v2= %p\n", v1,&v1,v2,&v2);	
    return v1+v2;
}

int main(int argc, char *argv[]) {
    if (argc > 1){
	int n=atoi(argv[1]);
	if (n==1)
	{
		int v = call_incr();
		printf("v1+v2 = %d\n", v);	
	}
	else if (n==2)
	{
		int v = call_incr2(1000);	
		printf("x+v2= %d\n",v);
	}}
	
	return 0;
}

2.代码分析

(1)主函数中“int main(int argc,char *argv[])”,

“形参argc是整型变量,用于存放命令行中参数的个数,因为程序名也计算在内,所以argc的值至少为1。形参argv是字符指针数组,数组中的每一个元素都是一个字符串指针,指向命令行中的一个命令行参数。”

(2)第27行“atoi()”,(在标准库<stdlib.h>中)

“C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。”

(3)主函数的选择语句,表示当命令行中输入参数“1”时,调用函数“call_incr()”,当命令行中输入参数“2”时,调用函数“call_incr2()”。

(4)函数“call_incr()”与“call_incr2()”的区别仅仅是最后返回的数据不相同,然后“call_incr2()”有参数传递,而“call_incr()”没有。

(5)在“incr()”函数中,形参“*p”指向的是“v1”的地址,val = 3000。“int x = *p”将指针“p”指向的值赋给了“x”,即将“v1”的值赋给了“x”。“int y = x + val”,即y = v1+val,然后“*p = y”,将“p”指向的数赋值为“y”,即此时v1的值变为v1+val。

3.运行程序并分析结果

(1)在命令行中输入“gcc -o call_test.o call_test.c”,“./call_test.o 1”。结果如图:
在这里插入图片描述
分析:此时调用了函数“call_incr()”,根据开始的代码分析,在该函数调用“incr()”之后,v1 = v1+val = 15213+3000 = 18213,v2 = x = *p = 15213。所以,最后有v1+v2 = 18213+15213 = 33426。我们还发现v1的地址和V2的地址正好相差4个字节,即为整型所占的字节数。

(2)在命令行中再输入“./call_test.o 2”。结果如图:
在这里插入图片描述
分析:前一部分的分析和“call_incr()”的相同,只是最后返回的数据为x+v2 = 1000+15213=16213。同样可以发现x,v1,v2的地址依次相差4个字节,即为整形所占字节数。

3.反编译结果

结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

incr()分析:

①前两句,先将基指针入栈,然后将基指针指到栈指针的位置。

②形参整形指针p按规定存储在寄存器“%rdi”中,“mov %rdi,-0x18(%rbp)”,将形参p入栈,位置在“-0x18(%rbp)”

③形参整形val存储在寄存器“%esi”中,“mov %esi,-0x1c(%rbp)”,将形参val入栈,位置在“-0x1c(%rbp)”

④“mov -0x18(%rbp),%rax”即将p所指向的地址存储在寄存器“%rax”中

⑤“mov (%rax),%eax”即将“%rax”中存储的地址所存的数据存储在“%eax”中,(4)和(5)两句代表着C语言中的“int x = *p;”

⑥“mov %eax,-0x8(%rbp)”,将寄存器“%eax”中的值存储在“-0x8(%rbp)”中,即将x的值存储在“-0x8(%rbp)”中

⑦“mov -0x8(%rbp),%edx”,将x的值存储在寄存器“%edx”当中

⑧“-0x1c(%rbp),%eax”,“-0x1c(%rbp)”之前存放的值是val,指一句就是将val的值存储在寄存器“%eax”当中

⑨“add %edx,%eax”,即%eax = %eax + %edx,即将x+val的值存储在寄存器“%eax”当中

⑩“mov %eax,%-0x4(%rbp)”,将%eax存储器中的数据入栈,存储在“%-0x4(%rbp)”

⑪“mov -0x18(%rbp),%rax”,之前“-0x18(%rbp)”中存储的是p指向的地址的值,这一句即将p指向的地址(即x的地址)存储在寄存器%rax当中

⑫“mov -0x4(%rbp),%edx”,%-0x4(%rbp)中存储的值为x+val,这一句将x+val的值存储在寄存器%edx当中,此时完成C语言当中的语句“int y=x+val;”

⑬“mov %edx,(%rax)”,此句将%edx中存储的值赋值给在%rax存储的地址所指向的数据,对应于C语言中的“*p = y;”

⑭“mov -0x8(%rbp),%eax”,就是将x的值存储在寄存器%eax中

⑮最后退栈并返回寄存器%eax当中的值,即返回x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我明白了。这是一个关于ROS工作空间和功能包的问题,需要创建一个服务端程序和客户端程序,实现对服务的请求与响应。在name_ws工作空间下,进入name_学号_pkg功能包的src目录,你需要创建两个文件:name_server.cpp和name_client.cpp。 首先,我们来创建服务端程序name_server.cpp。在该文件中,你需要定义一个服务,并编写回调函数来处理客户端请求。具体实现方法如下: ```c++ #include "ros/ros.h" #include "name_学号_pkg/NameService.h" // 根据实际情况修改 bool callback(name_学号_pkg::NameService::Request &req, name_学号_pkg::NameService::Response &res) { // 在这里编写服务的处理逻辑 // ... // 处理完后将结果存入 res 中 res.name = "Hello, " + req.name + "!"; // 以返回字符串“Hello, 名字!”为例 ROS_INFO("Received request from %s with response %s", req.name.c_str(), res.name.c_str()); return true; } int main(int argc, char **argv) { ros::init(argc, argv, "name_server"); ros::NodeHandle n; // 定义服务并设置回调函数 ros::ServiceServer service = n.advertiseService("name_service", callback); ROS_INFO("Ready to receive request."); ros::spin(); return 0; } ``` 在上述代码中,我们首先包含了ROS头文件和我们需要的服务文件,然后定义了一个回调函数callback,用于处理客户端请求。在该函数中,我们可以根据客户端传递的参数req进行一些处理,然后将结果存入服务响应res中。在本例中,我们只是简单地返回一个字符串,表示向客户端问候。 接下来,在main函数中,我们初始化ROS节点,然后定义一个服务,并设置回调函数为我们刚才编写的callback函数。最后,我们在循环中等待客户端请求,并通过ROS_INFO函数输出日志。 接下来,我们来创建客户端程序name_client.cpp。在该文件中,你需要编写代码来向服务端发送请求,并处理服务端的响应。具体实现方法如下: ```c++ #include "ros/ros.h" #include "name_学号_pkg/NameService.h" // 根据实际情况修改 int main(int argc, char **argv) { ros::init(argc, argv, "name_client"); ros::NodeHandle n; // 创建一个与服务端通信的客户端对象 ros::ServiceClient client = n.serviceClient<name_学号_pkg::NameService>("name_service"); // 创建请求和响应对象 name_学号_pkg::NameService::Request req; name_学号_pkg::NameService::Response res; // 设置请求参数 req.name = "world"; // 向服务端发送请求 if (client.call(req, res)) { ROS_INFO("Response from server: %s", res.name.c_str()); } else { ROS_ERROR("Failed to call service name_service"); return 1; } return 0; } ``` 在上述代码中,我们同样包含了ROS头文件和服务文件,然后创建了一个与服务端通信的客户端对象client,用于向服务端发送请求。接下来,我们创建请求和响应对象req和res,并设置请求参数req.name为"world"。注意,这里的req.name需要根据实际情况修改。 最后,我们调用客户端对象的call方法来向服务端发送请求,并处理服务端的响应。如果请求成功,我们通过ROS_INFO函数输出响应结果;否则,我们通过ROS_ERROR函数输出错误信息。 完成以上步骤后,你可以在终端中分别编译和运行服务端和客户端程序。在编译时,需要使用catkin_make命令,如下所示: ``` $ cd ~/name_ws $ catkin_make ``` 如果编译成功,你可以在devel/lib/name_学号_pkg目录下找到编译生成的name_server和name_client可执行文件。然后,你可以在两个不同的终端中分别运行服务端和客户端程序,如下所示: ``` # 终端1:运行服务端程序 $ rosrun name_学号_pkg name_server # 终端2:运行客户端程序 $ rosrun name_学号_pkg name_client ``` 如果一切正常,你应该能够在终端2中看到类似如下的输出: ``` [ INFO] [1632311250.932603989]: Response from server: Hello, world! ``` 这表明服务端已经成功地响应了客户端的请求,并返回了处理结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值