【问题标题】:Set typedef that can be 2 different types设置可以是 2 种不同类型的 typedef
【发布时间】:2021-07-09 20:20:40
【问题描述】:

我正在尝试创建一个可以接收变量的函数,该变量可以是 char 或 int,为此我必须给变量一个类型,但我不知道该放什么,因为我希望它能够接收两者。 我的代码是这样的,我不知道在写“IDK”的地方放什么:

#include <stdio.h>
#include <string.h>


int is_string = 0;
#define less(A,B) (!is_string ? ((A)<(B)) : (strcmp(A,B)<0))

void merge(IDK a[], int l, int m, int r)
{
    IDK aux[10000][20];
    int i, j, k;
    for (i = m+1; i > l; i--) aux[i-1] = a[i-1];
    for (j = m; j < r; j++) aux[r+m-j] = a[j+1];
    for (k = l; k <= r; k++){
        if (less(aux[j], aux[i])) a[k] = aux[j--];
        else a[k] = aux[i++];
    }
}

void mergesort(IDK a[], int l, int r) {
 int m = (r+l)/2;
 if (r <= l)
 return;
 mergesort(a, l, m);
 mergesort(a, m+1, r);
 merge(a, l, m, r);
}

int main(){
    mergesort(a,left,right);
}

当变量is_string 为 1 时,我希望“IDK”为 char,否则为 int... 目标是使用该函数按字母顺序或数字(从最低到最高)对单词进行排序,该函数一次只需对一种类型的对象进行排序,但我希望它能够对两种类型的对象进行排序,所以我不'不必重复代码。 我想要一个程序来选择(在运行时)它需要排序的类型。 谢谢

【问题讨论】:

  • 使用union 类型。
  • 您希望它是char 还是char *strcmp() 需要 char *
  • @Vasco Trancoso Vaz 在哪里声明了 aux?
  • 从你的宏less 我猜你想比较数字或字符串。这将变得更加复杂,因为 char[] 要么是单个字符的数组,要么是 one 字符串。为了复制字符串,您必须使用指针或使用strcpy。您想在同一个程序中使用两种变体(整数或字符串)还是想在编译时选择一个?请edit你的问题澄清一下,不要用cmets来回答。
  • @Bodo 完成了,正是这样,我希望我的算法能够有时对字符串进行排序,而在其他时间对整数进行排序

标签: arrays c sorting types mergesort


【解决方案1】:

在 C 中编写此类函数的一般方法是声明一个函数,该函数接受 void * 类型的指针以及传递的数组中的元素数量、数组元素类型的对象的大小和比较函数与标准 C 函数 qsort 的声明方式类似。

这是一个展示这种方法的演示程序。

函数merge可以在不动态分配内存的情况下编写。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void merge( void *a, size_t m, size_t n, size_t size, int cmp( const void *, const void * ) )
{
    void *p = malloc( n * size );
    
    size_t i = 0, j = m;
    
    char *pos = p;
    
    while ( i < m && j < n )
    {
        if ( cmp( ( char * )a + j * size, ( char *)a + i * size ) < 0 )
        {
            memcpy( pos, ( char * )a + j * size, size );
            ++j;
        }
        else
        {
            memcpy( pos, ( char * )a + i * size, size );
            ++i;
        }
        
        pos += size;
    }
    
    if ( i <  m )
    {
        memcpy( pos, ( char * )a + i * size, ( m - i ) * size );
    }
    
    if ( j <  n )
    {
        memcpy( pos, ( char * )a + j * size, ( n - j ) * size );
    }
    
    memcpy( a, p, n * size );
    
    free( p );
}

void mergesort( void *a, size_t n, size_t size, int cmp( const void *, const void * ) )
{
    if ( n / 2 )
    {
        mergesort( a, n / 2, size, cmp );
        mergesort( ( char * )a + n / 2 * size, n - n / 2, size, cmp );
        merge( a, n / 2, n, size, cmp );
    }
}

int cmp_int( const void *p1, const void *p2 )
{
    int a = *( int * )p1;
    int b = *( int * )p2;
    
    return ( b < a ) - ( a < b );
}

int cmp_string( const void *p1, const void *p2 )
{
    return strcmp( *( const char * const * )p1, *( const char * const * )p2 );
}

int main(void) 
{
    int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    const size_t N1 = sizeof( a ) / sizeof( *a );
    
    for ( size_t i = 0; i < N1; i++ )
    {
        printf( "%d ", a[i] );
    }
    putchar( '\n' );
    
    mergesort( a, N1, sizeof( *a ), cmp_int );

    for ( size_t i = 0; i < N1; i++ )
    {
        printf( "%d ", a[i] );
    }
    putchar( '\n' );

    char * s[] = { "J", "I", "H", "G", "F", "E", "D", "C", "B", "A" };
    const size_t N2 = sizeof( s ) / sizeof( *s );
    
    for ( size_t i = 0; i < N2; i++ )
    {
        printf( "%s ", s[i] );
    }
    putchar( '\n' );
    
    mergesort( s, N2, sizeof( *s ), cmp_string );

    for ( size_t i = 0; i < N2; i++ )
    {
        printf( "%s ", s[i] );
    }
    putchar( '\n' );

    return 0;
}

程序输出是

9 8 7 6 5 4 3 2 1 0 
0 1 2 3 4 5 6 7 8 9 
J I H G F E D C B A 
A B C D E F G H I J 

【讨论】:

  • 非常感谢您的帮助,这是一个学校项目,我的老师说我们不能保留内存,也不能使用 malloc 和 memcpy 函数,您有什么建议吗?
  • @VascoTrancosoVaz 函数merge可以不用动态内存分配来写。也就是说,您可以就地合并数组的分区。看来您正在使用辅助数组 aux。其实这是一个内存分配
  • 我是算法新手,不太了解,我只知道我们不能使用您在代码中使用的 malloc 和 memcpy 函数。 aux 是一个全局数组,是的,我的老师给了我们这样做的指导。
  • @VascoTrancosoVaz 你应该咨询你的老师。您也可以在互联网上搜索如何合并数组的两个分区。
  • 非常感谢!
【解决方案2】:

您正在寻找union,但这不适用于您对scanfprintf 的调用。他们期望特定类型的参数取决于格式修饰符,在这种情况下期望intint* 修饰符"%d"

union IDK
{
    int int_val;
    char *string_val;
};
void merge(union IDK a[], int l, int m, int r);

您从控制台读取的内容始终是字符串。 scanf 可以将其转换为数字,如果这是您对“%d”的要求。但看起来你真正在寻找的是一种对可能由数字组成的字符串进行排序的方法。最简单的方法是尝试使用strtol 进行转换。

您还应该注意另一个陷阱。 strcmp 比较字符的 ASCII 值。这不会按字母顺序排序,如果这是您要查找的内容。

请注意,通用排序函数通常通过传递void*、元素数量、这些元素的大小以及指向比较函数的函数指针来实现。示例见qsort

【讨论】:

  • scanf 和 printf 只是为了测试而存在的,当我将排序算法放在我的主项目中时不会出现这个问题,我也尝试了 union 并且代码不断产生错误出于某种原因在宏中,有什么想法吗?
  • @VascoTrancosoVaz 您可以通过成员名称访问联合的值,例如a.int_value。联合通常位于struct 中,其中还包含有关联合中数据类型的某种信息。这不是代码替换的下降。
猜你喜欢
  • 1970-01-01
  • 2018-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多