使用过Python的数据类型后,会发现C/C++
的数据类型比较单一。Arduino实际上是C++
,除了基础数据类型,增加了String类。通过了解标准库和类的头文件,可以减少不必要的、重复的、琐碎的、易错的手工代码。除了Arduino,其他的GCC和嵌入式C语言中也可以参照执行。
IoT相关数据类型
- byte, unsigned char
- char
- int, int and unsigned int
- float
- String,class of char array
- struct
IoT需要解决的主要是采集、传输、判断、控制。传输通道上数据以二进制数据为主。
C语言设计中,二进制数据采用unsigned char[],使用简单。二进制处理采用struct结构体可以很好地解决固定长度的unsigned char[]。但是动态长度的传输协议的结构体往往需要包含额外的缓冲区指针。
C语言中,字符串采用char[],即字符数组来实现。但是无论是初始化、转换、处理起来,代码都很琐碎。所以ANSI标准的C语言标准库中提供了大量的面向字符以及字符串的函数。在Java/C++等OOP语言中,也都定义String类。注意,这些是类(class),而非数据类型(type)。
由于IoT引入了许多Web协议,而大多数Web协议如HTTP/FTP/Telnet都基于字符串。所以许多情况下,代码需要在这两种协议之间进行转换。所以unsigned char和char是最基础的数据类型。
unsigned char和char可以通过强制类型转换来实现。但是unsigned char[],char[],String三者之间却有着一定的差异,主要因为char[]/String定义的字符串结束符必须使用NULL,即\0来实现。在某些特殊情况下会因为两种数据类型长度不一致,或者缺乏NULL结束符导致程序跑飞。
String类
在Arduino的WString.h/WString.cpp中可以找到对应的String类定义。
inline void String::init(void) {
buffer = NULL;
capacity = 0;
len = 0;
}
WString.cpp引用了stdlib_noiso.h这个头文件,也就是底层依然调用了libc来实现一些功能。在构建函数中,可以看到其调用utoa(),itoa(),ltoa()等stdlib.h中定义的函数。
类型转换
虽然C语言是理工科大学必修课程,但是在MCU级别使用C语言却是在工作中。从Keil的C51语言开始,因为资源受限,实际工程中很少使用标准库,一些小的常用函数都是自己写的,目的是通过不同模块的缓冲区复用(overlap)实现少占用RAM空间。而现在STM32/ESP8266的RAM都比之前8051 256B要多许多,使用标准库和类库以实现标准化开发,可以明显加快开发。
当然,不必将所有的库和类库都背下来,可以在使用中摸索出操作的常用函数,然后在标准库和类库中去寻找对应物。
- String to char array
- String to byte array
- String to int
- String to float
各种库
不同C编译器环境有着类似(ANSI C libc)但是有细微差异的库(glibc)。需要自行检索。
- stdlib.h,libc的一部分,包含了部分str转其他类型的函数定义。
- ctypes.h, libc的一部分,包含了字符char的函数定义。
- string.h, libc的一部分,包含了大多数字符串操纵如复制、检索、比较等函数定义。
- Wstring.h,Arduino自定义的String类,依赖于stdlib.h/string.h的函数。
- Wcharater.h,Arduino定义的字符类型操作,如判断、转换。依赖于ctype.h的转换函数。
#include
// string to double or float
double atof(const char *s);
float atoff(const char *s);
// string to integer
int atoi(const char *s); // = (int)strtol(s, NULL, 10);
long atol(const char *s); // = strtol(s, NULL, 10);
// double or float to string
char *ecvt(double val, int chars, int *decpt, int *sgn);
char *ecvtf(float val, int chars, int *decpt, int *sgn);
char *fcvt(double val, int decimals, int *decpt, int *sgn);
char *fcvtf(float val, int decimals, int *decpt, int *sgn);
// format double or float as string
char *gcvt(double val, int precision, char* buf);
char *gcvtf(float val, int precision, char* buf);
// double or float to string
char *ecvtbuf(double val, int chars, int *decpt, int *sgn, char *buf);
char *ecvtfbuf(float val, int chars, int *decpt, int *sgn, char *buf);
// integer or unsigned int to string
char *itoa(int value, char *str, int base);
char *utoa(unsigned value, char *str, int base);
// string to double or float
double strtod(const char *restrict str, char **restrict tail);
float strtof(const char *restrict str, char **restrict tail);
// string to long or unsigned long
long strtol(const char *restrict s, char **restrict ptr, int base);
unsigned long strtoul(const char *restrict s, char **restrict ptr, int base);
// binary search
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
#include
int isalnum(int c); // is alphanumberic
int isalpha(int c); // is alphabetic
int isascii(int c); // is ASCII char
int isblank(int c); //
int iscntrl(int c);
int isdigit(int c);
int islower(int c);
int isprint(int c); // is printable
int ispunct(int c); // punctuation
int isspace(int c); // is space
int isupper(int c);
int isxdigit(int c); // is hexdecimal digit
int toascii(int c); // force to ascii, by clearing MSB
int tolower(int c);
int toupper(int c);
#include
int bcmp(const void *s1, const void *s2, size_t n); // = memcmp
int bcopy(const void *in, void *out, size_t n); // = memmove
void bzero(void *b, size_t length);
char *index(const char *string, int c); // = strchr
void *memccpy(void *restrict out, const void *restrict in, int endchar, size_t n);
void *memchr(const void *src, int c, size_t length);
int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *restrict out, const void *restrict in, size_t n);
char *memmem(const void *s1, size_t l1, const void *s2, size_t l2);
void *memmove(void *dst, void *src, size_t length);
void *memrchr(const void *src, int c, size_t length);
void *memset(void *dst, int c, size_t length);
void *rawmemchr(const void *src, int c);
void *rindex(const char * string, int c);
char *strpcpy(char *restrict dst, const char *restrict src);
char *strncpy(char *restrict dst, const char *restrict src, size_t length);
int strcasecmp(const char *a, const char *b);
char *strcasestr(const char *s, const char *find);
char *strcat(char *restrict dst, const char *restrict src);
char *strchr(const char *string, int c);
char *strchrnul(const char *string, int c);
int strcmp(const char *a, const char *b);
char *strcpy(char *dst, char *src);
size_t strlen(const char *str);
char *strlwr(char *a);
int strncasecmp(const char *a, const char *b, size_t length);
char *strncat(char *restrict dst, const char *restrict src, size_t length);
int strncmp(const char *a, const char *b, size_t length);
char *strncpy(char *restrict dst, const char *restrict src, size_t length);
size_t strlen(const char *str, size_t length);
char *strpbrk(const char *s1, const char *s1);
char *strrchr(const char *string, int c);
char *strstr(const char *s1, const char *s2);
char *strtok(char *restrict source, const char *restrict delimiters);
仔细阅读Arduino的WString/WCharater的头文件,其底层依然是依赖于标准库libc。
其他操作
Python编写IoT应用时,有些特殊的操作,是没有C语言对应物的,都需要自己去编写。比如split()切割字符串等。
Web URLs
- http://www.runoob.com/cprogramming/c-standard-library-stdlib-h.html
- http://www.runoob.com/cprogramming/c-standard-library-string-h.html
- libc、glibc和glib的关系