# 帶你閱讀一座程式 函式拆解II
###### tags: `libmodbus`
Copyright 2021, [月下麒麟](https://hackmd.io/@YMont/note-catalog)
---
## 今日主角: **modbus_set_slave(ctx, SERVER_ID)**
**Trace go**
Reference: [trace here](https://github.com/stephane/libmodbus/blob/master/tests/unit-test-server.c#L67)
```c=
modbus_set_slave(ctx, SERVER_ID);
```
進行查找其函式**定義**
```c=
int modbus_set_slave(modbus_t *ctx, int slave)
{
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
return ctx->backend->set_slave(ctx, slave);
}
```
發現定義僅寫,若指標為空,則不繼續往下執行
另外,回傳的內容為指標(ctx)去取得結構modbus_t的成員backend,
但該成員(backend)其型別是另一結構modbus_backend_t,
繼續指向modbus_backend_t,並欲取得成員set_slave。
到這裡,進入第二層結構定義裡面,
```c=
typedef struct _modbus_backend {
//...
int (*set_slave) (modbus_t *ctx, int slave);
//...
} modbus_backend_t;
```
同時,你也會有疑問,那然後呢? ~~抵達世界的盡頭~~ \~.~

別緊張,請善用搜尋~ **modbus_backend_t**

找出所有有關該結構名稱存在的地方
除了在modbus-private.h 定義處出現外,
分別在**modbus-rtu.c** **modbus-tcp.c**,
**關鍵就是利用struct賦予初值,而初值的內容為函式名稱
同時,會將函式名稱套入到 結構定義處**
```c=
const modbus_backend_t _modbus_rtu_backend = {
//...
_modbus_set_slave, //函式名稱
//...
};
//=====分隔線,不同位置的程式碼=====
int (*set_slave) (modbus_t *ctx, int slave); //將函式名稱套入於此
```
這個技巧運用到了結構初值的賦予,與function point
---
**小補充:**
```c=
const modbus_backend_t _modbus_rtu_backend = {
//...
_modbus_set_slave,
//...
};
```
那你可能也觀察到 **\_modbus_rtu_backend** 這個struct variable要做什麼用?
對先對**modbus_backend_t**進行搜尋,
會發現到modbus-rtu.c 與 modbus-tcp.c都有它的身影,
這與上一篇文章的初始化有關[帶你閱讀一座程式 函式分解I](https://hackmd.io/INCbn-ZoTfuqDyLt8o9nuQ)
```c=
const modbus_backend_t _modbus_tcp_backend = {};
const modbus_backend_t _modbus_rtu_backend = {};
```
可以觀察到利用結構modbus_backend_t去創建兩個賦予結構初值的陣列,
並且將該宣告的陣列給予名稱 **\_modbus_tcp_backend** , **\_modbus_rtu_backend**
>賦予名稱最主要的意義為,可以讓它被接續使用
進行名稱搜訊
```c=
ctx->backend = &_modbus_tcp_backend;
ctx->backend = &_modbus_rtu_backend;
```
呼應上一篇,就是讓此處的初始化指向該陣列。
---
鏡頭拉回來,進行\_modbus_set_slave的解析
```c=
static int _modbus_set_slave(modbus_t *ctx, int slave)
{
/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
if (slave >= 0 && slave <= 247) {
ctx->slave = slave;
} else {
errno = EINVAL;
return -1;
}
return 0;
}
```
判斷slave號碼是介於0~247的範圍,
這部分可以參考Modbus Serial Specification裏頭有載名。

---
## 今日配角: header_length, checksum_length, max_adu_length
這三個結構成員出現在**modbus_backend_t**結構裡面。
會發現到不是還有一個backend_type嗎? 為何沒放進來?
因為該成員變數僅用來區分tcp, rtu。
那要介紹這三個主要為跟Modbus Protocol有關
```c=
/*RTU*/
_MODBUS_BACKEND_TYPE_RTU, //0
_MODBUS_RTU_HEADER_LENGTH, //1 -->server address (slave address)
_MODBUS_RTU_CHECKSUM_LENGTH, //2
MODBUS_RTU_MAX_ADU_LENGTH, //256
//=====分隔線,不同位置的程式碼=====
/*TCP*/
_MODBUS_BACKEND_TYPE_TCP, //1
_MODBUS_TCP_HEADER_LENGTH, //7
_MODBUS_TCP_CHECKSUM_LENGTH, //0
MODBUS_TCP_MAX_ADU_LENGTH, //260
```
這裡僅說明各成員之數值,與其Modbus Specification做對應。

---

---

---
這段說明書上的文字就很詳細的定義解釋了。

---
最後一上圖,Reference:[實例研討: 從 C++ 學習 C 高級技巧](https://goodspeedlee.blogspot.com/2016/10/c-c.html)
今天就寫到這,感謝您的閱讀。