【问题标题】:What is the best way to generate a path in C?在 C 中生成路径的最佳方法是什么?
【发布时间】:2013-06-13 16:12:35
【问题描述】:

我需要在运行时生成三种不同类型的路径:

  1. /sys/class/gpio/gpio%d
  2. /sys/class/gpio/gpio%d/value
  3. /sys/class/gpio/gpio%d/direction

目前我通过执行以下操作来生成这些:

#define GPIO_PATH_BASE "/sys/class/gpio/gpio"
#define GPIO_PATH_DIRECTION "/direction"
#define GPIO_PATH_VALUE "/value"

int open_gpio(const char * port) {
    char * base_path = (char *) malloc(sizeof(GPIO_PATH_BASE) + sizeof(port));
    strcpy(base_path, GPIO_PATH_BASE);
    strcat(base_path, port);

    char * value_path = (char *) malloc(sizeof(base_path) + sizeof(GPIO_PATH_VALUE));
    strcpy(value_path, (const char *) base_path);
    strcat(value_path, GPIO_PATH_VALUE);

    char * dir_path = (char *) malloc(sizeof(base_path) + sizeof(GPIO_PATH_DIRECTION));
    strcpy(dir_path, (const char *) base_path);
    strcat(dir_path, GPIO_PATH_DIRECTION);
}

我实际上对这种方法很不满意。是否有可能让宏为我提供这些东西,或者我应该创建一个辅助函数?

博多

【问题讨论】:

  • 为什么这被标记为 C++?
  • 它不再被标记为 C++... ;)
  • sizeof(port) 是指针大小。
  • 如果您在编译时知道port 的值,则可以构造适当的宏。你在编译时知道port吗?
  • 您有asprintf() 可用吗?如果是这样,请使用它。你需要了解sizeof(base_path)strlen(base_path)之间的区别——你需要使用后者。

标签: c string macros c-preprocessor


【解决方案1】:

我发现sprintf(或@larsmans 提到的snprintf)更适合字符串操作,特别是如果您想在字符串中添加十进制值。

我会使用 PATH_MAX(在 limits.h 中定义)来拥有一个静态分配的缓冲区,例如:

#include <limits.h>
#include <stdio.h>

unsigned char path[PATH_MAX];

#define GPIO_PATH_BASE "/sys/class/gpio/gpio"
#define GPIO_PATH_VALUE "/value"

int main(void)
{
        snprintf(path, PATH_MAX, "%s/%d%s", GPIO_PATH_BASE, 42, GPIO_PATH_VALUE);
        puts(path);
        return 0;
}

$ make main
cc     main.c   -o main
$ ./main 
/sys/class/gpio/gpio/42/value
$

【讨论】:

  • 不过,sprintf 非常不安全。最好使用snprintfasprintf
【解决方案2】:

我很喜欢用sprintf 来生成这样的字符串。但这假设您有一个合理的最大尺寸,您知道不会超过该尺寸。

当然,这至少比sizeof(base_path) 好一点,这是完全错误的。

对于在模块外不可见的变量,使用malloc 似乎也是个坏主意。

如果我们假设port 是正确的字符串,如下所示:

 char base_path[100]; 

 sprintf(base_path, "%s/%s", GPIO_PATH_BASE, port); 

【讨论】:

    【解决方案3】:

    您可以创建一个包含两个部分、分配空间并将它们连接起来的函数。这应该可以减少代码重复,同时保持代码的可读性:

    static char *concat(const char* prefix, const char* suffix) {
        size_t len = strlen(prefix) + strlen(suffix) + 1;
        char *res = malloc(len);
        strcpy(res, prefix);
        strcat(res, suffix);
        return res;
    }
    

    现在您可以按如下方式使用此功能:

    char * base_path = concat(GPIO_PATH_BASE, port);
    char * value_path = concat(base_path, GPIO_PATH_VALUE);
    char * dir_path = concat(base_path, GPIO_PATH_DIRECTION);
    

    【讨论】:

      【解决方案4】:

      如果您的系统支持,那么asprintf() 是最简单的机制:

      #define GPIO_PATH_BASE      "/sys/class/gpio/gpio"
      #define GPIO_PATH_DIRECTION "/direction"
      #define GPIO_PATH_VALUE     "/value"
      
      int open_gpio(const char * port)
      {
          char *base_path  = asprintf("%s%s", GPIO_PATH_BASE, port);
          char *value_path = asprintf("%s%s", base_path, GPIO_PATH_VALUE);
          char *dir_path   = asprintf("%s%s", base_path, GPIO_PATH_DIRECTION);
          //...do some work with these...or return them...
          //...should check for failed allocations, too...
      }
      

      如果您的系统不支持asprintf(),您可以“伪造”它:

      #include <stdio.h>
      #include <stdarg.h>
      #include <stdlib.h>
      
      /* Should be in a header */
      #ifndef HAVE_ASPRINTF
      extern int asprintf(char **ret, const char *format, ...);
      extern int vasprintf(char **ret, const char *format, va_list args);
      #endif
      
      #ifndef HAVE_ASPRINTF
      
      int vasprintf(char **ret, const char *format, va_list args)
      {
          va_list copy;
          va_copy(copy, args);
      
          /* Make sure return pointer is determinate */
          *ret = 0;
      
          int count = vsnprintf(NULL, 0, format, args);
          if (count >= 0)
          {
              char* buffer = malloc(count + 1);
              if (buffer != NULL)
              {
                  count = vsnprintf(buffer, count + 1, format, copy);
                  if (count < 0)
                  {
                      free(buffer);
                      buffer = 0;
                  }
                  *ret = buffer;
              }
          }
      
          va_end(copy);
      
          return count;
      }
      
      int asprintf(char **ret, const char *format, ...)
      {
          va_list args;
          va_start(args, format);
          int count = vasprintf(ret, format, args);
          va_end(args);
          return(count);
      }
      
      #endif /* HAVE_ASPRINTF */
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-17
        • 2012-10-14
        • 1970-01-01
        • 2012-01-31
        • 2011-01-30
        • 2010-11-06
        • 2010-09-30
        • 2019-08-10
        相关资源
        最近更新 更多