在C中连接const字符串和变量输入

时间:2015-12-17 14:14:46

标签: c string parameters concatenation

我在C工作,我必须连接一些东西。

现在我有这个:

int main(int argc, char *argv[])
{
    char tftp_cmd[TFTP_MAX_BUFFER_SIZE];    
    char ip_server[IP_MAX_LEN];
    char file_name[FILE_MAX_LEN];
    const char * tftp_get = "tftp -g -r ";

    strcpy(&ip_server[0], argv[1]);
    strcpy(&file_name[0], argv[2]);

    tftp_cmd[0] = '\0';
    strcpy(tftp_cmd, tftp_get);
    printf("tftp get command = %s\n", tftp_cmd);
    strncat(tftp_cmd, file_name, sizeof(tftp_get) + sizeof(file_name));
    printf("tftp get command = %s\n", tftp_cmd);     
    strncat(tftp_cmd, ip_server, sizeof(tftp_get) + sizeof(file_name) + 1);
    printf("tftp get command = %s\n", tftp_cmd);
    return 0
}

此应用程序返回:

# ./test_app 10.0.0.1 MY_TEST_FILE_17.12.2015
tftp get command = tftp -g -r 
tftp get command = tftp -g -r MY_TEST_FILE_17.12.2015
tftp get command = tftp -g -r MY_TEST_FILE_17.12.201510.0.0.1

我想要tftp -g -r MY_TEST_FILE_17.12.2015 10.0.0.1

我使用的好方法?

3 个答案:

答案 0 :(得分:2)

首先,请注意/* Start by setting display:none to make this hidden. Then we position it in relation to the viewport window with position:fixed. Width, height, top and left speak for themselves. Background we set to 80% white with our animation centered, and no-repeating */ .modal { display: none; position: fixed; z-index: 1000; top: 0; left: 0; height: 100%; width: 100%; background: rgba( 255, 255, 255, .8 ) url('../Images/Loader.gif') 50% 50% no-repeat; } /* When the body has the loading class, we turn the scrollbar off with overflow:hidden */ body.loading { overflow: hidden; } /* Anytime the body has the loading class, our modal element will be visible */ body.loading .modal { display: block; } 基本上与&ip_server[0]相同,第二ip_server不会做你认为它做的事情。

下面

sizeof

相反,做一些像

这样的事情
strncat(tftp_cmd, file_name, sizeof(tftp_get) + sizeof(file_name));

strcat(tftp_cmd, file_name); 必须指向足够大的内存缓冲区。

使用tftp_cmd可能非常危险,因为它可能会遗漏终止strncat()

'\0'成为tftp_cmd的第一个参数时,您也不需要初始化strcpy(),但您需要它指向足够大的内存缓冲区。而且您根本不需要复制argv[1]argv[2],您可以直接使用它们。如果必须,您可以使用指针而不是复制昂贵的内存。

最后,正如EugeneSh.中@ comment所建议的那样,最好的方法是snprintf(),这是一个例子

int main(int argc, char *argv[])
{
    char *tftp_cmd;
    ssize_t length;
    const char *format;

    format = "tftp -g -r %s %s";
    if (argc < 3) // Check that parameters were passed to the funcion
        return -1;
    length = snprintf(NULL, 0, format, argv[2], argv[1]);
    tftp_cmd = malloc(length + 1);
    if (tftp_cmd == NULL)
        return -1; // Allocation Error
    sprintf(tftp_cmd, format, argv[2], argv[1]);
    // Use the `tftp_cmd' here, for example
    fprintf(stdout, "%s\n", tftp_cmd);
    // And then, free it
    free(tftp_cmd);
    return 0;
}

答案 1 :(得分:2)

显然,所提供的代码存在各种问题:

  • 参数被不必要地复制到临时缓冲区中,没有检查缓冲区溢出。 (例如strcpy(&ip_server[0], argv[1]);
  • strncat的第三个参数是第二个参数的最大大小,而不是连接字符串的最大大小。示例代码中提供的值允许缓冲区溢出。
  • 在ip号码之前未插入所需的空格字符。
  • 过度依赖固定大小的阵列。

更重要的是,一般方法既难以阅读又效率低下。 C语言中的字符串处理有点烦人,但自1989年以来C字符串库已经有了重大改进,使用新的库函数可以使代码更安全,更易读,更高效。好的C代码应该可以最佳地利用库功能。

(在原始代码中重复使用strncat是低效的,因为strncat将在每次调用时从头开始重新扫描输出字符串,从而导致字符串变长时的二次执行时间。)

更好的方法是使用snprintf来提供简单,易读,安全和有效的格式化操作:

int main(int argc, char** argv) {
  if (argc < 3) {
      fprintf(stderr, "Too few arguments.\n");
      exit(1);
  }
  /* These are for documentation; no copying is involved */
  const char* file_name = argv[2];
  const char* ip_server = argv[1];
  char tftp_cmd[TFTP_MAX_BUFFER_SIZE];
  int outlen = snprintf(tftp_cmd, sizeof tftp_cmd,
      "tftp -g -r %s %s", file_name, ip_server);
  if (outlen >= sizeof tftp_cmd) {
      fprintf(stderr, "Arguments are too long\n");
      exit(1);
  }
  printf("%s\n", tftp_cmd);
  return 0;

}

这可以通过分配内存而不是使用固定大小的缓冲区来改进:

int main(int argc, char** argv) {
  if (argc < 3) {
      fprintf(stderr, "Too few arguments.\n");
      exit(1);
  }
  char* tftp_cmd = NULL;
  const char* file_name = argv[2];
  const char* ip_server = argv[1];
  int outlen = snprintf(tftp_cmd, 0,
      "tftp -g -r %s %s", file_name, ip_server);
  tftp_cmd = malloc(outlen + 1);
  snprintf(tftp_cmd, outlen + 1,
      "tftp -g -r %s %s", file_name, ip_server);
  printf("%s\n", tftp_cmd);
  free(tftp_cmd);
  return 0;

}

一些现代C库实现asprintf,它自动进行内存分配,更方便,因为它避免了两次调用snprintf

int main(int argc, char** argv) {
  if (argc < 3) {
      fprintf(stderr, "Too few arguments.\n");
      exit(1);
  }
  char* tftp_cmd = NULL;
  const char* file_name = argv[2];
  const char* ip_server = argv[1];
  if (0 > asprintf(&tftp_cmd, 
                  "tftp -g -r %s %s", file_name, ip_server)) {
      fprintf(stderr, "Memory allocation error\n");
      exit(1);
  }
  printf("%s\n", tftp_cmd);
  free(tftp_cmd);
  return 0;

}

答案 2 :(得分:1)

以下代码更正了发布代码中发现的问题。

int main(int argc, char *argv[])
{
    char tftp_cmd[TFTP_MAX_BUFFER_SIZE];
    char ip_server[IP_MAX_LEN];
    char file_name[FILE_MAX_LEN];
    const char * tftp_get = "tftp -g -r ";

    if( 3 != argc )
    {
        fprintf( stderr, "USAGE: %s <serverIP> <filename>\n", argv[0]);
        exit( EXIT_FAILURE );
    }

    // implied else, correct number of command line parameters

    strcpy(ip_server, argv[1]);
    strcpy(file_name, argv[2]);

   // tftp_cmd[0] = '\0'; -- not needed because first data is set by strcpy()
    strcpy(tftp_cmd, tftp_get);
    printf("tftp get command = %s\n", tftp_cmd);
    strcat(tftp_cmd, file_name);
    printf("tftp get command = %s\n", tftp_cmd);
    strcat(tftp_cmd, ip_server );
    printf("tftp get command = %s\n", tftp_cmd);
    return 0
}

但是,对strcat()的一系列调用可能(可能)溢出tftp_cmd[]缓冲区。并且发布的代码正在尝试使用strncat(),因此以下内容更安全,因为它不会溢出tftp_cmd []缓冲区。

int main(int argc, char *argv[])
{
    char tftp_cmd[TFTP_MAX_BUFFER_SIZE];
    char ip_server[IP_MAX_LEN];
    char file_name[FILE_MAX_LEN];
    const char * tftp_get = "tftp -g -r ";

    if( 3 != argc )
    {
        fprintf( stderr, "USAGE: %s <serverIP> <filename>\n", argv[0]);
        exit( EXIT_FAILURE );
    }

    // implied else, correct number of command line parameters

    strcpy(ip_server, argv[1]);
    strcpy(file_name, argv[2]);

   // tftp_cmd[0] = '\0'; -- not needed because first data is set by strcpy()
    strncpy(tftp_cmd, tftp_get, TFTP_MAX_BUFFER_SIZE);
    printf("tftp get command = %s\n", tftp_cmd);

    strncat(tftp_cmd, file_name. TFTP_MAX_BUFFER_SIZE - strlen( tftp_cmd ) );
    printf("tftp get command = %s\n", tftp_cmd);

    strncat(tftp_cmd, ip_server,  TFTP_MAX_BUFFER_SIZE - strlen( tftp_cmd );
    printf("tftp get command = %s\n", tftp_cmd);
    return 0
}

但是,这并未提供缓冲区实际包含整个数据字符串的任何指示。

因此可以在return语句

之前插入以下内容
    if( (strlen( tftp_get) + strlen( file_name ) + strlen( ip_server ) +1 ) > TFTP_MAX_BUFFER_SIZE )
    {
        printf( "unable to create the full contents of the tftp command\n" );
    }