为什么要在strcat()之前先使用strcpy()?

55ooxyrt  于 2023-08-03  发布在  其他
关注(0)|答案(6)|浏览(102)

为什么这段代码会产生运行时问题:

char stuff[100];
strcat(stuff,"hi ");
strcat(stuff,"there");

字符串
但这个却不是

char stuff[100];
strcpy(stuff,"hi ");
strcat(stuff,"there");

wyyhbhjk

wyyhbhjk1#

strcat将查找空终止符,将其解释为字符串的结尾,并在那里追加新的文本,在此过程中覆盖空终止符,并在串联的结尾处写入新的空终止符。

char stuff[100];  // 'stuff' is uninitialized

字符串
空终止符在哪里?stuff未初始化,因此它可能以NUL开头,也可能没有NUL。
在C++中,你可以这样做:

char stuff[100] = {};  // 'stuff' is initialized to all zeroes


现在可以执行strcat了,因为'stuff'的第一个字符是空终止符,所以它会追加到正确的位置。
在C中,你仍然需要初始化'stuff',这可以通过以下几种方式完成:

char stuff[100]; // not initialized
stuff[0] = '\0'; // first character is now the null terminator,
                 // so 'stuff' is effectively ""
strcpy(stuff, "hi ");  // this initializes 'stuff' if it's not already.

pengsaosao

pengsaosao2#

在第一种情况下,stuff包含垃圾。strcat要求目标和源都包含正确的空终止字符串。

strcat(stuff, "hi ");

字符串
将扫描stuff以查找终止字符'\0',并从该字符开始复制"hi "。如果它没有找到它,它将从数组的末尾运行,并且可能会发生任何不好的事情(即,行为未定义)。
避免这个问题的一个方法是这样的:

char stuff[100];
stuff[0] = '\0';      /* ensures stuff contains a valid string */
strcat(stuff, "hi ");
strcat(stuff, "there");


也可以将stuff初始化为空字符串:

char stuff[100] = "";


这将用零填充stuff的所有100个字节(增加的清晰度可能值得任何小的性能问题)。

w6lpcovy

w6lpcovy3#

因为stuff在调用strcpy之前未初始化。在声明stuff不是空字符串之后,它是未初始化的数据。
strcat将数据追加到字符串的末尾-也就是说,它在字符串中找到空终止符并在其后添加字符。一个未初始化的字符串不能保证有一个null终止符,所以strcat很可能会崩溃。
如果要初始化stuff,如下所示,您可以执行strcat:

char stuff[100] = "";
strcat(stuff,"hi ");
strcat(stuff,"there");

字符串

ttvkxqim

ttvkxqim4#

Strcat将字符串追加到现有字符串。如果字符串数组为空,则不会查找字符串结尾('\0'),并且会导致运行时错误。
根据Linux手册页,简单的strcat是这样实现的:

char*
   strncat(char *dest, const char *src, size_t n)
   {
       size_t dest_len = strlen(dest);
       size_t i;

       for (i = 0 ; i < n && src[i] != '\0' ; i++)
           dest[dest_len + i] = src[i];
       dest[dest_len + i] = '\0';

       return dest;
   }

字符串
正如您在这个实现中看到的,strlen(dest)将不会返回正确的字符串长度,除非将dest初始化为正确的c字符串值。你可能很幸运地在char stuff[100];处有一个第一个值为零的数组,但你不应该依赖它。

kjthegm6

kjthegm65#

另外,我建议不要使用strcpystrcat,因为它们可能会导致一些意想不到的问题。
使用strncpystrncat,因为它们有助于防止缓冲区溢出。

oiopk7p5

oiopk7p56#

可能更安全:

// AddToArray help:
//
// In the beginning you must initialize ArrCurrIdx to zero during the first call:
//      uint8_t ErrorArr8[255]; memset(ErrorArr8, 0, 255); // add '\0
//      then first call:
//      if(!AddToArray(ErrorArr8, tmp)) return 0; // overflow
//
// Arr is an array where we need to put the data
// AddedArr is an added array
// AddToArray is an analog of strcat() with added overflow protect

uint8_t AddToArray(uint8_t * Arr, uint8_t * AddedArr)
{
  static uint8_t ArrCurrIdx = 0;
  uint16_t ArrLen = strlen((const char*)Arr);
  uint16_t AddedLen = strlen((const char*)AddedArr);
  if(ArrLen == 0) // initialize ArrCurrIdx to zero during the first call
  {
    memset(Arr, 0, 255); 
    ArrCurrIdx = 0;
  }
  if((ArrCurrIdx + AddedLen) > 255 ) // 255 is max Arr size
  {
      uint8_t tmp[] = "OVERFLOW in AddToArray\n\r"; // 
      memcpy(&Arr[0], tmp, strlen((const char*)tmp));
      return 0; // to prevent overflow
  }
  memcpy(&Arr[ArrCurrIdx], AddedArr, AddedLen); 
  ArrCurrIdx = ArrCurrIdx + AddedLen;
  return 1;
}

// Example:
//    1.
//    uint8_t ErrorArr8[255];
//    memset(ErrorArr8, 0, 255); // To set ArrCurrIdx  to zero
//    uint8_t tmp[] = "Error message or message 1. "; 
//    if(!AddToArray(ErrorArr8, tmp)) return 0; // if 0 then overflow;
//    
//    uint8_t tmp11[] = "Next message. ";
//    if(!AddToArray(ErrorArr8, tmp11)) return 0; // if 0 then overflow;
//    
//    uint8_t tmp22[] = "Then another added message. ";
//    if(!AddToArray(ErrorArr8, tmp22)) return 0; // if 0 then overflow;
//    
//    // We start new cycle:
//    uint8_t a[20]; 
//    memset(a, 0, 20); // To set ArrCurrIdx  to zero
//    uint8_t tmp33[] = "Brand new error message or message. ";
//    if(!AddToArray(a, tmp33);)) return 0; // if 0 then overflow;
//    2.
//    if(!AddToArray(ErrorArr8, "\n\r")) return 0; // if 0 then overflow; will automatically append \0 to "\n\r"

/********************************************************************************************************************************************/

字符串
/********************************************************************************************************************************************/

// uint8 transform to uint8_t is analog of atoi

uint8_t* ui8toa(uint8_t i)
{
    static uint8_t arr[4]; // fourth digit is for '\0'
    memset(arr, 0, 4); // 0 equals '\0' because '\0' and 0 are stored as 0.
    sprintf((char*)arr, "%u", i);
    return arr;
}

//Example:
//  if(!AddToArray(ErrorArr8, ui8toa(i))) return 0; // if 0 then overflow

/********************************************************************************************************************************************/

相关问题