CAPL的数据类型(一)

系列文章目录

CAPL的数据类型(一)

CAPL的数据类型(二)


文章目录
系列文章目录

前言

一、数值类型及其格式化打印

[1. 数值类型](#1. 数值类型)

[2. 格式化打印](#2. 格式化打印)

二、数组

[1. 数组的初始化和访问](#1. 数组的初始化和访问)

[2. 求数组的大小](#2. 求数组的大小)

[3. 数组作为参数传递](#3. 数组作为参数传递)

[4. 字符数组](#4. 字符数组)

三、枚举类型

[1. 枚举类型的定义](#1. 枚举类型的定义)

[2. 枚举类型的应用](#2. 枚举类型的应用)

[3. 枚举类型的语法拓展](#3. 枚举类型的语法拓展)

四、结构体类型

[1. 结构体类型定义](#1. 结构体类型定义)

[2. 结构体变量初始化](#2. 结构体变量初始化)

[3. 结构体变量成员访问与赋值](#3. 结构体变量成员访问与赋值)

[4. 结构体数组](#4. 结构体数组)

[5. 结构体作为函数参数](#5. 结构体作为函数参数)

[6. 结构体嵌套](#6. 结构体嵌套)

[7. 结构体字节对齐](#7. 结构体字节对齐)

[8. 字节对齐应用](#8. 字节对齐应用)

总结


前言

CAPL(Communication Access Programming Language)是用于CANoe、CANalyzer等车载网络仿真与测试工具的编程语言,广泛应用于汽车电子系统开发中的通信协议测试与自动化脚本编写。其数据类型设计兼顾实时性、可读性与嵌入式系统的资源限制,是理解CAPL程序逻辑的基础。


一、数值类型及其格式化打印

1. 数值类型

CAPL 语言中的数值类型和 C 语言中的数值类型相似,主要有以下 3 点区别:

  1. CAPL 语言中的 byte,word,dword,qword 数值类型和 C 语言中的 unsigned char,unsigned int,unsigned long,unsigned int64 数据类型是相对应的;

无符号数:

  • byte -> unsigned char,1 byte
  • word -> unsigned int,2 bytes
  • dword -> unsigned long,4 bytes
  • qword -> unsigned int64, 8 bytes

有符号数:

  • char -> char,1 byte
  • int -> short,2 bytes
  • long -> long,4 bytes
  • int64 -> long long,8 bytes

浮点数:

  • float -> double,8 bytes
  • double -> double,8 bytes
  1. CAPL 中的 float 和 double 不区分单精度和双精度,两者都是双精度,都占 8 字节,可以认为没有区别;

  2. CAPL 语言中的 int 类型占两个字节,且和编译器无关;

2. 格式化打印

在 CAPL 语言中,通过 write 函数来打印内容,进行格式化输出;

但是 CAPL 编译器不会检测打印格式是否正确,如果打印格式是错误的,打印出来的内容就是非预期的,编译器仍然不会报错。所以使用这个函数要注意打印格式是否正确;

有符号显示:

  • int -> %d
  • long -> %d 或者 %ld
  • int64 -> %lld 或者 %I64d

无符号显示:

  • byte/word -> %u
  • dword -> %lu
  • qword -> %llu 或者 %I64u

十六进制显示:

  • byte/word/int -> %x
  • dword/long -> %lx
  • qword/int64 -> %llx 或者 %I64x

十六进制大写显示:

  • byte/word/int -> %X
  • dword/long -> %lX
  • qword/int64 -> %llX 或者 %I64X

浮点数显示:

  • float/double -> %g 或者 %f

字符/字符串显示:

  • character -> %c
  • string -> %s

十六进制显示打印示例:

cpp 复制代码
on key 'a'
{
  byte a = 0x01;
  word b = 0xab20;
  dword c = 0x10203040;
  qword d = 0x1020304050607080LL;
  
  write("a = 0x%x", a);
  write("b = 0x%x", b);
  write("c = 0x%lx", c);
  write("d = 0x%llx", d);
}

十进制数显示打印示例:

cpp 复制代码
on key 'b'
{
  int a = 32767;
  long b = 214783647;
  int64 c = 123456778902LL;
  
  write("a = %d", a);
  write("b = %ld", b);
  write("c = %lld", c);
}

浮点数打印示例:

cpp 复制代码
on key 'c'
{
  double a = 345.541236732;
  float b = 1023.2893042123;
  
  write("a = %f", a);
  write("b = %f", b);
  write("b = %.2f", b); // 保留两位小数
}

字符及字符串打印示例:

cpp 复制代码
on key 'd'
{
  char a = 'A';
  char b = 65;
  char c[100] = "Hello World!";
  
  write("a = %c", a);
  write("b = %c", b);
  write("c = %s", c);
}

二、数组

数组是一组固定大小且元素类型相同的数据集合,数组在内存上是连续的。

1. 数组的初始化和访问

数组的定义及初始化示例:

cpp 复制代码
on key 'a'
{
  int intArray[10]; // 数组的定义
  byte byteArray[3] = {0x01, 0x02, 0x03}; // 数组的就地初始化
  dword dwordArray[3] = {0x11223344, 0x55667788}; // 数组的部分初始化
}

数组可以根据索引/下标进行访问和赋值;

数组的访问和赋值示例:

cpp 复制代码
on key 'b'
{
  int intArray[2] = {1, 2};
  
  write("array[0] = %d", intArray[0]); // 根据下标进行访问
  intArray[1] = 10; // 根据下标进行修改
  write("array[1] = %d", intArray[1]);
}

2. 求数组的大小

CAPL 中可以使用 elCount() 函数返回数组的大小,如果是多维数组,返回最外层数组的大小;

elCount() 函数示例:

cpp 复制代码
on key 'c'
{
  int a[2] = {1, 2};
  int b[3][4] = 
  {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 0, 1, 2}
  };
  
  write("elCount(a) = %d", elCount(a));
  write("elCount(b) = %d", elCount(b));
}

一旦数组发生越界访问,CANoe 就会报错:invalid array range;

3. 数组作为参数传递

当数组作为形参时,函数内部更改形参数组的元素值,会同步更改函数体外面的数组;

可以类比 java 语言进行理解:

  • Java 语言中,引用类型作为形参传递时,函数内部更改引用类型变量指向的对象的值,会影响到外部的变量本身,基本类型则不受影响。
  • 基本类型作为形参传递时,会被复制一份,在复制的变量上进行修改,函数运行结束,复制的变量的生命周期结束,不影响外部的变量;
  • 引用类型作为参数传递的时候,虽然引用也会被复制一份,但是指向的对象还是原来的对象。函数内部针对变量进行修改时,如果修改的是堆上的内容,就会影响到外部的变量;如果修改的是引用的指向,则不会影响外部的变量;

函数内部更改形参数组的元素值,等价于修改堆上的对象,会影响到外部的变量。

实际上 CAPL 语言传数组传的是地址的别名,修改数组等同于修改内存地址中保存的内容;

示例代码:

cpp 复制代码
on key 'd'
{
  long i;
  int a[2] = {1, 2};
  
  write("调用函数前, 数组的值:");
  for(i = 0; i < elCount(a); i++)
    write("a[%ld] = %d", i, a[i]);
  
  function(a); // 调用函数
  
  write("调用函数后, 数组的值:");
  for(i = 0; i < elCount(a); i++)
    write("a[%ld] = %d", i, a[i]);
}

void function(int a[])
{
  long i;
  
  for(i = 0; i < elCount(a); i++)
    a[i] += 1;
}

4. 字符数组

字符数组的初始化示例:

cpp 复制代码
on key '1'
{
  char a[5] = {'h', 'e', 'l', 'l', 'o'}; // 就地初始化数组
  char b[6] = "hello";
  
  write("%s", a);
  write("%s", b);
}

二维字符数组的初始化示例:

cpp 复制代码
on key '2'
{
  long i;
  char fruit[3][200] = 
  {
    "Apple",
    "Orange",
    "Banana"
  };
  
  for(i = 0; i < elCount(fruit); i++)
    write("fruit[%d]: %s", i, fruit[i]);
}

求字符串的长度示例:

elCount() 函数是求数组的大小,strlen() 是求字符数组内字符串的长度;

cpp 复制代码
on key '3'
{
  char a[200] = "Apple";
  
  write("字符串的长度: %d", strlen(a));
}

字符串的格式化输出示例:

snprintf() 函数是 CAPL 中常用的格式化输出字符串的函数,语法和 write() 函数一样,最多支持 64 个参数,函数返回值是字符串的长度;

cpp 复制代码
on key '4'
{
  long size;
  char text[200];
  char sig[20] = "vhSpeed";
  double speed = 60.5;
  
  size = snprintf(text, elCount(text), "车速信号名: %s; 车速信号值: %.1f", sig, speed);
  write("return size = %d", size);
  write("text: %s", text);
}

注意:英文字符的长度是 1,中文字符的长度是 2;

字符数组赋值示例:

字符数组初始化的时候可以直接赋值,后续不能直接赋值。赋值必须使用 CAPL 内置的字符串函数 strncpy() 或者 snprintf();

cpp 复制代码
on key '5'
{
  char a[200] = "Apple";
  char b[200];
  
  strncpy(a, "orange", elCount(a)); // 赋值方式 1
  write("a = %s", a);
  
  snprintf(b, elCount(b), "orange"); // 赋值方式 2
  write("b = %s", b);
}

三、枚举类型

枚举类型是用户自定义的数据类型,通常使用枚举类型能提高代码的可读性和可维护性;

使用枚举类型的形参,更能限制用户调用函数时的输入,避免用户传非法值,代码更加安全;

1. 枚举类型的定义

枚举类型默认第一个成员的值为 0,也可以手动指定第一个成员的值,后续成员依此递增。

cpp 复制代码
on key '1'
{
  // 定义方式一
  enum DAY
  {
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
  };
  enum DAY day;
}
cpp 复制代码
on key '2'
{
  // 定义方式二
  enum DAY
  {
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
  } day;
}
cpp 复制代码
on key '3'
{
  // 定义方式三
  enum
  {
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
  } day;
}

2. 枚举类型的应用

枚举类型的最重要的作用就是能够提高代码的可读性和可维护性;

1. 作为常数使用

cpp 复制代码
on key '4'
{
  int value;
  enum Bool
  {
    kFalse,
    kTrue
  };
  
  if(value == kTrue)
    write("value = kTrue");
}

2. 搭配 switch-case 使用 和 作为函数形参

搭配 switch-case 使用,代码的可读性更强;

作为函数形参,能够限制用户传入的内容,只能是枚举类型定义的内容,使函数的调用更加安全;

cpp 复制代码
variables
{
  enum DAY
  {
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
  } day;
}

on key '5'
{
  enumTest(MON);
}

void enumTest(enum DAY day)
{
  switch(day)
  {
    case MON: write("Monday"); break;
    case TUE: write("Tuesday"); break;
    case WED: write("Wednesday"); break;
    case THU: write("Thursday"); break;
    case FRI: write("Friday"); break;
    case SAT: write("Saturday"); break;
    case SUN: write("Sunday"); break;
    default: write("Error");
  }
}

3. 作为函数返回值

cpp 复制代码
variables
{  
  enum Bool 
  {
    kFalse, kTrue
  };
}

enum Bool getStatus(int flag)
{
  if(flag)
    return kTrue;
  else
  return kFalse;
}

3. 枚举类型的语法拓展

CAPL 语言枚举类型语法和 C 一致;

枚举类型本质上也是整型数据,因此可以通过算术运算转换为整型:

cpp 复制代码
on key '6'
{
  int i;
  enum State
  {
    KEY_OFF, KEY_ON
  } state;
  
  state = KEY_OFF;
  i  = state + 1;
  write("i = %d", i);
}

Name() 方法:

可以通过 Name() 方法获取当前枚举变量的名称;

cpp 复制代码
on key '6'
{
  int i;
  enum State
  {
    KEY_OFF, KEY_ON
  } state;
  
  state = KEY_OFF;
  
  write("state 变量当前的状态为: %s", state.Name());
}

containsValue() 方法:

可以通过 containsValue() 方法判断,当前枚举变量中是否包含某个数值的成员,包含返回 1,不包含返回 0;

cpp 复制代码
on key '6'
{
  int i;
  enum State
  {
    KEY_OFF, KEY_ON
  } state;
  
  write("state 变量是否包含值为 1 的成员: %d", state.containsValue(1));
  write("state 变量是否包含值为 2 的成员: %d", state.containsValue(2));
}

四、结构体类型

结构体是一种复合数据类型,由一系列具有相同或者不同类型的数据构成的数据集合;

1. 结构体类型定义

代码示例:

cpp 复制代码
on key '1'
{
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  } stu1;
}
cpp 复制代码
on key '2'
{
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  };
  
  struct Student stu;
}

2. 结构体变量初始化

代码示例:

cpp 复制代码
on key '3'
{
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  };
  
  struct Student stu1 = 
  {
    "Caocao", 123, 'A'
  };
  
  struct Student stu2 = 
  {
    name = "Machao",
    id = 124,
    gender = 'A'
  };
}

3. 结构体变量成员访问与赋值

结构体通过访问运算符 "." 来访问结构体成员;

代码示例:

cpp 复制代码
on key '4'
{
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  } stu1;
  
  // 结构体成员变量赋值
  stu1.id = 123;
  stu1.gender = 'A';
  stu1.age = 20;
  
  strncpy(stu1.name, "Caocao", elCount(stu1.name));
  
  // 结构体成员变量访问
  write("stu1.name: %s", stu1.name);
  write("stu1.id: %d", stu1.id);
  write("stu1.gender: %c", stu1.gender);
  write("stu1.age: %d", stu1.age);
}

4. 结构体数组

结构体数组是多个相同类型的结构体元素组成的数组。在实际应用中,往往需要结构体数组保存一组数据。

示例代码:

cpp 复制代码
on key '5'
{
  long i;
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  };
  
  struct Student stu[3] = 
  {
    {"Caocao", 123, 'A'},
    {"Machao", 124, 'A'},
    {"Zhangfei", 125, 'A'}
  };
  
  for(i = 0; i < elCount(stu); i++)
    write("name: %s; id: %d; gneder: %c", stu[i].name, stu[i].id, stu[i].gender);
}

5. 结构体作为函数参数

结构体也可以作为函数的参数,和数组类型一样,在函数内部修改结构体的成员变量的值,函数外部的结构体变量的值也会发生变化。

因为函数内部结构体和外部变量结构体指向的内存地址是相同的。

代码示例:

cpp 复制代码
variables
{
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    int score;
  };
  
  struct Student stu = {"Caocao", 123, 'A'};
}

on key '6'
{
  write("调用函数前, 结构体成员 name = %s", stu.name);
  structTest(stu);
  write("调用函数后, 结构体成员 name = %s", stu.name);
}

void structTest(struct Student stu)
{
  strncpy(stu.name, "Zhangfei", elCount(stu.name));
}

6. 结构体嵌套

结构体嵌套要注意,先定义成员结构体类型,再定义结构体类型;

代码示例:

cpp 复制代码
on key '7'
{
  struct Score
  {
    int chinese;
    int math;
    int english;
  };
  
  struct Student
  {
    char name[20];
    int id;
    char gender;
    int age;
    struct Score score;
  } stu;
 
  strncpy(stu.name, "Caocao", elCount(stu.name));
  
  stu.score.chinese = 90;
  stu.score.math = 99;
}

7. 结构体字节对齐

计算机的内存空间是按照字节划分的。但是变量并不是按照顺序,逐个字节排列的。

变量是按照字节对齐的规则去排列的。

CAPL 字节对齐函数库:

__size_of(aligned-type):返回结构体按内存的字节数;

__alignment_of(aligned-type):返回结构体的对齐字节;

__offset_of(struct-type, member):返回结构体成员在内存中的偏移量;

字节对齐规则:

默认情况下,字节对齐由结构体中数据类型最大的成员决定;

如果结构体的后一个成员的字节比前一个成员的数据类型字节大,则后一个成员的内存地址的偏移量 = 前一个成员内存地址偏移量 + 后一个成员的字节大小;

如果结构体的后一个成员的字节比前一个成员的数据类型字节小,则后一个成员的内存地址的偏移量 = 前一个成员内存地址偏移量 + 前一个成员的字节大小;

结构体占内存的字节数 = 最后一个成员的额偏移量 + 最后一个成员的额字节大小;

代码示例:

cpp 复制代码
on key '8'
{
  struct LongPoint
  {
    byte x;
    qword y;
    byte z;
    int k;
  };
  
  write("结构体占内存字节数: %d", __size_of(struct LongPoint));
  write("结构体的字节对齐: %d", __alignment_of(struct LongPoint));
  write("结构体的元素 x 内存地址偏移量: %d", __offset_of(struct LongPoint, x));
  write("结构体的元素 y 内存地址偏移量: %d", __offset_of(struct LongPoint, y));
  write("结构体的元素 z 内存地址偏移量: %d", __offset_of(struct LongPoint, z));
  write("结构体的元素 k 内存地址偏移量: %d", __offset_of(struct LongPoint, k));
}

按照字节对齐的规则分析:

字节对齐的大小是可以通过 _align() 设置的,代码示例:

cpp 复制代码
on key '9'
{
  _align(2) struct LongPoint
  {
    byte x;
    qword y;
    byte z;
    int k;
  };
  
  write("结构体占内存字节数: %d", __size_of(struct LongPoint));
  write("结构体的字节对齐: %d", __alignment_of(struct LongPoint));
  write("结构体的元素 x 内存地址偏移量: %d", __offset_of(struct LongPoint, x));
  write("结构体的元素 y 内存地址偏移量: %d", __offset_of(struct LongPoint, y));
  write("结构体的元素 z 内存地址偏移量: %d", __offset_of(struct LongPoint, z));
  write("结构体的元素 k 内存地址偏移量: %d", __offset_of(struct LongPoint, k));
}

分别设置字节对齐数为 1,2,4:

8. 字节对齐应用

通过 memcpy_h2n() 函数将结构体数据赋值给 byte 数组;

cpp 复制代码
on key '0'
{
  long i;
  byte data[100];
  struct LongPoint
  {
    byte x;
    qword y;
    byte z;
    int k;
  };
  
  struct LongPoint test = 
  {
    x = 0x01,
    y = 0x1122334455667788LL,
    z = 0x55,
    k = 0x0102
  };
  
  memcpy_h2n(data, test);
  
  for(i = 0; i < elCount(test); i++)
    write("地址偏移量: %d; 值: 0x%x", i, data[i]);
}

总结

本文简单介绍了 CAPL 语言的数据类型,包括基本类型,数组,枚举和结构体,并通过代码示例演示数据类型的使用方法。

相关推荐
制冷技术咨询与服务11 小时前
新能源汽车线束的漏水问题
汽车·新能源汽车线束
杨福宇11 小时前
100BASE T1以太网实时控制的危机V3.21 ——辅助自动驾驶难达标
网络·网络协议·安全·自动驾驶·汽车
zhangduang_KHKW17 小时前
功能安全标准体系与工程边界
安全·汽车
电子科技圈18 小时前
基于车载SerDes和OpenGMSL协议的SoC设计与验证:行业高频问题解析
设计模式·车载系统·软件构建·设计规范
泛联新安2 天前
软件定义汽车时代,如何让AI研发“可信”?——泛联新安构建汽车企业级可信AI体系的落地路径
大数据·人工智能·安全·网络安全·汽车·漏洞挖掘·代码安全
田里的水稻2 天前
FA_融合和滤波(FF)-误差状态卡尔曼滤波(ESKF)
人工智能·机器学习·机器人·自动驾驶
QYR-分析2 天前
内燃机配套核心部件:汽车摇臂市场稳健增长,5.0% CAGR 背后的产业博弈
人工智能·汽车
大唐荣华2 天前
汽车供应链Tier层级全解析:Tier0.5 / Tier1 / Tier2国内外头部厂商一览
汽车·行业
CHENKONG_CK2 天前
RFID 赋能汽车零部件涂装产线,构建全流程数字化追溯体系
网络·人工智能·单片机·网络协议·tcp/ip·汽车