static和头文件,源文件放什么

本文详细解析C/C++中static关键字的多种用途,包括局部静态变量、全局静态变量、静态成员函数及数据成员的特点与应用场景。阐述了static如何影响变量的存储空间分配、初始化、赋值以及函数的作用域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

头文件or源文件

1、局部静态变量

2、全局静态变量/普通的全局静态函数

3、静态数据成员、成员函数(面向对象)


头文件or源文件

a)头文件应该放:

                          1)普通的全局函数声明

                          2)普通的全局变量声明 (必须是 extern int i 这种方式   //用来声明在另一个编译单元有i变量 ) 

                          3) 类的声明+定义(tips1:类的实现,即成员函数(不管是普通的还是静态的)放在源文件中,tips2:写在类的定义里面的函数成员的定义是隐式内联的,所以可以放在头文件里)

                          4) 模板类和模板函数的实现,即所有的一切,包括成员函数的定义,包括静态数据成员的初始化(好像是因为实例化发生在编译阶段,所以每个实例化这个模板的cpp文件都需要知道模板的实现)

                          5) inline函数的定义   ( 加了inline并不一定,inline最大的用处是:非template 函数,不管是成员或非成员函数,假如把定义放在头文件中,而且定义前不加inline ,然后如果头文件被多个translation unit(cpp文件)引用,编译器会报错multiple definition。)

b)源文件应该放:

                          1)普通的全局函数定义

                          2)普通的全局变量定义 

                          3) 类的实现(包括函数成员的定义,静态数据成员的初始化(定义时,不加static))

                          4) 普通的static函数和普通的全局static变量的定义(也可以放在头文件中,但不推荐)

static总结

static 关键字在C/C++中可以修饰局部变量、全局变量,函数——面向过程程序设计,C++中还可以修饰成员函数和成员变量——面向对象程序设计。我们对这几种类型进行分析,来了解static的用途和特性。

1、局部静态变量

在C/C++中,局部变量按照存储形式可以分为以下三种:auto、static、register。默认是auto,与auto类型的局部变量相比,static局部变量有以下三点不同:

(1)存储空间分配

auto类型(普通)局部变量分配于上,属于动态存储类型,函数调用完成之后会存储空间自动释放。而static局部变量分配于全局数据区(初始化分配于data区,未初始化或者初始化为0,则分配于BSS区),函数调用完成之后不释放。

总结:静态局部变量和普通局部变量的作用域还是一样的,都是针对局部使用,在函数外不能使用,但是生存期不同,静态局部变量的生存期为整个程序运行期间。

(2)初始化

static局部变量只初始化一次,即在声明该变量的函数第一次运行时。所以局部静态变量只会初始化一次

(3)赋值

static局部变量如果未初始化初值(未初始化全局变量和未初始化静态变量都存放于BSS区),编译期间会自动初始化为0或空指针。而auto类型的局部变量的初值是不确定的(针对内置类型而言)。JAVA中局部变量不初始化是不可以使用的(编译出错),C/C++虽然可以编译通过,但是其值未知。

特点:static局部变量的记忆性和生存期的全局性

在多线程中对静态局部变量的访问要注意线程安全问题。

2、全局静态变量/普通的全局静态函数

1)static 和 普通的全局变量(函数)区别:

static修饰全局变量或函数时,表示本地性。就是不能被其他cpp文件访问和使用的全局变量或函数。

也就是说,static全局变量在定义它的整个文件都是可见的,而在文件之外是不可见的;而static函数表示该函数只能在本源文件进行调用,其他文件或模块不能调用。

所以普通的全局静态函数与普通函数的区别是:用static修饰的函数,限定在本源码文件中,不能被本cpp文件以外的cpp文件调用。而普通的函数,默认是extern的,也就是说它可以被其它代码文件调用。(全局静态变量和普通全局变量同理)

总结:所以这里的static的作用只是限定了作用域——仅限于本文件注意,对于全局变量而言,无论是否有static修饰,其存储区域都是全局数据区(静态存储区,data区和bss区),生命周期都是全局的。

2)作用

这样有个作用,模块开发时,不同源文件下的static函数(全局变量)可以同名。
3)头文件or源文件

普通的static函数和普通的全局static变量的定义,应该放在源文件中,并且不应该在头文件中有声明。

假如想实现在多个cpp中复用该函数和变量,那可以把定义放在头文件中
 

3、静态数据成员、成员函数(面向对象)

用static修饰类中的成员变量或成员函数,表示该变量或者函数是属于类的,而不是属于该类的实例对象的,是被该类的所有对象所共享使用的。指示了变量和函数对于此类的唯一性,可以用类名直接引用。

对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。

静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是属于某个类的具体对象的。通常情况下,this是缺省的。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。所以,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。

同样,类的静态成员定义(初始化)应该放在源文件中

<think>我们正在基于RT-Thread实现ATGM336H GPS模块的驱动解析。根据用户要求,我们将提供完整的代码示例,包括头文件源文件。 我们将按照以下结构组织代码: 1. 头文件(gps_atgm336h.h):包含函数声明、结构体定义等。 2. 源文件(gps_atgm336h.c):包含具体的实现代码,包括初始化、线程创建、数据解析等。 注意:我们参考了之前提供的实现指南,并考虑用户提到的引用内容(如UART配置、FAL抽象层等)可能涉及的问题,但当前问题主要关注GPS模块的完整代码示例。 我们将实现以下功能: - 初始化UART设备(这里假设使用uart3) - 创建线程用于读取解析GPS数据 - 解析NMEA数据(以GPRMC语句为例) - 提供设置波特率、启用多卫星系统的示例 注意:由于RT-Thread的配置可能因具体硬件BSP不同,以下代码需要根据实际情况调整(如UART设备名称、线程栈大小等)。 头文件:gps_atgm336h.h</think>### 基于RT-Thread的ATGM336H GPS模块完整代码示例 #### 头文件 `gps_atgm336h.h` ```c #ifndef __GPS_ATGM336H_H__ #define __GPS_ATGM336H_H__ #include <rtthread.h> #include <rtdevice.h> /* GPS数据结构体 */ struct gps_data { double latitude; // 纬度 double longitude; // 经度 rt_uint8_t hour; // UTC小时 rt_uint8_t minute; // UTC分钟 rt_uint8_t second; // UTC秒 rt_bool_t valid; // 数据有效性标志 }; /* 函数声明 */ rt_err_t gps_init(rt_device_t uart_dev); void gps_start(void); struct gps_data gps_get_data(void); void gps_set_baudrate(rt_uint32_t baud); void gps_enable_glonass(rt_bool_t enable); #endif /* __GPS_ATGM336H_H__ */ ``` #### 源文件 `gps_atgm336h.c` ```c #include "gps_atgm336h.h" #include <string.h> #include <stdlib.h> #define GPS_BUFFER_SIZE 128 #define GPS_THREAD_STACK_SIZE 1024 #define GPS_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2) #define GPS_THREAD_TICK 20 static rt_device_t g_gps_dev = RT_NULL; static struct gps_data g_current_data = {0}; static rt_mutex_t g_data_mutex = RT_NULL; /* 解析GPRMC语句 */ static void parse_gprmc(const char *data) { char *token = strtok((char *)data, ","); int field = 0; double lat = 0.0, lon = 0.0; rt_uint8_t hour = 0, min = 0, sec = 0; char status = 'V'; while (token != NULL) { switch (field++) { case 1: // UTC时间 HHMMSS.ss if (strlen(token) >= 6) { hour = atoi(token); min = atoi(token + 2); sec = atoi(token + 4); } break; case 2: // 状态 A=有效, V=无效 status = *token; break; case 3: // 纬度 ddmm.mmmm lat = atof(token); break; case 4: // 纬度方向 N/S if (*token == 'S') lat = -lat; break; case 5: // 经度 dddmm.mmmm lon = atof(token); break; case 6: // 经度方向 E/W if (*token == 'W') lon = -lon; break; } token = strtok(NULL, ","); } // 转换为十进制度数 lat = (int)(lat / 100) + fmod(lat, 100) / 60.0; lon = (int)(lon / 100) + fmod(lon, 100) / 60.0; // 更新数据(加锁保护) rt_mutex_take(g_data_mutex, RT_WAITING_FOREVER); g_current_data.latitude = lat; g_current_data.longitude = lon; g_current_data.hour = hour; g_current_data.minute = min; g_current_data.second = sec; g_current_data.valid = (status == 'A') ? RT_TRUE : RT_FALSE; rt_mutex_release(g_data_mutex); } /* GPS数据解析线程入口 */ static void gps_parser_thread_entry(void *parameter) { char buffer[GPS_BUFFER_SIZE]; while (1) { rt_size_t len = rt_device_read(g_gps_dev, 0, buffer, sizeof(buffer) - 1); if (len > 0) { buffer[len] = '\0'; // 检查并解析GPRMC语句 char *start = strstr(buffer, "$GPRMC"); if (start != NULL) { char *end = strchr(start, '*'); if (end != NULL) { // 提取完整语句 char nmea[82] = {0}; rt_size_t nmea_len = end - start + 1; if (nmea_len < sizeof(nmea)) { strncpy(nmea, start, nmea_len); parse_gprmc(nmea); } } } } rt_thread_mdelay(100); } } /* GPS初始化函数 */ rt_err_t gps_init(rt_device_t uart_dev) { if (uart_dev == RT_NULL) { rt_kprintf("GPS init failed: invalid UART device\n"); return -RT_ERROR; } // 配置UART参数 struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; config.baud_rate = BAUD_RATE_9600; config.data_bits = DATA_BITS_8; config.stop_bits = STOP_BITS_1; config.parity = PARITY_NONE; if (rt_device_control(uart_dev, RT_DEVICE_CTRL_CONFIG, &config) != RT_EOK) { rt_kprintf("GPS UART config failed\n"); return -RT_ERROR; } // 打开UART设备 if (rt_device_open(uart_dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK) { rt_kprintf("GPS UART open failed\n"); return -RT_ERROR; } g_gps_dev = uart_dev; // 创建数据互斥锁 g_data_mutex = rt_mutex_create("gps_mutex", RT_IPC_FLAG_FIFO); if (g_data_mutex == RT_NULL) { rt_kprintf("GPS mutex create failed\n"); return -RT_ERROR; } return RT_EOK; } /* 启动GPS线程 */ void gps_start(void) { if (g_gps_dev == RT_NULL) { rt_kprintf("GPS device not initialized\n"); return; } // 创建GPS解析线程 rt_thread_t tid = rt_thread_create("gps_parser", gps_parser_thread_entry, RT_NULL, GPS_THREAD_STACK_SIZE, GPS_THREAD_PRIORITY, GPS_THREAD_TICK); if (tid != RT_NULL) { rt_thread_startup(tid); rt_kprintf("GPS thread started\n"); } else { rt_kprintf("GPS thread create failed\n"); } } /* 获取最新GPS数据 */ struct gps_data gps_get_data(void) { struct gps_data data; rt_mutex_take(g_data_mutex, RT_WAITING_FOREVER); data = g_current_data; rt_mutex_release(g_data_mutex); return data; } /* 设置波特率 */ void gps_set_baudrate(rt_uint32_t baud) { if (g_gps_dev == RT_NULL) return; struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; config.baud_rate = baud; if (rt_device_control(g_gps_dev, RT_DEVICE_CTRL_CONFIG, &config) == RT_EOK) { rt_kprintf("GPS baudrate set to %d\n", baud); } } /* 启用GLONASS支持 */ void gps_enable_glonass(rt_bool_t enable) { if (g_gps_dev == RT_NULL) return; const char *cmd = enable ? "$PCAS04,1*1E\r\n" : "$PCAS04,0*1D\r\n"; rt_device_write(g_gps_dev, 0, cmd, strlen(cmd)); rt_kprintf("GLONASS %s\n", enable ? "enabled" : "disabled"); } ``` #### 使用示例 `main.c` ```c #include <rtthread.h> #include "gps_atgm336h.h" int main(void) { // 查找UART设备 rt_device_t uart_dev = rt_device_find("uart3"); if (uart_dev == RT_NULL) { rt_kprintf("UART3 device not found!\n"); return -1; } // 初始化GPS if (gps_init(uart_dev) != RT_EOK) { return -1; } // 启用GLONASS支持 gps_enable_glonass(RT_TRUE); // 启动GPS线程 gps_start(); while (1) { // 获取GPS数据 struct gps_data data = gps_get_data(); if (data.valid) { rt_kprintf("[GPS] Time: %02d:%02d:%02d | Position: %.6f, %.6f\n", data.hour, data.minute, data.second, data.latitude, data.longitude); } else { rt_kprintf("[GPS] No valid fix\n"); } rt_thread_mdelay(5000); // 每5秒打印一次 } return 0; } ``` #### 关键配置说明 1. **RT-Thread配置** 在`rtconfig.h`中确保启用UART驱动互斥锁支持: ```c #define RT_USING_DEVICE #define RT_USING_SERIAL #define RT_USING_MUTEX ``` 2. **Kconfig配置** 在`Kconfig`文件中添加GPS组件配置: ```kconfig menu "GPS Configuration" config GPS_DEVICE_NAME string "GPS UART device name" default "uart3" config GPS_THREAD_PRIORITY int "GPS thread priority" default 20 endmenu ``` 3. **SConscript构建配置** 在`SConscript`中添加源文件: ```python from building import * cwd = GetCurrentDir() src = Glob('*.c') + ['gps_atgm336h.c'] objs = DefineGroup('gps_driver', src, depend = [''], CPPPATH = [cwd]) Return('objs') ``` #### 调试技巧 1. 查看原始NMEA数据: ```c // 在gps_parser_thread_entry中添加 rt_kprintf("Raw: %s\n", buffer); ``` 2. 使用Finsh命令测试: ```bash msh > list_device msh > gps_enable_glonass 1 msh > gps_set_baudrate 115200 ``` 3. 使用ulog日志框架: ```c #include <ulog.h> #define LOG_TAG "GPS" LOG_D("Position: %.6f, %.6f", lat, lon); ``` > 完整项目参考:[RT-Thread GPS驱动示例](https://github.com/RT-Thread-packages/gps)[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值