【问题标题】:How to use a swapping function without malloc?如何在没有 malloc 的情况下使用交换功能?
【发布时间】:2020-10-13 21:56:03
【问题描述】:

我想创建没有 malloc 的 void 指针。但是通过交换值和使用 void 指针,我看不到不使用 malloc 的解决方案。

有没有办法在不使用 malloc 的情况下进行这种操作。我在考虑双指针,但这也不是解决方案。

.................................................. ..................................................... ..................................................... .......

#include <stdio.h> // Standard library functions for file input and output
#include <stdlib.h> // Standard library used for memory allocation, process control, and conversions

typedef unsigned char byte_t;

// Declaration of the function
void genericSwap(void *pdata1, void *pdata2, byte_t nBytes);

// Global variable
unsigned int dataFloat = 0; // this will help us as a flag to differentiate
                            // between a float or integer

int main() // main program
{
  int sw;             // integer value for switch
  int a = 0, b = 0;   // declaring integer a and b
  char x = 0, y = 0;  // declaring character x and y
  float c = 0, d = 0; // declaring float c and d
  for (;;)            //  infinite loop
  {
    printf("\nPlease enter your choice:\n'1' for integer value.\n'2' for "
           "character value.\n'3' for float value.\n'0' for exit. \n : ");

    scanf_s("%d", &sw); // choose the input for the switch function

    switch (sw) // switch function
    {
    case 1: // case 1 for selecting integer operations
      printf("\nPlease enter the values for a and b \n : ");
      scanf_s("%d%d", &a, &b); // choose the input for the integer operation
      printf("\nValues before swap\na=%d\tb=%d", a, b);

      genericSwap(
          &a, &b,
          sizeof(int)); // calling the swap function with address of a and b

      printf("\nValues after swap\na=%d\tb=%d", a,
             b); // printing the swap values
      break;

    case 2: // case 2 for selecting character operations
      printf("\nPlease enter the values for a and b \n : ");
      scanf_s(" %c", &x);
      scanf_s(" %c", &y);

      printf("\nValues before swap\na= %c\tb= %c", x, y);

      genericSwap(
          &x, &y,
          sizeof(char)); // calling the swap function with address of x and y

      printf("\nValues after swap\na= %c\tb= %c", x, y);
      break;
    case 3:          // case 3 for selecting float operations
      dataFloat = 1; // indicate that the float operation has been selected
      printf("\nPlease enter the values for a and b \n : ");
      scanf_s("%f%f", &c, &d);

      printf("\nValues before swap\na= %.2f\tb= %.2f", c, d);

      genericSwap(
          &c, &d,
          sizeof(float)); // calling the swap function with address of c and d

      printf("\nValues after swap\na= %.2f\tb= %.2f", c, d);
      break;

    case 0: // terminate the application
      exit(1);
      break;

    default: // when no appropriate selection has been made
      printf("\nYou have entered the wrong choice");
      break;
    }
  }
}

// define function
void genericSwap(void *pdata1, void *pdata2, byte_t nBytes) {
  void *temp = NULL; // setting void pointer temp to NULL

  temp = malloc(nBytes); // allocating memory to void pointer
  if (nBytes > 1) // if the size is more then 1 byte this means it is another
                  // value then a character
  {
    if (dataFloat == 1) // if dataFloat is equal to 1 then it is a float type
    {
      dataFloat = 0;                         // resetting dataFloat to 0
      *((float *)temp) = *((float *)pdata1); // typecasting the pointer to float
      *((float *)pdata1) = *((float *)pdata2);
      *((float *)pdata2) = *((float *)temp);

    } else {
      *((int *)temp) = *((int *)pdata1); // typecasting the pointer to integer
      *((int *)pdata1) = *((int *)pdata2);
      *((int *)pdata2) = *((int *)temp);
    }
  } else {
    *((char *)temp) = *((char *)pdata1); // typecasting the pointer to character
    *((char *)pdata1) = *((char *)pdata2);
    *((char *)pdata2) = *((char *)temp);
  }

  free(temp); // de-allocating memory

  temp = NULL; // making the pointer temp to NULL
}

【问题讨论】:

  • 如果您真的不想分配动态内存,可以一次交换一个字节(或某个固定大小)。
  • 如果nBytes 保证不会太大,另一个可能的选择是使用 VLA(可变长度数组 - 例如uint8_t tmpArray[nBytes];)。这有点危险,而且不太便携。但只是注意到作为完整性的一个选项。
  • 不要想太多问题。您正在交换 memory 的两个区域(最好是相同的大小才能工作)。使用两个 unsigned char 指针并同时迭代两个区域,沿途交换八位字节。更难:计算机器字边界,以更有效地交换大部分相关区域的数据,一旦您确定这是您错误地认为需要修复的某些性能瓶颈的核心。跨度>
  • 如果可用,您可以使用在堆栈上分配内存的allocaman7.org/linux/man-pages/man3/alloca.3.html
  • 上面的代码不关心别名。这是一个UB

标签: c pointers malloc swap


【解决方案1】:
void *genericSwap(void *a, void *b, size_t size)
{
    uint64_t tmp;   //max size depends on the hardware architecture
    unsigned char *ua = a;
    unsigned char *ub = b;
    if(a && b)
    {
        while(size >= sizeof(tmp))
        {
            memcpy(&tmp, ua, sizeof(tmp));
            memcpy(ua, ub, sizeof(tmp));
            memcpy(ub, &tmp, sizeof(tmp));
            ua += sizeof(tmp);
            ub += sizeof(tmp);
            size -= sizeof(tmp);
        }
        while(size--) 
        {
            unsigned char tmp = *ua;
            *ua++ = *ub;
            *ub++ = tmp;
        }
    }
    return a;
}

如果架构可以处理对 32 位或 64 位数据的非对齐访问,编译器很可能会优化对 memcpy 的调用。

https://godbolt.org/z/9vxP4j

Cortex-M0 需要对齐数据 - 编译器没有优化 memcpy。

您可以将缓冲区设置得更大(在本例中为 64 字节),编译器还将生成非常高效的代码:https://godbolt.org/z/vxK1vj

【讨论】:

  • 值得注意:即使编写基本的 while 循环和单字符交换,人们可能会惊讶于 compiler optimizes movement regardless 的出色表现。
  • @WhozCraig 它远非高效。我们不希望每个字节都分支刷新管道等。godbolt.org/z/qzv9bz
  • @WhozCraig 从我的回答中看到第二个例子:godbolt.org/z/vxK1vj
  • @WhozCraig clang 好一点
  • 好的,如果它内联您在内部循环中使用的对memcpy 的三个调用。
猜你喜欢
  • 2023-01-24
  • 1970-01-01
  • 1970-01-01
  • 2019-08-08
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
相关资源
最近更新 更多