【问题标题】:Trying to copy char array to struct crashing试图将 char 数组复制到结构崩溃
【发布时间】:2016-09-27 20:41:55
【问题描述】:

(作为序言,我的 C 很糟糕)

我正在尝试将字符串从 iOS 发送到 BLE 设备。我用swift对字符串进行编码并这样写:

func sendUserName(userName: String) {
    let bytes: [UInt8] = Array(userName.utf8)

    print(bytes.count)

    let data = NSData(bytes: bytes, length: bytes.count)
    capsenseLedBoard!.writeValue(data, forCharacteristic: userIdCharacteristic, type: CBCharacteristicWriteType.WithResponse)
}

我发送这个字符串"THISISATEST123456789",这行print(bytes.count) 打印出20


我像这样在 BLE 设备上接收数据并将其传递给下面的 userDidConnect 函数:

userDidConnect((char *)wrReqParam->handleValPair.value.val);

我有一个名为Eventstruct,看起来像这样:

struct Event {
    char time[20];   // The time in ISO 1601 format
    char name[3];    // The two character name of the event. See header for declarations.
    char userId[20]; // The userId of the connected user if one is present.
    struct Event* next;
};

我有一个这样声明的全局变量:

char currentlyConnectedUserID[20];

然后我有一个如下所示的入队函数:

/**
 Creates a new Event and adds to the linked list.

 @param time   The time in ISO 8601 format.
 @param name   The name descriptor of the event ("VS", "VO", etc.)
 @param userId The id of the user who is currently connect (if they are connected).
 */
    void enqueueEvent(char time[20], char name[3], char userId[20]) {

    struct Event* temp = (struct Event*)malloc(sizeof(struct Event));
    strncpy( temp->time,   time, 20);
    strncpy( temp->name,   name, 3);
    strncpy( temp->userId, userId, 20);
    temp->next = NULL;

    if(front == NULL && rear == NULL) {
        front = rear = temp;
        return;
    }
    rear->next = temp;
    rear = temp;
}

我有一个函数,它接受一个新的 userId,然后创建一个新的 Event 并将其添加到链表中。这就是它应该做的:

void userDidConnect(char *userId)
{
    size_t destination_size = sizeof(userId);
    snprintf(currentlyConnectedUserID, destination_size, "%s", userId);
    //enqueueEvent("2007-03-01T13:00:20", "UC", currentlyConnectedUserID);

    showMessageInUART(currentlyConnectedUserID, sizeof(currentlyConnectedUserID));
}

目前,如果我运行上面的userDidConnect 方法,我可以正确打印出currentlyConnectedUserID。但是,如果我取消注释掉这一行:

//enqueueEvent("2007-03-01T13:00:20", "UC", currentlyConnectedUserID);

我遇到“崩溃”。我在一个相当晦涩的 IDE(赛普拉斯的 PSoC Creator)中执行此操作,因此我看不到任何错误日志或 IDE 崩溃日志。我能说的唯一方法是永远不会调用showMessageInUART,所以我知道它必须是那条线。

如果我这样做,我可以成功创建一个新事件并将其加入队列:

enqueueEvent("2007-03-01T13:00:20", "UC", "1234567891234567891"); 

我唯一的想法是数组的大小可能是错误的?也许?或者也许有一些尾随的\0 把事情搞砸了?


建议更新:

我试过这样做:

size_t destination_size = strlen(userId) + 1;

这为currentlyConnectedUserID 提供了正确的值,但是排队仍然会导致崩溃。

--

我已将strcpy 替换为strncpy,这仍然会导致崩溃;(

--

试过这个以确保我没有溢出但仍然没有工作:

sprintf(currentlyConnectedUserID, "%.19s", userId);

更新

由于没有断点,我将队列更新为如下所示:

void enqueueEvent(char time[20], char name[3], char userId[20]) {

    UART_UartPutString("start enqueue");

    struct Event* temp = (struct Event*)malloc(sizeof(struct Event));

    UART_UartPutString("1");

    strncpy( temp->time,   time, 20);

    UART_UartPutString("2");

    strncpy( temp->name,   name, 3);

    UART_UartPutString("3");

    strncpy( temp->userId, userId, 20);

    UART_UartPutString("4");

    temp->next = NULL;

    UART_UartPutString("5");

    if(front == NULL && rear == NULL) {
        front = rear = temp;
        return;
    }
    rear->next = temp;
    rear = temp;
}

这条线崩溃了:

strncpy( temp->time, time, 20);

我们从来没有在这里成功过:UART_UartPutString("2");

如果我从 main 调用相同的函数,它工作正常。知道为什么从不同的方法调用它会在这里崩溃吗?

【问题讨论】:

  • 我怀疑userDidConnect 没有做你想做的事。 userId 是一个指针,所以sizeof(userId) 将在 32 位机器上返回 4,在 64 位机器上返回 8。如果userId 指向一个字符串,我敢打赌你想要的是字符串长度。
  • @yano 我试过用strlen 替换sizeof 仍然可以正确转换为currentlyConnectedUserID 但是排队仍然崩溃:(
  • 然后 sizeof(userId) + 1 将根据您的架构返回 5 或 9。我的猜测是你有字符串问题,但这里没有足够的代码可以确定。你要向userDidConnect 传递什么?
  • 任何时候你在 C 中覆盖一个缓冲区,你都会执行 未定义的行为,这意味着在那之后所有的赌注都被取消了。你可能会很幸运,事情会奏效,它可能会在看似随机的地方崩溃。仅仅因为事情在enqueueEvent 中崩溃并不一定意味着这就是错误所在。这可能只是暴露了之前的错误。
  • 根据您传递给enqueueEvent 的内容,我认为没有问题。您正在传递长度为 19、2、19 的字符串,这些字符串将适合 20、3、20(包括空字节)的缓冲区。这就是为什么我认为真正的问题在此之前的某个地方。此外,在enqueueEvent 中,确保malloc 没有返回NULL。如果是这样,那也会导致崩溃。也不知道frontrear 是什么。这里根本没有足够的代码来知道问题是什么,因此我的所有猜测。

标签: c arrays swift struct bluetooth-lowenergy


【解决方案1】:

strcpy 函数将源指向的 C 字符串复制到目标指向的数组中,包括终止空字符(并在该点停止)。 因此,我建议您更改enqueueEvent 功能,使用strncpy 而不是危险的strcpy 为:

void enqueueEvent(char time[20], char name[3], char userId[20]) {

    struct Event* temp = (struct Event*)malloc(sizeof(struct Event));
    strncpy( temp->time,   time,20);
    strncpy( temp->name,   name,3);
    strncpy( temp->userId, userId,20);
    temp->next = NULL;

    if(front == NULL && rear == NULL) {
        front = rear = temp;
        return;
    }
    rear->next = temp;
    rear = temp;
}

还将temp 指针的分配从enqueueEvent 函数的本地堆栈更改为全局级别,因为指针分配在离开函数时消失了。

【讨论】:

  • 我是否也应该对我的问题的第一条评论进行建议更改?
  • 小心strncpy。它不保证复制终止的'\0',因此虽然在复制过程中它可能不会溢出缓冲区,但如果它们没有空终止符,您可能会在访问/修改这些字符串的过程中遇到麻烦
  • 另一点要调查的是 currentlyConnectedUserID 值:你怎么确定它小于 20 个字符长度(包括 \0)?
  • @P.Bra 我正在将一个 19 个字符的字符串转换为 char *,然后传递给 userDidConnect 函数。我也试过,18个字符,20个字符。我使用的 IDE 很糟糕,没有真正简单的方法来获取它的长度:(
  • @random 我认为您的问题可能来自frontrear 指针分配,这些指针分配在函数enqueueEvent 的本地堆栈上进行,然后在您外出时消失的功能。我建议你把你设计的所有与机制链接指针相关的代码都注释掉,然后重新测试。
猜你喜欢
  • 2014-12-13
  • 2013-07-19
  • 2013-05-15
  • 2020-01-04
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多