我传递了我的char数组或指针的副本吗?

时间:2011-03-23 03:46:20

标签: c

我一直在学习C,我决定通过创建一些操作字符串的函数来练习我的知识。我写了一个字符串反转器函数,以及一个要求用户输入的主函数,通过stringreverse()发送它,并打印结果。

基本上我只是想了解我的功能是如何工作的。当我用'tempstr'作为第一个参数调用它时,它是否被理解为数组中第一个元素的地址?基本上就像说& tempstr [0],对吧?

我想回答这个问题就会告诉我:如果我将一个char *指针指向我的tempstr数组然后将其作为第一个参数发送给stringreverse(),那么会有什么不同吗,而不是我现在正在做什么?我想知道我是否正在发送数组tempstr或内存地址的副本。

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

int main()
{
    char* stringreverse(char* tempstr, char* returnptr);

    printf("\nEnter a string:\n\t");

    char tempstr[1024];

    gets(tempstr);
    char *revstr = stringreverse(tempstr, revstr); //Assigns revstr the address of the first character of the reversed string.

    printf("\nReversed string:\n"
           "\t%s\n", revstr);

    main();
    return 0;
}

char* stringreverse(char* tempstr, char* returnptr)
{
    char revstr[1024] = {0};

    int i, j = 0;

    for (i = strlen(tempstr) - 1; i >= 0; i--, j++)
    {
        revstr[j] = tempstr[i]; //string reverse algorithm
    }

    returnptr = &revstr[0];
    return returnptr;
}

感谢您的时间。任何其他批评都会有所帮助。 。编程只有几周时间:P

编辑:感谢所有答案,我明白了。对于任何想知道的人来说,这是我的解决方案:

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

void stringreverse(char* s);

int main(void)
{
    printf("\nEnter a string:\n\t");

    char userinput[1024] = {0}; //Need to learn how to use malloc() xD

    gets(userinput);
    stringreverse(userinput);

    printf("\nReversed string:\n"
           "\t%s\n", userinput);

    main();
    return 0;
}

void stringreverse(char* s)
{
    int i, j = 0;
    char scopy[1024]; //Update to dynamic buffer
    strcpy(scopy, s);

    for (i = strlen(s) - 1; i >= 0; i--, j++)
    {
        *(s + j) = scopy[i];
    }
}

5 个答案:

答案 0 :(得分:1)

  

当我用'tempstr'作为第一个参数调用它时,是否应将其理解为   数组中第一个元素的地址?基本上就像说&tempstr[0],   正确?

char tempstr[1024];

tempstr是一个字符数组。当将tempstr传递给函数时,它会衰减到指向tempstr的第一个元素的指针。所以,它与发送&tempstr[0]基本相同。

  

如果我将一个char *指针指向我的tempstr数组然后将其作为第一个参数发送给stringreverse(),那么会有什么不同吗,而不是我现在正在做什么?

没有区别。你可能会这样做 -

char* pointer = tempstr ; // And can pass pointer

char *revstr = stringreverse(tempstr, revstr); 

评估第一个右侧表达式,并将返回值分配给revstr。但是正在通过的revstr是什么。程序应该为它分配内存。

char revstr[1024] ;
char *retValue = stringreverse(tempstr, revstr) ;
     // ^^^^^^ changed to be different.

现在,当传递tempstrrevstr时,它们会衰减到指向各自第一个索引的指针。在那种情况下,为什么会出错 -

revstr = stringreverse(tempstr, revstr) ;

仅仅因为数组不是指针。 char *与char []不同。希望它有所帮助!

答案 1 :(得分:1)

  
    

基本上我只是想了解我的功能是如何工作的。

  

您遇到的一个问题是您使用revstr而未初始化或为其分配内存。这是未定义的行为,因为您写入内存不属于您。它似乎可行,但实际上你所拥有的是一个bug,可以随时产生意想不到的结果。

  
    

当我用'tempstr'作为第一个参数调用它时,它是否被理解为数组中第一个元素的地址?基本上就像说&amp; tempstr [0],对吧?

  

是。当数组作为参数传递给函数时,它们被视为常规指针,指向数组中的第一个元素。如果在将&temp[0]传递给char*之前将stringreverser分配给{{1}}没有区别,因为这就是编译器为您做的事情。

当您开始学习模板和模板专业化时,唯一一次看到传递给函数的数组和指针之间存在差异。但是这个问题是C,所以我只是想把它扔出去。

答案 2 :(得分:1)

首先,详细说明:

int main()
{
    char* stringreverse(char* tempstr, char* returnptr);

该原型应该在main()之外,如下所示:

char* stringreverse(char* tempstr, char* returnptr);

int main()
{

关于你的主要问题:变量tempstr是一个char *,即一个字符的地址。如果你使用C的索引表示法,比如tempstr [i],它与*(tempstr + i)基本相同。 revstr也是如此,除了在那种情况下,当你指向的数组超出范围时,你将返回一块即将被破坏的内存块的地址。你有一个正确的想法,传递一些内存的地址写入反向字符串,但你实际上并没有将数据复制到该块指向的内存中。这一行:

returnptr = &revstr[0];

不按你的想法行事。你不能为returnptr分配一个新的指针;如果你真的想修改returnptr,你需要传入它的地址,所以参数将被指定为char ** returnptr。但是不要这样做:相反,在main()中创建一个块,它将接收反向字符串,并在returnptr参数中传递其地址。然后,使用该块而不是您现在在stringreverse()中使用的临时块。

答案 3 :(得分:1)

在回答你关于传递给函数的东西是数组还是指针的问题时,C99标准(6.3.2.1/3)的相关部分指出:

  

除非它是sizeof运算符或一元&amp;的操作数。运算符,或者是用于初始化数组的字符串文字,具有类型''数组类型''的表达式将转换为类型为''指向类型'的指针的表达式,指向数组对象的初始元素,不是左值。

所以是的,除了引入另一个显式变量之外,以下两行是等价的:

char x[] = "abc";                     fn (x);
char x[] = "abc"; char *px = &(x[0]); fn (px);

关于批评,我想提出以下几点。


虽然合法,但我觉得将函数原型(例如stringreverse)放在文件级以外的任何地方都是不合适的。事实上,我倾向于命令我的函数,以便它们通常不是必需的,如果需要更改参数或返回类型,则需要更少的地方来更改它。在这种情况下,这需要在 stringreverse之前放置main


不要永远在实际程序中使用gets。它对缓冲区溢出无法保护。至少使用可以受到保护的fgets,或者使用一个像here找到的输入功能。{/ p>


您无法在stringreverse中创建局部变量并传回其地址。这是未定义的行为。一旦该函数返回,该变量消失并且您很可能指向下次调用函数时在堆栈上替换它的任何事情。


也无需传递revstr变量。如果它是一个带有后备内存的指针(即,为它分配了空间),那就没问题,但是没有必要返回它。在这种情况下,您将在调用者中分配两个

char tempstr[1024];
char revstr[1024];
stringreverse (tempstr, revstr); // Note no return value needed
                                 //  since you're manipulating revstr directly.

您还应该尝试避免像1024这样的幻数。最好有这样的行:

#define BUFFSZ 1024
char tempstr[BUFFSZ];

因此,如果您需要新值,则只需在一个位置进行更改(如果您有大量具有不同含义的1024个数字,这一点就变得尤为重要 - 全局搜索在这种情况下,替换将是你的敌人,而不是你的朋友。)


为了使您的功能更具适应性,您可能需要考虑允许它处理任何长度。您可以通过传入两个缓冲区,或者使用malloc为您动态分配缓冲区来实现这一点,例如:

char *reversestring (char *src) {
    char *dst = malloc (strlen (src) + 1);
    if (dst != NULL) {
        // copy characters in reverse order.
    }
    return dst;
}

这使得将这些记忆释放给来电者的责任,但这是一种陈旧的做事方式。


您应该使用main的两种规范形式之一:

int main (int argc, char *argv[]);
int main (void);

在任何地方从调用main也是一个特别糟糕的主意。虽然这可能看起来就像一个获得无限循环的好方法,但几乎可以肯定最终将占用你的筹码空间: - )


总而言之,这可能是我最初写的功能。它允许用户填写他们的自己的缓冲区,或者指定他们没有,在这种情况下将为他们创建一个:

char *revstr (char *src, char *dst) {
    // Cache size in case compiler not smart enough to do so.
    // Then create destination buffer if none provided.

    size_t sz = strlen (src);
    if (dst == NULL) dst = malloc (sz + 1);

    // Assuming buffer available, copy string.

    if (dst != NULL) {
        // Run dst end to start, null terminator first.

        dst += sz; *dst = '\0';

        // Copy character by character until null terminator in src.
        // We end up with dst set to original correct value.

        while (*src != '\0')
            *--dst = *src++;
    }

    // Return reversed string (possibly NULL if malloc failed).

    return dst;
}

答案 4 :(得分:0)

stringreverse()函数中,您将返回局部变量的地址(revstr)。这是未定义的行为,非常糟糕。您的程序可能出现现在可以正常工作,但由于不明显的原因,它将来会突然失败。

您有两种常规选择:

  1. stringreverse()为返回的字符串分配内存,并将其留给调用者以释放它。
  2. 让调用者为返回的字符串预分配空间,并告诉stringreverse()它在哪里以及它有多大。