C语言中如何对数组指针进行递增操作?

3

有人能解释一下为什么这个不起作用吗?我是Java开发人员,对C/C++很陌生。
据我所知,数组指针实际上是指向数组的第一个元素的指针,这是正确的吗?

void test(char *tmp)
{
    test2(tmp);
    //*tmp++ = '1';
    //*tmp++ = '2';
    *tmp++ = '3';
    *tmp++ = '4';
    *tmp = 0;
}

void test2(char *tmp)
{
    *tmp++ = '1';
    *tmp++ = '2';
}

int main(int argc, char **argv)
{
    char tmp[5];
    test(tmp);
    printf("%s", tmp);
    return 0;
}

只有 34 被打印出来。当我在函数 test2 中调试此代码时,指针 tmp 增加了一个,但在调用 test2 后回到函数 test 中时,指针 tmp 回到了其初始值。
如果我将所有代码放在单个函数中,就像这样,它可以工作:

void test(char *tmp)
{
    *tmp++ = '1';
    *tmp++ = '2';
    *tmp++ = '3';
    *tmp++ = '4';
    *tmp = 0;
}

同时,最后一行的*tmp = 0是什么意思?我从其他代码中复制了这行代码。没有它,在数组末尾会有一些垃圾数据。

2
  1. 字符串以空字符结尾(因此 *tmp = 0)。
  2. 通过使用 tmp[0] = 等方法使代码易读。
- Ed Heal
@Ed Heal 那个方法是可行的,而且那是我尝试的第一件事,但我不想让 test 依赖于 test2。例如,如果我在 test2 中将 tmp 增加了3次,我希望在 test 中只从第四次继续执行。这可能吗? - pedja
@dasblinkenlight 已修复 - pedja
如果我有4个元素,我需要创建大小为5的数组吗? - pedja
2
@pedja 如果你的字符串中有四个字母,那么你需要一个由五个char组成的数组来容纳空终止符。 - Sergey Kalinichenko
5个回答

4
在函数test2中,传递指针意味着您可以修改tmp所指向的内容,但test2本身不会修改指针本身
void test2(char *tmp)
{
    *tmp++ = '1';
    *tmp++ = '2';
}

因此,在调用test2之后,在调用函数的test中,指针并没有像您期望的那样向前移动两个位置,而是tmp保持不变(它指向数组的第一个值)。实际发生的是,test2为前两个元素赋值为12,然后test34覆盖了这两个值。

void test(char *tmp)
{
    test2(tmp);
    *tmp++ = '3';
    *tmp++ = '4';
    *tmp = 0;
}

这是字符串终止符。在最后一行,*tmp = 0 是将字符串的结尾标记为0。

我现在明白了,但是我怎么能让它像这样工作,而不使用 tmp[0]tmp[1] 等呢? - pedja

2
在test2()中增加'tmp'并不会增加test()中的'tmp'。在test2()调用返回后,test2写入的'1'和'2'被'3'和'4'覆盖。
数字0是C风格字符串终止符'\0'。

那么我如何在不明确指定数组索引的情况下完成这个操作呢?例如 tmp[0]tmp[1] 等等。 - pedja
2
一种方法是从test2()返回指针:'char *test2(char *tmp)', ..... 'return tmp;',然后从temp中调用它:'tmp=test2(tmp);' - Martin James

1

函数参数是其本地变量。参数通过值传递给函数,这意味着参数的副本被传递。

因此,在函数test2中,使用了函数test的指针tmp的副本。对对象副本的任何更改都不会影响对象本身。

可以想象调用test2的方式如下:

void test(char *tmp)
{
//  test2(tmp);
    char *new_ptr = tmp;
    test2( new_ptr );
//  ...
}

void test2(/*char *tmp*/)
{
    char *tmp = new_ptr;
    *tmp++ = '1';
    *tmp++ = '2';
}

如果你将test2的参数声明为指针的引用,那么在这种情况下指针本身是可以被改变的。例如:
void test(char *tmp)
{
    test2( &tmp );
    //     ^^^^
    //*tmp++ = '1';
    //*tmp++ = '2';
    *tmp++ = '3';
    *tmp++ = '4';
    *tmp = 0;
}

void test2(char **tmp)
//         ^^^^^^^^^^
{
    *( *tmp )++ = '1';
    *( *tmp )++ = '2';
}

关于数组,除了在使用 sizeof 运算符时,数组被隐式转换为其第一个元素的指针之外,数组通常用于表达式中。
因此,如果您有一个类型为 T 的数组,则该数组将被隐式转换为其第一个元素的指针。
T a[N];

接下来是以下声明

T *p = a;

如果有效并且指针被初始化为数组 a 的第一个元素的地址,则为有效。

另一个例子

char *s = "Hello";

这里使用字符串字面值"Hello"作为初始化表达式。包括终止零'\0'的字符串字面值具有类型char[6],隐式转换为指向其第一个字符'H'的指针。

在C++中,字符串字面值具有常量字符数组的类型。因此,在C++中,字符串字面值"Hello"的类型为const char[6]

因此,在C++中你需要写

const char *s = "Hello";

但是在这两种语言中,字符串字面值都是不可变的。


1

0是空终止符,在字符串末尾以0或空字节结束,这样像printf()这样的函数就知道字符串在哪里结束了,因为有时候字符串有多长谁也说不准。

由于您将指针传递给test2(),而test2()正在增加指针 - 但实际上没有将更改/增加后的指针传回调用函数...对于test()来说,指针没有变化,因此随后的更改正在覆盖先前的更改。

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

char *test2(char *tmp)
{
   *tmp++ = '1';
   *tmp++ = '2';

   return tmp;
}

void test(char *tmp)
{
    tmp = test2(tmp);

    //*tmp++ = '1';
    //*tmp++ = '2';
    *tmp++ = '3';
    *tmp++ = '4';
    *tmp = 0;
}

int main(int argc, char **argv)
{
    char tmp[5];

    test(tmp);
    printf("%s", tmp);

    return 0;
}

这里的test2返回的是指针的修改/增量给调用者,tess()将会接收到更新后的字符。由于你在末尾添加了0或nul字节,因此你需要至少有5个元素的数组。

上述代码对你应该可以正常工作。


0

如果您将所有内容都写在一个函数中,那么得到的结果可能是1234。但是,如果您编写两个函数并始终传递相同的指针,则第二个函数会覆盖第一个函数已经完成的内容。

test:
tmp -> Point to [100]
call test2 giving [100] as parameter
write 3 in [100]
increment pointer from [100] to [101]
write 4 in [101]
increment pointer from [101] to [102]

test2:
tmp -> Point to [100]
write 1 in [100]
increment pointer from [100] to [101]
write 2 in [101]
increment pointer from [101] to [102]

现在我想你可以看到错误了。


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