C中用于交换字符串和数组的通用交换函数

vfh0ocws  于 2023-10-16  发布在  其他
关注(0)|答案(3)|浏览(88)

我正在学习C,试图实现一个通用的交换函数,它能够交换字符串和数组。我认为这是可能的,因为字符串和数组本质上是内存中的数组,我没有交换实际的字节,而只是指针。下面是我的实现:

#include "stdio.h"

void swap(void **a, void **b) {
    void *tmp;
    tmp = *b;
    *b = *a;
    *a = tmp;
}

void print_array(char *c, int size) {
    for (int i = 0; i < size; ++i) {
        printf("%c ", c[i]);
    }
    printf("\n");
}

int main() {
    char *h = "hell";
    char *w = "world";
    printf("%s %s\n", h, w);
    swap((void **)&h, (void **)&w);
    printf("%s %s\n", h, w);

    char c[5] = { 'a', 'e', 'i', 'o', 'u' };
    char d[3] = { '1', '2', '3' };
    print_array(c, 5);
    print_array(d, 3);
    swap((void **)&c, (void **)&d);

    return 0;
}

结果是:

hell world
world hell
a e i o u 
1 2 3 
*** stack smashing detected ***: terminated
Aborted (core dumped)

正如您所看到的,这个通用的swap函数可以交换两个字符串,但是当输入是数组时,它会触发core dump。我不明白我对指针和数组的理解哪一部分是错误的。

des4xlb0

des4xlb01#

char c[5] = { 'a', 'e', 'i', 'o', 'u' };

数组不是指针。&c不是char **
是的,你可以对一个数组做*c*(c + 2)以及其他类似指针的事情,这自然会让你认为它是一个指针,但它不是。
这些之所以有效,是因为数组在许多情况下都被 * 隐式转换 *(“衰减”)为指针(指向其第一个元素),包括那些情况。结果指针是临时的(右值),它在需要时进行动态计算,否则不会存储在内存中。数组中唯一存储的东西是它的元素。
一个数组不衰减为指针的情况之一是&c。这不会返回char **(指向char的指针),这不可能工作,因为衰减产生的指针是计算的,而不是持久存储在内存中。
相反,您会得到一个char (*)[5]类型的指针(指向5char s数组的指针),它与&c[0]具有相同的值,但类型不同(&c[0]char *)。
因为没有指向交换的底层指针,所以你想要的是不可能的。
你能做的最接近的事情就是遍历每个数组元素并交换它。这要求数组具有相同的长度。大概是这样的:

#include <stddef.h>
#include <stdio.h>

void swap_memory(void *a, void *b, size_t n)
{
    char *ca = a;
    char *cb = b;
    char *end_ca = ca + n;
    while (ca != end_ca)
    {
        char tmp = *ca;
        *ca = *cb;
        *cb = tmp;
        ca++;
        cb++;
    }
}

void print_array(char *c, int size) {
    for (int i = 0; i < size; ++i) {
        printf("%c ", c[i]);
    }
    printf("\n");
}

int main() {
    char *h = "hell";
    char *w = "world";
    printf("%s %s\n", h, w);
    swap_memory(&h, &w, sizeof(char *));
    printf("%s %s\n", h, w);

    char c[5] = { 'a', 'e', 'i', 'o', 'u' };
    char d[5] = { '1', '2', '3' };
    swap_memory(c, d, 5); // or `(&c, &d, 5)`, doesn't matter
    print_array(c, 3);
    print_array(d, 5);

    return 0;
}
myss37ts

myss37ts2#

您可以设计一个通用的交换函数来交换任何类型的对象,但是要使交换成为可能,对象必须具有相同的大小。这里的字符串指针确实有相同的大小,sizeof(char *),这与sizeof(void *)相同,所以你的代码应该像预期的那样工作,尽管它违反了严格的别名规则。相反,sizeof(c) != sizeof(d),所以交换这些数组是不可能的,而且,你的交换函数不够通用,不能交换大小不完全是指针大小的对象。
最根本的误解似乎是数组是不是指针。hw是指向char的数组的指针,初始化为指向字符串文字,这些文字是char的(不可修改的)数组,而cd是resp的实际数组。6和4字节。将c传递给一个函数,比如print_array,确实传递了一个指向它们的第一个元素的指针,但这并不能使它们成为指针。
下面是一个修改后的版本,带有一个通用的swap函数,并定义了行为:

#include <stdio.h>

void swap(void *a, void *b, size_t n) {
    unsigned char *aa = a;
    unsigned char *bb = b;
    for (size_t i = 0; i < n; i++) {
        unsigned char cc = aa[i];
        aa[i] = bb[i];
        bb[i] = cc;
    }
}

void print_array(const char *c, size_t size) {
    for (size_t i = 0; i < size; ++i) {
        printf("%c ", c[i]);
    }
    printf("\n");
}

int main(void) {
    const char *h = "hell";
    const char *w = "world";
    printf("%s %s\n", h, w);
    swap(&h, &w, sizeof(h));
    printf("%s %s\n", h, w);

    char c[5] = { 'a', 'e', 'i', 'o', 'u' };
    char d[5] = { '1', '2', '3', '4', '5' };
    print_array(c, sizeof(c));
    print_array(d, sizeof(d));
    swap(c, d, sizeof(c));
    print_array(c, sizeof(c));
    print_array(d, sizeof(d));

    return 0;
}
i1icjdpr

i1icjdpr3#

不能使用同一个函数来交换指针和数组。
例如,要交换两个数组,您需要交换它们的元素。
也就是说,你不能交换地址的范围内存occupeid数组。只能交换数组占用的内存区的内容。
下面是一个简单的演示程序,它显示要交换两个数组的内容,您需要交换它们的元素对

#include <stdio.h>

void print_array( const int a[], size_t n  ) 
{
    while ( n-- )
    {
        printf( "%02d ", *a++ );
    }

    putchar( '\n' );
}

int main( void )
{
    enum { N = 5 };
    int a[N] = { 1, 2, 3 ,4 ,5 };
    int b[N] = { 10, 20, 30 ,40 ,50 };

    print_array( a, N );
    print_array( b, N );

    putchar( '\n' );

    for (size_t i = 0; i < N; i++)
    {
        int tmp = a[i];
        a[i] = b[i];
        b[i] = tmp;
    }

    print_array( a, N );
    print_array( b, N );
}

程序输出为

01 02 03 04 05
10 20 30 40 50

10 20 30 40 50
01 02 03 04 05

至于你的功能

void swap(void **a, void **b) {
    void *tmp;
    tmp = *b;
    *b = *a;
    *a = tmp;
}

那么从这个声明中可以清楚地看到,

void *tmp;

它只交换两个void *类型的指针。例如不能交换两个字符,因为sizeof( char )小于sizeof( void * )
对于错误,例如表达式&c的值是数组c的第一个元素的地址值。
在函数swap中解引用地址的操作

*b = *a;

读取由作为其元素{'a', 'e', 'i', 'o', 'u'};的数组占用的存储器作为导致未定义行为的地址的值。

相关问题