Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用C编写泛型函数,如何处理字符串_C - Fatal编程技术网

用C编写泛型函数,如何处理字符串

用C编写泛型函数,如何处理字符串,c,C,我有一个函数,它接受一个void**参数和一个指示其数据类型的整数 void foo (void** values, int datatype) 在函数内部,根据数据类型,我以以下方式对其进行malloc: if (datatype == 1) *values = (int*) malloc (5 * sizeof(int)); else if (datatype == 2) *values = (float*) malloc (5 * sizeof(float)); 到目前

我有一个函数,它接受一个
void**
参数和一个指示其数据类型的整数

void foo (void** values, int datatype)
在函数内部,根据数据类型,我以以下方式对其进行malloc:

if (datatype == 1)
    *values = (int*) malloc (5 * sizeof(int));
else if (datatype == 2)
    *values = (float*) malloc (5 * sizeof(float));
到目前为止一切都很好。然而,当字符串出现在图片中时,事情就变得复杂了。
void**
必须是
void***
,因为我需要这样做:

*values = (char**) malloc (5 * sizeof(char*));
for(i=0;i<5;i++)
    (*values)[i] = (char*) malloc (10);
..
strncpy( (*values)[0], "hello", 5);
所以我只是把
void**
转换成
char***
。我试过了,忽略了警告,效果很好。 但这安全吗?还有更优雅的选择吗

这种情况应如何处理?我是否可以将
char***
传递给期望使用
void**
但在其内部正确强制转换的函数

void foo (void** values, int datatype) {

if(datatype == 3) {
    char*** tmp_vals = (char***) values;
    *tmp_vals = (char**) malloc (5 * sizeof(char*));
    ...
    (*tmp_vals)[i] = (char*) malloc (10 * sizeof(char));
    strncpy (  (*tmp_vals)[i], "hello", 5);
}
不,这是技术上未定义的行为。它可能在您的计算机上运行,但在将来的某些计算机上可能会失败,这些计算机使用不同的表示实现了不同的指针类型,这是C语言标准所允许的

如果函数需要一个
void**
,那么最好传递一个
void**
。任何指针类型都可以隐式转换为
void*
,但这只能在顶层工作:
char*
可以转换为
void*
,而
char**
可以隐式转换为
void*
(因为
char**
是“指向
char*
的指针”),但是
char**
不能转换为
void**
,同样
char***
也不能转换为
void**

调用此函数的正确方法是向其传递正确的
void**
,然后将生成的
void*
指针转换回其原始类型:

void foo(void **values, int datatype)
{
    if(datatype == 3)
    {
        char ***str_values = ...;
        *values = str_values;  // Implicit cast from char*** to void*
    }
    else
    ...
}

...

void *values;
foo(&values, 2);
char ***real_values = (char ***)values;
假设
*值
实际上指向了
字符***
,那么此强制转换是有效的,并且在任何代码路径中都没有任何未定义的行为。

您根本不需要(也可能不应该)使用
void**
,只需使用常规的
void*
。根据C11 6.3.2.3.1,“指向
void
的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向
void
的指针,然后再转换回来;结果应与原始指针相同。”指针变量(包括指向另一个指针)是一个对象
void**
不是“指向
void
的指针”。您可以自由、安全地在
void*
之间进行转换,但不能保证您能够安全地在
void**
之间进行转换

所以你可以做:

void foo (void* values, int datatype) {
    if ( datatype == 1 ) {
        int ** pnvalues = values;
        *pnvalues = malloc(5 * sizeof int);

    /*  Rest of function  */
}
依此类推,然后将其称为类似:

int * new_int_array;
foo(&new_int_array, 1);
&new\u int\u array
属于
int**
类型,它将通过
foo()
隐式转换为
void*
,而
foo()
将其转换回类型
int**
,并取消引用以间接修改
new\u int\u array
,以指向它动态分配的新内存

对于指向动态字符串数组的指针:

void foo (void* values, int datatype) {

    /*  Deal with previous datatypes  */

    } else if ( datatype == 3 ) {
        char *** psvalues = values;
        *psvalues = malloc(5 * sizeof char *);
        *psvalues[0] = malloc(5);

    /*  Rest of function  */
}
以此类推,称之为:

char ** new_string_array;
foo(&new_string_array, 3);

类似地,
&new\u string\u array
类型为
char***
,再次隐式转换为
void*
,并且
foo()
将其转换回,并间接使
new\u string\u array
指向新分配的内存块

A
void*
只是指向未指定类型的指针;它可以是指向
int
、或
char
、或
char*
、或
char**
、或任何您想要的对象的指针,只要您确保在解除引用时将其视为适当的类型(或原始类型可以安全地解释为的类型)

因此,
void**
只是指向
void*
的指针,它可以是指向任何类型的指针,例如
char*
。因此,是的,如果您正在分配某些类型的对象的数组,并且在一种情况下,这些对象是
char*
,那么您可以使用
void**
来引用它们,这样您就可以将其称为
char***

直接看到这种结构通常是不常见的,因为通常您会将一些类型或长度信息附加到数组中,而不是使用
char***
您有一个
struct-typed\u对象**foo
或类似的
struct-typed\u对象
有一个类型标记和指针,然后将从这些元素中提取的指针强制转换为适当的类型,或者有一个
struct-typed_-array*foo
,它是一个包含类型和数组的结构

关于风格的几个注释。首先,做这种事情会使你的代码难以阅读。要非常小心地组织它,并清楚地记录它,以便人们(包括你自己)能够了解发生了什么。另外,不要强制转换malloc的结果;
void*
自动升级到其指定的类型,如果忘记包含
或更新类型声明但忘记更新强制转换,则强制转换
malloc
的结果可能会导致细微的错误。有关更多信息,请参阅

通常,将声明中的
*
附加到变量名,而不是类型名是一个好习惯,因为它实际上是这样解析的。下面声明了一个
char
和一个
char*
,但是如果您按照编写它们的方式编写,您可能希望它声明两个
char*

char *foo, bar;
或者用另一种方式写:

char* foo, bar;

通过一些技巧,你可以做到。见示例:

int sizes[] = { 0, sizeof(int), sizeof(float), sizeof(char *) }

void *foo(datatype) {
   void *rc = (void*)malloc(5 * sizes[datatype]);
   switch(datatype) {
     case 1: {
       int *p_int = (int*)rc;
       for(int i = 0; i < 5; i++)
         p_int[i] = 1;
     } break;
     case 3: {
       char **p_ch = (char**)rc;
       for(int i = 0; i < 5; i++)
         p_ch[i] = strdup("hello");
     } break;
   } // switch
   return rc;
} // foo
int size[]={0,sizeof(int),sizeof(float),sizeof(char*)}
void*foo(数据类型){
void*rc=(void*)malloc(5*个大小[数据类型]);
交换机(数据类型){
案例1:{
int*p_int=(int*)rc;
对于(int i=0;i<5;i++)
p_int[i]=1;
}中断;
案例3:{
中国
/*_Just for reference_ the functions required for variable arguments can be defined as:
#define va_list             char*
#define va_arg(ap,type)     (*(type *)(((ap)+=(((sizeof(type))+(sizeof(int)-1)) \
                                & (~(sizeof(int)-1))))-(((sizeof(type))+ \
                                (sizeof(int)-1)) & (~(sizeof(int)-1)))))
#define va_end(ap)          (void) 0
#define va_start(ap,arg)    (void)((ap)=(((char *)&(arg))+(((sizeof(arg))+ \
                                (sizeof(int)-1)) & (~(sizeof(int)-1)))))
*/
#define INT '0'
#define DOUBLE '1'
#define STRING '2'

void yourfunc(char *fmt_string, ...){
  va_list args;
  va_start (args, fmt_string);
  while(*fmt_string){
    switch(*fmt_string++){
     case INT: some_intfxn(va_arg(ap, int));
     case DOUBLE: some_doublefxn(va_arg(ap, double));
     case STRING: some_stringfxn(va_arg(ap, char *));
     /* extend this as you like using pointers and casting to your type */
     default: handlfailfunc();
    }
  }
  va_end (args);
}
#define INT '0'
#define DOUBLE '1'
#define STRING '2'

void yourfunc(datatype, ...){ /*leaving "..." for future while on datatype(s)*/
  va_list args;
  va_start (args, datatype);
  switch(datatype){
     case INT: some_intfxn(va_arg(ap, int));
     case DOUBLE: some_doublefxn(va_arg(ap, double));
     case STRING: some_stringfxn(va_arg(ap, char *));
     /* extend this as you like using pointers and casting to your type */
     default: handlfailfunc();
  }
  va_end (args);
}