C语言中操作字符数组指针的困惑

3

我写了一些操作指向char数组的代码,用于从NMEA RMC句子中格式化时间和日期,以在Arduino Pro Mini上使用,类似于以下代码:

char *UTC = "120435";
char *DATE = "050117";

char TIME[9];
char *ptr = TIME;
char *fieldPtr = UTC;
for (int a = 0; a < 8; a++) {
    *ptr++ = *fieldPtr++;
    if (a == 1 || a == 3) {
        *ptr = ':';
        ptr++;
    }
}
*ptr = '\0';
Serial.print("TIME: ");   
Serial.println(TIME);     //output: "12:04:35"  //-OK

char date[9];
ptr = date;
fieldPtr = DATE;
for (int a = 0; a < 8; a++) {
    *ptr++ = *fieldPtr++;
    if (a == 1 || a == 3) {
        *ptr = '.';
        ptr++;
    }
}
*ptr = '\0';
Serial.print("TIME: ");
Serial.println(TIME);        //output: "d"  //whatever follows DATE
Serial.print("date: ");
Serial.println(date);        //output: "05.01.17"  //-OK

第二轮时间输出似乎是跟在日期后面的一些字符,存储在内存中。

请问有谁能帮我解释一下这是怎么回事吗?


a < 8 Overrun is occurring. --> a < 6 - BLUEPIXY
@BLUEPIXY,a < 7是为了包含结尾的\0 - David Ranieri
@KeineLust 在循环外使用 *ptr = '\0'; - BLUEPIXY
@BLUEPIXY 你确定吗?UTC的长度为7个字节。 - David Ranieri
@KeineLust 如果它被设置为a < 7,则需要删除*ptr = '\0'; - BLUEPIXY
@BLUEPIXY 哦,我明白了,你说的是在 for 循环外面,你是对的! - David Ranieri
2个回答

3

您的循环迭代次数太多了。它必须复制长度为6的输入字符串,并插入两个额外的字符。但这并不改变输入长度。请将您的循环更改为:

for (int a = 0; a < 6; a++) {
   ...

3
你正在数组边界之外进行读写操作。
看一下这部分内容:
for (int a = 0; a < 8; a++) {
    *ptr++ = *fieldPtr++;

在第一个循环中,您通过fieldPtr访问UTC。在内存中,UTC的样子是这样的:

'1' '2' '0' '4' '3' '5' '\0'

a0 时,读取 1;当 a1 时,读取 2,以此类推。例如:

'1' '2' '0' '4' '3' '5' '\0' 
 ^   ^   ^   ^   ^   ^   ^   ^
a=0 a=1 a=2 a=3 a=4 a=5 a=6 a=7

a=7时,您读取了数组之外的内容,这是非法的(即未定义的行为)。

写入部分基本相同。但需要注意的是,您必须增加ptr的两个额外增量。因此,写入操作如下:

TIME[0] TIME[1] TIME[2] TIME[3] TIME[4] TIME[5] TIME[6] TIME[7] TIME[8]
 ^       ^       ^       ^       ^       ^       ^       ^       ^       ^
a=0     a=1     a=1     a=2     a=3     a=3     a=4     a=5     a=6     a=7

所以,当a=7时,您在TIME之外进行了编写。实际上,在循环之后您又进行了一次编写:*ptr = '\0';
因此,总之就是"未定义行为"。
当您遇到未定义的行为时,从语言角度来看讨论正在发生的事情是没有意义的。
然而,您可以分析在您特定的系统上发生了什么。在第二个循环中,当您写入DATE并超出DATE数组时,写入的数据可能会落入TIME,从而破坏原始值,并导致错误的输出。再次注意-这取决于系统。在我的系统上,您的代码产生了预期的输出,尽管存在未定义的行为。
要解决您的问题,请参阅@PaulOgilvie的答案。
由于您正在处理小型固定大小的输入字符串,因此避免所有指针内容的替代解决方案可能是:
  char *UTC = "120435";
  char TIME[9];
  if (strlen(UTC) == 6)
  {
      sprintf(TIME, "%c%c:%c%c:%c%c", UTC[0], UTC[1], UTC[2], UTC[3], UTC[4], UTC[5]);
      printf("%s\n", TIME);
  }
  else
  {
    printf("wrong format\n");

    // .... add error handling here ....
  }

谢谢大家:我没有看到显而易见的事情! - Andreas Swart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接