Android Binder通信 - bio_put_string16_x函数

110 阅读5分钟

一、函数原型:

这函数主要就是向binder_io结构体里面写入一个string16的字符串。

 /**
  * 向 binder_io 结构体中写入一个 UTF-16 格式的字符串
  * binder_io 结构体的指针 bio
  * const char* 类型的字符串 _str
  * */
 void bio_put_string16_x(struct binder_io *bio, const char *_str)
 {
     unsigned char *str = (unsigned char*) _str;
     size_t len;
     uint16_t *ptr;
     // 如果字符串为null,则向 bio 中写入 0xffffffff 表示空字符串,然后返回
     if (!str) {
         bio_put_uint32(bio, 0xffffffff);
         return;
     }
     // 计算输入字符串的长度
     len = strlen(_str);
     // 检查字符串长度是否超出了定义的最大长度
     if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
         bio_put_uint32(bio, 0xffffffff);
         return;
     }
 ​
     /* Note: The payload will carry 32bit size instead of size_t */
     // 向 bio 结构体中写入字符串长度,使用 bio_put_uint32 函数将长度作为一个 32 位整数写入
     bio_put_uint32(bio, len);
     // 在 bio 结构体中分配足够空间来存储 UTF-16 格式的字符串,长度为 (len + 1) 个 uint16_t 字符
     ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t));
     if (!ptr)
         return;
 ​
     while (*str)
         *ptr++ = *str++;
     *ptr++ = 0;
 }

1、科普小课堂:

有点啰嗦,主要是防止对c语言不熟悉的同学没注意到这个知识点,高手跳过!!

1)uint32_t和size_t:

注意看上面bio_put_uint32的注释:

/* Note: The payload will carry 32bit size instead of size_t */

为什么要强调uint32_t呢?那是因为:

size_t 的大小可能会根据不同的编译器、操作系统和硬件架构而有所不同,因此,转为uint32_t,保证可移植性 在32位系统上,size_t 通常是4字节(32位),而在64位系统上,size_t 通常是8字节(64位)

2)bio_alloc大小:

看下面这句代码,为什么分配大小是len+1,而不是len呢?

len+1是因为,c语言的字符串,最后面需要一个空字符 ('\0')

二、uint16_t ptr和char ptr:

uint16_t *ptrchar *ptr 在 C 语言中代表不同的数据类型和用途:

  1. uint16_t *ptr

    • uint16_t 是 C 语言标准中定义的一个无符号 16 位整数类型,通常用于存储 Unicode 字符或者处理 16 位数据。
    • uint16_t *ptr 声明了一个指向 uint16_t 类型数据的指针。这种指针通常用于处理 16 位数据,例如在处理 UTF-16 编码的字符串时会使用这种类型。
  2. char *ptr

    • char 是 C 语言中表示字符的数据类型,通常用于存储 ASCII 字符或者字节数据。
    • char *ptr 声明了一个指向 char 类型数据的指针。这种指针通常用于处理字符数据或字节数据,例如在处理字符串时经常会使用这种类型。

主要区别在于数据类型的不同:uint16_t 是一个 16 位整数类型,而 char 是一个字符类型。因此,根据所处理的数据的不同,选择合适的指针类型很重要。如果处理的是 16 位数据,如 UTF-16 编码的字符串,那么使用 uint16_t *ptr 更合适;而如果处理的是字符数据或字节数据,通常会使用 char *ptr

在 C 语言中,指针的类型决定了指针所指向的数据类型,因此选择正确的指针类型能够更好地反映所处理数据的实际类型,避免数据类型转换或错误处理。

三、举例说明:

让我们通过一个具体的例子来说明 uint16_t *ptrchar *ptr 之间的区别。假设我们有一个 UTF-16 编码的字符串,我们将看到如何使用这两种指针类型来处理这个字符串。

假设我们有一个 UTF-16 编码的字符串 "Hello",其内部存储形式为 uint16_t 类型的数据。现在我们来看看如何使用 uint16_t *ptrchar *ptr 来处理这个字符串:

 #include <stdio.h>
 #include <stdint.h>
 ​
 int main() {
     // UTF-16 encoded string "Hello" (H: 0x0048, e: 0x0065, l: 0x006C, o: 0x006F, null termination: 0x0000)
     uint16_t utf16_string[] = {0x0048, 0x0065, 0x006C, 0x006C, 0x006F, 0x0000};
 ​
     // Using uint16_t pointer to access UTF-16 encoded string
     uint16_t *ptr_uint16 = utf16_string;
     while (*ptr_uint16 != 0x0000) {
         printf("%c", (char)(*ptr_uint16));  // Convert uint16_t to char for printing
         ptr_uint16++;
     }
     printf("\n");
 ​
     // Using char pointer to access UTF-16 encoded string byte by byte
     char *ptr_char = (char *)utf16_string;
     while (*ptr_char != 0x00 || *(ptr_char + 1) != 0x00) {
         printf("%c", *ptr_char);
         ptr_char += 2;  // Move pointer by 2 bytes to access next UTF-16 character
     }
     printf("\n");
 ​
     return 0;
 }

在上面的例子中,我们首先定义了一个 UTF-16 编码的字符串 utf16_string,然后展示了如何使用 uint16_t *ptrchar *ptr 来访问这个字符串。

  • 使用 uint16_t *ptr:我们通过逐个读取 uint16_t 类型的数据来访问 UTF-16 编码的字符串,然后将其转换为 char 类型来打印字符。
  • 使用 char *ptr:我们将 uint16_t 类型的数据强制转换为 char 类型,以便以字节为单位来访问 UTF-16 编码的字符串。在这种情况下,我们需要注意跨越每个 UTF-16 字符的两个字节。

四、为什么要使用uint16_t指针?

选择使用 uint16_t *ptr 还是 char *ptr 取决于所处理的数据类型和操作粒度。如果处理的是 16 位数据或 UTF-16 编码的字符串,uint16_t *ptr 更为合适;而如果处理的是字节数据或者单字节字符,char *ptr 则更为适合。说白了还是编码方式决定的。