在C语言中交换指针(char,int)

43

我一直在努力理解在C语言中交换指针时的不同行为。如果我想要交换两个int指针,那么可以这样做:

void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

然而,如果我想要交换两个char指针,我需要像这样做:

void charSwap(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

因为如果我这样做

void charSwap(char* a, char* b){
    char temp = *a;
    *a = *b;
    *b = temp;
}
编译器报错,因为它无法更改值而表达式*a = *b试图更改它们。 如果我想交换两个字符串(即char* s1=“Hello”;char* s2=”Bye”;),应该怎么做? 你能给我一点帮助吗?我真的想了解它是如何工作的,这样我就不需要一次又一次地尝试错误,直到得到正确的答案。 希望这对很多其他人也有用。

16
第一个例子没有交换两个 int 指针。我认为这是你困惑的根源。 - R. Martinho Fernandes
@R.MartinhoFernandes 我想你可能是对的。 - Manolete
6个回答

143

首先你需要明白的是,当你把某个东西传递给一个函数时,这个东西会被复制到函数的参数中。

假设你有以下代码:

void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    assert(a == 17);
    assert(b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap1(x, y);
assert(x == 42);
assert(y == 17);
// no, they're not swapped!

原始变量不会被交换,因为它们的值被复制到函数的参数中。然后函数继续交换这些参数的值,然后返回。原始值没有改变,因为函数只交换了自己的私有副本。
现在我们该如何解决这个问题呢?函数需要一种引用原始变量而不是它们的值副本的方法。在C语言中,我们如何引用其他变量?使用指针。
如果我们将指向变量的指针传递到函数中,函数可以交换“我们”的变量的值,��不是其自己的参数副本的值。
void swap2(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap2(&x, &y); // give the function pointers to our variables
assert(x == 17);
assert(y == 42);
// yes, they're swapped!

请注意,在函数内部,我们没有将值分配给指针,而是将其分配给它们所指向的内容。这些指针指向我们的变量 xy。该函数通过我们提供的指针直接更改了存储在我们自己的变量中的值。这正是我们所需要的。
现在,如果我们有两个指针变量,并且想要交换指针本身(而不是它们所指向的值),会发生什么情况?如果我们传递指针,那么指针将简单地被复制到参数中(而不是它们所指向的值)。
void swap3(int* a, int* b) {
    int* temp = a;
    a = b;
    b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}
void swap4(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
int* xp = &x;
int* yp = &y;
swap3(xp, yp);
assert(xp == &x);
assert(yp == &y);
assert(x == 42);
assert(y == 17);
// Didn't swap anything!
swap4(xp, yp);
assert(xp == &x);
assert(yp == &y);
assert(x == 17);
assert(y == 42);
// Swapped the stored values instead!

函数swap3只交换其参数中得到的指针的私有副本。这与swap1存在相同的问题。而swap4改变了我们的变量所指向的值,而不是指针!我们给函数提供了一种引用变量xy的方法,但我们希望它们引用xpyp

我们如何做到这一点?我们传递它们的地址!

void swap5(int** a, int** b) {
    int* temp = *a;
    *a = *b;
    *b = temp;
    assert(**a == 17);
    assert(**b == 42);
    // they're swapped!
}


int x = 42;
int y = 17;
int* xp = &x;
int* yp = &y;
swap5(&xp, &yp);
assert(xp == &y);
assert(yp == &x);
assert(x == 42);
assert(y == 17);
// swapped only the pointers variables

这样它就交换了我们的指针变量(注意现在xp指向y),但没有交换它们所指向的值。我们为它提供了一种引用我们的指针变量的方式,因此它可以更改它们!
现在应该很容易理解如何交换两个以char*变量形式表示的字符串。交换函数需要接收char*指针。
void swapStrings(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
    assert(strcmp(*a, "world") == 0);
    assert(strcmp(*b, "Hello") == 0);
}

char* x = "Hello";
char* y = "world";
swapStrings(&x, &y);
assert(strcmp(x, "world") == 0);
assert(strcmp(y, "Hello") == 0);

2
也许只关注一件事情会更好,但是为了这个例子的正确性,我们需要swapStrings(const char ** a, const char ** b)函数。 - u0b34a0f6ae
2
@kaizer.se 谢谢您注意到这一点!我从答案中删除了 const 以避免混淆。 - R. Martinho Fernandes

10
void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

您需要知道以下内容:

int a = 5; // an integer, contains value
int *p; // an integer pointer, contains address
p = &a; // &a means address of a
a = *p; // *p means value stored in that address, here 5

void charSwap(char* a, char* b){
    char temp = *a;
    *a = *b;
    *b = temp;
}

因此,当您像这样交换时,只有值会被交换。对于char*,只有它们的第一个char会被交换。

现在,如果您清楚地了解char*(字符串),那么您应该知道,您只需要交换指针。如果您将其视为array而不是字符串,则更容易理解。

void stringSwap(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

所以,这里传递双指针是因为数组本身的开头就是一个指针。


4
在C语言中,字符串是一个字符指针(char *)。如果您想要交换两个字符串,实际上是在交换两个字符指针,也就是说交换两个地址。要在函数中进行任何交换操作,都需要给它传递您要交换的两个事物的地址。因此,在交换两个指针的情况下,您需要一个指向指针的指针。就像交换int一样,只需要一个指向int的指针。
您上面的代码片段之所以不能正常工作,是因为您希望它交换两个char指针,但它实际上写的是交换两个字符!
编辑:如R. Martinho Fernandes所指出的那样,在您上面的示例中,您尝试错误地交换了两个int指针。如果您有:
int a, b;
intSwap(&a, &b);

1

你需要理解传值和传引用之间的区别。

基本上,C语言只支持传值。因此,当将变量传递给函数时,无法直接引用变量。如果要在函数外部更改变量(例如swap操作),则需要使用传引用。要在C语言中实现传引用,需要使用指针,可以通过解除引用来获取值。

该函数:

void intSwap(int* a, int* b)

它将两个指针的值传递给intSwap,并在函数中交换a/b指向的值,但不交换指针本身。这就是为什么R. Martinho和Dan Fego说它交换了两个整数而不是指针的原因。

对于字符来说,我认为你意思是字符串,更加复杂。在C中,字符串被实现为一个字符数组,由char*引用作为字符串值。如果您想通过引用传递char*,则需要使用char*的指针,因此得到char**。

也许下面的代码更清楚:

typedef char* str;
void strSwap(str* a, str* b);

函数语法 swap(int& a, int& b) 是 C++ 中的一种,意味着直接通过引用传递。也许一些 C 编译器也实现了这个功能。

希望我能更清楚地表达,不要让人感到困惑。


1

这个示例不会交换两个int指针。它交换了papb所指向的整数的值。下面是调用此函数时发生的情况的示例:

void Swap1 (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

int main()
{
    int a = 42;
    int b = 17;


    int *pa = &a;
    int *pb = &b;

    printf("--------Swap1---------\n");
    printf("a = %d\n b = %d\n", a, b);
    swap1(pa, pb);
    printf("a = %d\n = %d\n", a, a);
    printf("pb address =  %p\n", pa);
    printf("pa address =  %p\n", pb);
}

这里的输出是:
a = 42
b = 17
pa address =  0x7fffdf933228
pb address =  0x7fffdf93322c
--------Swap---------
pa = 17
pb = 42
a = 17
b = 42
pa address =  0x7fffdf933228
pb address =  0x7fffdf93322c

请注意,值被交换了,但指针地址并没有交换!
为了交换地址,我们需要这样做:
void swap2 (int **pa, int **pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

在主函数中调用函数的方式如下:swap2(&pa, &pb);

现在地址已经交换,指针所指向的值也随之交换。变量ab的值与初始化时相同。整数a和b没有交换,因为swap2交换的是指针所指向的地址!

a = 42
b = 17
pa address =  0x7fffddaa9c98
pb address =  0x7fffddaa9c9c
--------Swap---------
pa = 17
pb = 42
a = 42
b = 17
pa address =  0x7fffddaa9c9c
pb address =  0x7fffddaa9c98

由于C语言中的字符串是char指针,因此您要交换字符串时实际上是在交换char指针。就像使用int的示例一样,您需要一个双指针来交换地址。

即使地址不同,整数的值也可以交换,但是字符串按定义是字符指针。如果参数是单指针,则可以交换一个char,但是为了交换字符串,字符指针需要是双指针。


0
如果您有幸能够使用C ++进行编程,请使用以下代码:
template<typename T>
void swapPrimitives(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

当然,在char*的情况下,它只会交换指针本身,而不是它们所指向的数据,但在大多数情况下,这是可以接受的,对吧?


6
如果您有幸能够使用C++进行工作,您可以使用std::swap来完成这个操作! - Qqwy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接