简体   繁体   English

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

[英]Am I passing a copy of my char array, or a pointer?

I've been studying C, and I decided to practice using my knowledge by creating some functions to manipulate strings. 我一直在学习C,我决定通过创建一些操作字符串的函数来练习我的知识。 I wrote a string reverser function, and a main function that asks for user input, sends it through stringreverse(), and prints the results. 我写了一个字符串反转器函数,以及一个要求用户输入的主函数,通过stringreverse()发送它,并打印结果。

Basically I just want to understand how my function works. 基本上我只想了解我的功能是如何工作的。 When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? 当我用'tempstr'作为第一个参数调用它时,它是否被理解为数组中第一个元素的地址? Basically like saying &tempstr[0], right? 基本上就像说&tempstr [0],对吧?

I guess answering this question would tell me: Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now? 我想回答这个问题会告诉我:如果我将一个char *指针分配给我的tempstr数组,然后将它作为第一个参数发送给stringreverse(),那么会有什么区别,而不是我现在这样做了吗? I want to know whether I'm sending a duplicate of the array tempstr, or a memory address. 我想知道我是否发送了数组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;
}

Thanks for your time. 谢谢你的时间。 Any other critiques would be helpful . 任何其他批评都会有所帮助。 . only a few weeks into programming :P 编程只有几周时间:P

EDIT: Thanks to all the answers, I figured it out. 编辑:感谢所有的答案,我想通了。 Here's my solution for anyone wondering: 这是我的解决方案,任何人都想知道:

#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];
    }
}

When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? 当我用'tempstr'作为第一个参数调用它时,它是否被理解为数组中第一个元素的地址? Basically like saying &tempstr[0] , right? 基本上就像说&tempstr[0] ,对吧?

char tempstr[1024];

tempstr is an array of characters. tempstr是一个字符数组。 When passed tempstr to a function, it decays to a pointer pointing to first element of tempstr. tempstr传递给函数时,它会衰减到指向tempstr的第一个元素的指针。 So, its basically same as sending &tempstr[0] . 所以,它与发送&tempstr[0]基本相同。

Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now? 如果我将一个char *指针指向我的tempstr数组然后将其作为第一个参数发送给stringreverse(),那么会有什么不同吗,而不是我现在正在做什么?

No difference. 没有不同。 You might do - 你可能会 -

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

char *revstr = stringreverse(tempstr, revstr); 

First right side expression's is evaluavated and the return value is assigned to revstr . 评估第一个右侧表达式,并将返回值分配给revstr But what is revstr that is being passed. 但是什么是正在通过的revstr Program should allocate memory for it. 程序应该为它分配内存。

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

Now, when passing tempstr and revstr , they decayed to pointers pointing to their respective first indexes. 现在,当传递tempstrrevstr ,它们会衰减到指向各自第一个索引的指针。 In that case why this would go wrong - 在这种情况下,为什么会出错 -

revstr = stringreverse(tempstr, revstr) ;

Just because arrays are not pointers . 仅仅因为数组不是指针 char* is different from char[]. char *与char []不同。 Hope it helps ! 希望能帮助到你 !

Basically I just want to understand how my function works. 基本上我只想了解我的功能是如何工作的。

One problem you have is that you are using revstr without initializing it or allocating memory for it. 您遇到的一个问题是您使用revstr而不初始化它或为其分配内存。 This is undefined behavior since you are writing into memory doesn't belong to you. 这是未定义的行为,因为您写入内存不属于您。 It may appear to work, but in fact what you have is a bug and can produce unexpected results at any time. 它似乎可行,但实际上你所拥有的是一个bug,可以随时产生意想不到的结果。

When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? 当我用'tempstr'作为第一个参数调用它时,它是否被理解为数组中第一个元素的地址? Basically like saying &tempstr[0], right? 基本上就像说&tempstr [0],对吧?

Yes. 是。 When arrays are passed as arguments to a function, they are treated as regular pointers, pointing to the first element in the array. 当数组作为参数传递给函数时,它们被视为常规指针,指向数组中的第一个元素。 There is no difference if you assigned &temp[0] to a char* before passing it to stringreverser , because that's what the compiler is doing for you anyway. 如果在将char* &temp[0]传递给stringreverser之前将其分配给char*没有区别,因为无论如何编译器正在为你做什么。

The only time you will see a difference between arrays and pointers being passed to functions is in C++ when you start learning about templates and template specialization. 当您开始学习模板和模板特化时,唯一一次您将看到传递给函数的数组和指针之间的区别。 But this question is C, so I just thought I'd throw that out there. 但这个问题是C,所以我只是想把它扔出去。

First, a detail: 一,细节:

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

That prototype should go outside main(), like this: 该原型应该在main()之外,如下所示:

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

int main()
{

As to your main question: the variable tempstr is a char*, ie the address of a character. 至于你的主要问题:变量tempstr是一个char *,即一个字符的地址。 If you use C's index notation, like tempstr[i], that's essentially the same as *(tempstr + i). 如果你使用C的索引表示法,比如tempstr [i],它与*(tempstr + i)基本相同。 The same is true of revstr, except that in that case you're returning the address of a block of memory that's about to be clobbered when the array it points to goes out of scope. revstr也是如此,除了在那种情况下,当你指向的数组超出范围时,你将返回一块即将被破坏的内存块的地址。 You've got the right idea in passing in the address of some memory into which to write the reversed string, but you're not actually copying the data into the memory pointed to by that block. 你有一个正确的想法,传递一些内存的地址写入反向字符串,但你实际上并没有将数据复制到该块指向的内存中。 Also, the line: 另外,该行:

returnptr = &revstr[0];

Doesn't do what you think. 不做你的想法。 You can't assign a new pointer to returnptr; 你不能为returnptr分配一个新的指针; if you really want to modify returnptr, you'll need to pass in its address, so the parameter would be specified char** returnptr. 如果你真的想修改returnptr,你需要传入它的地址,所以参数将被指定为char ** returnptr。 But don't do that: instead, create a block in your main() that will receive the reversed string, and pass its address in the returnptr parameter. 但是不要这样做:相反,在main()中创建一个块,它将接收反向字符串,并在returnptr参数中传递其地址。 Then, use that block rather than the temporary one you're using now in stringreverse(). 然后,使用该块而不是您现在在stringreverse()中使用的临时块。

In response to your question about whether the thing passed to the function is an array or a pointer, the relevant part of the C99 standard (6.3.2.1/3) states: 在回答你关于传递给函数的东西是数组还是指针的问题时,C99标准(6.3.2.1/3)的相关部分指出:

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. 除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为''array of type''的表达式转换为类型为''指针的表达式type''指向数组对象的初始元素,而不是左值。

So yes, other than the introduction of another explicit variable, the following two lines are equivalent: 所以是的,除了引入另一个显式变量之外,以下两行是等价的:

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

As to a critique, I'd like to raise the following. 至于批评,我想提出以下几点。


While legal, I find it incongruous to have function prototypes (such as stringreverse ) anywhere other than at file level. 虽然合法,但我发现将函数原型(例如stringreverse )放在文件级以外的任何地方都是stringreverse In fact, I tend to order my functions so that they're not usually necessary, making one less place where you have to change it, should the arguments or return type need to be changed. 事实上,我倾向于命令我的函数,以便它们通常不是必需的,如果需要更改参数或返回类型,则需要更少的地方来更改它。 That would entail, in this case, placing stringreverse before main . 这将意味着,在这种情况下,将stringreverse main


Don't ever use gets in a real program.. It's unprotectable against buffer overflows. 永远不要使用gets的真正的程序。它是对缓冲区溢出unprotectable。 At a minimum, use fgets which can be protected, or use a decent input function such as the one found here . 至少,使用可以保护的fgets ,或者使用像这里找到的那样的合适的输入功能。


You cannot create a local variable within stringreverse and pass back the address of it. 您无法在stringreverse创建局部变量并传回它的地址。 That's undefined behaviour. 这是未定义的行为。 Once that function returns, that variable is gone and you're most likely pointing to whatever happens to replace it on the stack the next time you call a function. 一旦该函数返回,该变量就消失了,并且您很可能指向下次调用函数时在堆栈上替换它的任何事件。


There's no need to pass in the revstr variable either. 也没有必要传递revstr变量。 If it were a pointer with backing memory (ie, had space allocated for it), that would be fine but then there would be no need to return it. 如果它是一个带有后备内存的指针(即,为它分配了空间),那就没问题,但是没有必要返回它。 In that case you would allocate both in the caller: 在这种情况下,您将在调用者中分配两个

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

You should also try to avoid magic numbers like 1024 . 您还应该尝试避免像1024这样的幻数。 Better to have lines like: 最好有这样的行:

#define BUFFSZ 1024
char tempstr[BUFFSZ];

so that you only need to change it in one place if you ever need a new value (that becomes particularly important if you have lots of 1024 numbers with different meanings - global search and replace will be your enemy in that case rather than your friend). 如果您需要一个新值,那么您只需要在一个地方更改它(如果您有大量1024具有不同含义的数字,那就变得特别重要 - 在这种情况下,全局搜索和替换将成为您的敌人 ,而不是您的朋友) 。


In order to make you function more adaptable, you may want to consider allowing it to handle any length. 为了使您的功能更具适应性,您可能需要考虑允许它处理任何长度。 You can do that by passing both buffers in, or by using malloc to dynamically allocate a buffer for you, something like: 您可以通过传入两个缓冲区,或者使用malloc为您动态分配缓冲区来实现这一点,例如:

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

This puts the responsibility for freeing that memory on the caller but that's a well-worn way of doing things. 这样就有责任将这些记忆释放给呼叫者,但这是一种陈旧的做事方式。


You should probably use one of the two canonical forms for main : 你可能应该使用main的两个规范形式之一:

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

It's also a particularly bad idea to call main from anywhere. 任何地方呼叫main也是一个特别糟糕的主意 While that may look like a nifty way to get an infinite loop, it almost certainly will end up chewing up your stack space :-) 虽然这可能看起来像一个获得无限循环的好方法,但它几乎肯定会最终咀嚼你的堆栈空间:-)


All in all, this is probably the function I'd initially write. 总而言之,这可能是我最初写的功能。 It allows the user to populate their own buffer if they want, or to specify they don't have one, in which case one will be created for them: 它允许用户在需要时填充自己缓冲区,或者指定它们没有缓冲区,在这种情况下,将为它们创建一个缓冲区:

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;
}

In your stringreverse() function, you are returning the address of a local variable ( revstr ). stringreverse()函数中,您将返回局部变量的地址( revstr )。 This is undefined behaviour and is very bad. 这是未定义的行为 ,非常糟糕。 Your program may appear to work right now, but it will suddenly fail sometime in the future for reasons that are not obvious. 你的程序现在似乎可以正常工作,但由于不明显的原因,它将在未来的某个时间突然失败。

You have two general choices: 您有两个一般选择:

  1. Have stringreverse() allocate memory for the returned string, and leave it up to the caller to free it. stringreverse()为返回的字符串分配内存,并将其留给调用者以释放它。
  2. Have the caller preallocate space for the returned string, and tell stringreverse() where it is and how big it is. 让调用者为返回的字符串预分配空间,并告诉stringreverse()它在哪里以及它有多大。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM