简体   繁体   中英

How do I make a function return a pointer to a new string in C?

I'm reading K&R and I'm almost through the chapter on pointers. I'm not entirely sure if I'm going about using them the right way. I decided to try implementing itoa(n) using pointers. Is there something glaringly wrong about the way I went about doing it? I don't particularly like that I needed to set aside a large array to work as a string buffer in order to do anything, but then again, I'm not sure if that's actually the correct way to go about it in C.

Are there any general guidelines you like to follow when deciding to use pointers in your code? Is there anything I can improve on in the code below? Is there a way I can work with strings without a static string buffer?

/*Source file: String Functions*/
#include <stdio.h>

static char stringBuffer[500];
static char *strPtr = stringBuffer;

/* Algorithm: n % 10^(n+1) / 10^(n) */
char *intToString(int n){
    int p = 1;
    int i = 0;

    while(n/p != 0)
        p*=10, i++;

    for(;p != 1; p/=10)
       *(strPtr++) = ((n % p)/(p/10)) + '0';  
    *strPtr++ = '\0';

    return strPtr - i - 1;
}

int main(){
    char *s[3] = {intToString(123), intToString(456), intToString(78910)};
    printf("%s\n",s[2]);
    int x = stringToInteger(s[2]);

    printf("%d\n", x);

    return 0;
}

Lastly, can someone clarify for me what the difference between an array and a pointer is? There's a section in K&R that has me very confused about it; "5.5 - Character Pointers and Functions." I'll quote it here:

"There is an important difference between the definitions:

 char amessage[] = "now is the time"; /*an array*/ char *pmessage = "now is the time"; /*a pointer*/ 

amessage is an array, just big enough to hold the sequence of characters and '\\0' that initializes it. Individual characters within the array may be changed but amessage will always refer to the same storage. On the other hand, pmessage is a pointer, initialized to point to a string constant; the pointer may subsequently be modified to point elsewhere, but the result is undefined if you try to modify the string contents."

What does that even mean?

Regarding your last question:

char amessage[] = "now is the time"; - is an array. Arrays cannot be reassigned to point to something else (unlike pointers), it points to a fixed address in memory. If the array was allocated in a block, it will be cleaned up at the end of the block (meaning you cannot return such an array from a function). You can however fiddle with the data inside the array as much as you like so long as you don't exceed the size of the array.

Eg this is legal amessage[0] = 'N';

char *pmessage = "now is the time"; - is a pointer. A pointer points to a block in memory, nothing more. "now is the time" is a string literal, meaning it is stored inside the executable in a read only location. You cannot under any circumstances modify the data it is pointing to. You can however reassign the pointer to point to something else.

This is NOT legal - *pmessage = 'N'; - will segfault most likely (note that you can use the array syntax with pointers, *pmessage is equivalent to pmessage[0] ).

If you compile it with gcc using the -S flag you can actually see "now is the time" stored in the read only part of the assembly executable.

One other thing to point out is that arrays decay to pointers when passed as arguments to a function. The following two declarations are equivalent:

void foo(char arr[]);

and

void foo(char* arr);

Regarding your code:

You are using a single static buffer for every call to intToString: this is bad because the string produced by the first call to it will be overwritten by the next.

Generally, functions that handle strings in C should either return a new buffer from malloc , or they should write into a buffer provided by the caller. Allocating a new buffer is less prone to problems due to running out of buffer space.

You are also using a static pointer for the location to write into the buffer, and it never rewinds, so that's definitely a problem: enough calls to this function, and you will run off the end of the buffer and crash.

You already have an initial loop that calculates the number of digits in the function. So you should then just make a new buffer that big using malloc , making sure to leave space for the \\0 , write in to that, and return it.

Also, since i is not just a loop index, change it to something more obvious like length :

That is to say: get rid of the global variables, and instead after computing length :

char *s, *result;

// compute length
s = result = malloc(length+1);
if (!s) return NULL; // out of memory

for(;p != 1; p/=10)
   *(s++) = ((n % p)/(p/10)) + '0';  
*s++ = '\0';
return result;

The caller is responsible for releasing the buffer when they're done with it.

Two other things I'd really recommend while learning about pointers:

  • Compile with all warnings turned on ( -Wall etc) and if you get an error try to understand what caused it; they will have things to teach you about how you're using the language

  • Run your program under Valgrind or some similar checker, which will make pointer bugs more obvious, rather than causing silent corruption

For itoa the length of a resulting string can't be greater than the length of INT_MAX + minus sign - so you'd be safe with a buffer of that length. The length of number string is easy to determine by using log10(number) + 1 , so you'd need buffer sized log10(INT_MAX) + 3 , with space for minus and terminating \\0.

Also, generally it's not a good practice to return pointers to 'black box' buffers from functions. Your best bet here would be to provide a buffer as a pointer argument in intToString , so then you can easily use any type of memory you like (dynamic, allocated on stack, etc.). Here's an example:

char *intToString(int n, char *buffer) {
    // ...        
    char *bufferStart = buffer;
    for(;p != 1; p/=10)
      *(buffer++) = ((n % p)/(p/10)) + '0';  
    *buffer++ = '\0';
    return bufferStart;
}

Then you can use it as follows:

  char *buffer1 = malloc(30);
  char buffer2[15];

  intToString(10, buffer1); // providing pointer to heap allocated memory as a buffer
  intToString(20, &buffer2[0]); // providing pointer to statically allocated memory

what the difference between an array and a pointer is?

The answer is in your quote - a pointer can be modified to be pointing to another memory address. Compare:

int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int *ptrA = &a[0]; // the ptrA now contains pointer to a's first element
ptrA = &b[0];      // now it's b's first element
a = b;             // it won't compile

Also, arrays are generally statically allocated, while pointers are suitable for any allocation mechanism.

关于如何使用指针以及数组和指针之间的区别,建议您阅读“专家c编程”( http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298/ref= sr_1_1?ie = UTF8&qid = 1371439251&sr = 8-1&keywords = expert + c + programming )。

Better way to return strings from functions is to allocate dynamic memory (using malloc) and fill it with the required string...return this pointer to the calling function and then free it.

Sample code :

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define MAX_NAME_SIZE 20

char * func1()
{
    char * c1= NULL;
    c1 = (char*)malloc(sizeof(MAX_NAME_SIZE));
    strcpy(c1,"John");
    return c1;
}

main()
{
    char * c2 = NULL;
    c2 = func1();
    printf("%s \n",c2);
    free(c2);
}

And this works without the static strings.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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