You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ SerialPort库char*赋值疑问及用户指定端口实现咨询

C++串口通信程序端口赋值问题解答

问题背景

参考YouTube教程使用第三方SerialPort库开发C++串口通信程序,实现消息发送与响应监听。教程中直接用char* port = "\\\\.\\COM10";初始化SerialPort对象可正常运行,但尝试让用户输入端口号,通过std::string拼接后赋值给char*时,VS Studio提示“无法从string转换为char*”错误。有两个疑问:

  1. 为何直接将字符串字面量赋值给char*可行?
  2. 如何实现让用户指定端口并赋值给char*以初始化串口对象?

用户原始代码

#include<iostream>
#include<string>
#include<stdlib.h>

using namespace std;
#include"SerialPort.h"

char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];
char* port;

int main() {
    string data;
    string portone;
    cout << "输入端口号: " << endl;
    cin >> portone;
    string porttwo = "\\\\.\\COM" + portone;
    port = porttwo;
    SerialPort arduino(port);
    if (arduino.isConnected()) {
        cout << "已连接" << endl << endl;
    }
    else {
        cout << "端口未占用" << endl << endl;
    }
    if (arduino.isConnected()) {
        cout << "输入发送内容: " << endl;
        cin >> data;
        char* charArray = new char[data.size() + 1];
        copy(data.begin(), data.end(), charArray);
        charArray[data.size()] = '\n';
        arduino.writeSerialPort(charArray, MAX_DATA_LENGTH);
        arduino.readSerialPort(output, MAX_DATA_LENGTH);

        cout << "响应内容: " << output << endl;

        delete[] charArray;
    }
    cout << "程序结束" << endl << endl;
    system("PAUSE");
    return 0;
}

SerialPort类实现代码

#include "SerialPort.h"

SerialPort::SerialPort(char *portName)
{
    this->connected = false;

    this->handler = CreateFileA(static_cast<LPCSTR>(portName),
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (this->handler == INVALID_HANDLE_VALUE){
        if (GetLastError() == ERROR_FILE_NOT_FOUND){
            printf("ERROR: 句柄未关联,原因: %s 不可用\n", portName);
        }
    else
        {
            printf("ERROR!!!");
        }
    }
    else {
        DCB dcbSerialParameters = {0};

        if (!GetCommState(this->handler, &dcbSerialParameters)) {
            printf("获取当前串口参数失败");
        }
        else {
            dcbSerialParameters.BaudRate = CBR_9600;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(handler, &dcbSerialParameters))
            {
                printf("警告: 无法设置串口参数\n");
            }
            else {
                this->connected = true;
                PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
}

SerialPort::~SerialPort()
{
    if (this->connected){
        this->connected = false;
        CloseHandle(this->handler);
    }
}

int SerialPort::readSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesRead;
    unsigned int toRead = 0;

    ClearCommError(this->handler, &this->errors, &this->status);

    if (this->status.cbInQue > 0){
        if (this->status.cbInQue > buf_size){
            toRead = buf_size;
        }
        else toRead = this->status.cbInQue;
    }

    if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;

    return 0;
}

bool SerialPort::writeSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesSend;

    if (!WriteFile(this->handler, (void*) buffer, buf_size, &bytesSend, 0)){
        ClearCommError(this->handler, &this->errors, &this->status);
        return false;
    }
    else return true;
}

bool SerialPort::isConnected()
{
    return this->connected;
}

问题解答

1. 为何字符串字面量可直接赋值给char*?

字符串字面量(比如"\\\\.\\COM10")在C中是**const char[]**类型,早期C标准允许将其隐式转换为char*(为兼容旧代码),但这是不安全的——因为字符串字面量存储在只读内存区,修改它会导致未定义行为。现代C标准(C11及以后)已经弃用了这种隐式转换,不过部分编译器(比如VS)为了兼容旧代码仍会允许,但会给出警告。

而std::string是一个C++类,它的内部存储是动态分配的,无法直接将std::string对象赋值给char*,因为两者类型不匹配,没有隐式转换规则支持这种操作。

2. 实现用户指定端口的正确方式

有两种常用的解决方案:

方案一:使用std::string的c_str()或data()方法

std::string提供了c_str()和data()方法(C++11后两者功能一致),返回指向内部字符数组的const char*指针。由于SerialPort构造函数接收的是char*,而CreateFileA实际接收的是LPCSTR(即const char*),可以用const_cast<char*>临时转换(注意:不要修改转换后的指针指向的内容)。

修改后的main函数关键部分:

int main() {
    string data;
    string portone;
    cout << "输入端口号: " << endl;
    cin >> portone;
    string porttwo = "\\\\.\\COM" + portone;
    // 使用c_str()获取const char*,并用const_cast转换为char*
    SerialPort arduino(const_cast<char*>(porttwo.c_str()));
    // ... 后续代码不变
}

方案二:修改SerialPort构造函数参数为const char*

更推荐的做法是修改SerialPort类的构造函数,将参数改为const char*,因为串口端口名不需要修改,这样更符合const正确性:

修改SerialPort.h中的构造函数声明:

class SerialPort {
    // ... 其他成员
public:
    SerialPort(const char *portName); // 改为const char*
    // ... 其他成员
};

然后修改SerialPort.cpp中的构造函数实现:

SerialPort::SerialPort(const char *portName)
{
    this->connected = false;

    this->handler = CreateFileA(portName, // 无需static_cast,因为portName已经是const char*
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    // ... 后续代码不变
}

此时main函数中直接使用porttwo.c_str()即可:

SerialPort arduino(porttwo.c_str());

完整修改后的main函数代码

#include<iostream>
#include<string>
#include<stdlib.h>

using namespace std;
#include"SerialPort.h"

char output[MAX_DATA_LENGTH];
char incomingData[MAX_DATA_LENGTH];

int main() {
    string data;
    string portone;
    cout << "输入端口号: " << endl;
    cin >> portone;
    string porttwo = "\\\\.\\COM" + portone;
    // 假设SerialPort构造函数已改为const char*参数
    SerialPort arduino(porttwo.c_str());
    if (arduino.isConnected()) {
        cout << "已连接" << endl << endl;
    }
    else {
        cout << "端口不可用" << endl << endl;
    }
    if (arduino.isConnected()) {
        cout << "输入发送内容: " << endl;
        cin >> data;
        char* charArray = new char[data.size() + 2]; // 多留一个位置存'\n'和'\0'
        copy(data.begin(), data.end(), charArray);
        charArray[data.size()] = '\n';
        charArray[data.size() + 1] = '\0'; // 确保字符串以'\0'结尾
        arduino.writeSerialPort(charArray, data.size() + 1); // 只发送有效长度,而非MAX_DATA_LENGTH
        int bytesRead = arduino.readSerialPort(output, MAX_DATA_LENGTH);
        output[bytesRead] = '\0'; // 手动添加结束符,避免乱码

        cout << "响应内容: " << output << endl;

        delete[] charArray;
    }
    cout << "程序结束" << endl << endl;
    system("PAUSE");
    return 0;
}

内容的提问来源于stack exchange,提问作者CheeseGoblin44

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:36:27