0

我正在阅读 K&R,我几乎读完了关于指针的章节。我不完全确定我是否要以正确的方式使用它们。我决定尝试使用指针实现 itoa(n)。我做这件事的方式有什么明显的错误吗?我不是特别喜欢我需要留出一个大数组来作为字符串缓冲区来做任何事情,但话又说回来,我不确定这是否真的是在 C 中处理它的正确方法。

在决定在代码中使用指针时,您是否喜欢遵循任何一般准则?下面的代码有什么可以改进的吗?有没有一种方法可以在没有静态字符串缓冲区的情况下使用字符串?

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

最后,有人可以为我澄清一下数组和指针之间的区别是什么?K&R 中有一段让我很困惑;“5.5 - 字符指针和函数。” 我将在这里引用它:

“定义之间有一个重要区别:

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

message 是一个数组,刚好足以容纳字符序列和初始化它的 '\0'。数组中的单个字符可能会更改,但消息将始终引用相同的存储。另一方面,pmessage 是一个指针,初始化为指向一个字符串常量;指针随后可能会被修改为指向别处,但如果您尝试修改字符串内容,则结果未定义。”

那有什么意思?

4

5 回答 5

1

关于您的代码:

每次调用 intToString 时都使用一个静态缓冲区:这很糟糕,因为第一次调用它产生的字符串将被下一次覆盖。

通常,在 C 中处理字符串的函数应该从 中返回一个新缓冲区malloc,或者它们应该写入调用者提供的缓冲区。由于缓冲区空间不足,分配新缓冲区不太容易出现问题。

您还使用静态指针作为写入缓冲区的位置,并且它永远不会倒带,所以这绝对是一个问题:对该函数的调用次数过多,您将跑出缓冲区的末尾并崩溃。

您已经有一个初始循环来计算函数中的位数。因此,您应该使用 新建一个那么大的缓冲区,malloc确保为 留出空间\0,写入该缓冲区并返回它。

此外,由于i不仅仅是循环索引,请将其更改为更明显的内容,例如length

也就是说:去掉全局变量,取而代之的是经过计算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;

调用者负责在完成后释放缓冲区。

在学习指针时,我真的推荐另外两件事:

  • 编译时打开所有警告(-Wall等),如果出现错误,请尝试了解导致错误的原因;他们会教你如何使用语言

  • 在 Valgrind 或类似的检查器下运行你的程序,这将使指针错误更加明显,而不是导致静默损坏

于 2013-06-17T03:28:40.837 回答
1

对于itoa结果字符串的长度不能大于 INT_MAX + 减号的长度 - 所以使用该长度的缓冲区是安全的。数字字符串的长度很容易通过使用来确定log10(number) + 1,因此您需要缓冲区大小log10(INT_MAX) + 3,其中包含减号和终止 \0 的空间。

此外,通常从函数返回指向“黑盒”缓冲区的指针不是一个好习惯。您最好的选择是提供一个缓冲区作为 in 中的指针参数intToString,这样您就可以轻松地使用您喜欢的任何类型的内存(动态、在堆栈上分配等)。这是一个例子:

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

然后你可以按如下方式使用它:

  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

数组和指针有什么区别?

答案就在您的报价中-可以将指针修改为指向另一个内存地址。相比:

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

此外,数组通常是静态分配的,而指针适用于任何分配机制。

于 2013-06-17T03:31:21.113 回答
1

关于你的最后一个问题:

char amessage[] = "now is the time";- 是一个数组。数组不能被重新分配以指向其他东西(与指针不同),它指向内存中的固定地址。如果数组是在一个块中分配的,它将在块的末尾被清理(这意味着你不能从函数中返回这样的数组)。但是,只要不超过数组的大小,您就可以随意摆弄数组中的数据。

例如这是合法的amessage[0] = 'N';

char *pmessage = "now is the time";- 是一个指针。指针指向内存中的一个块,仅此而已。"now is the time"是一个字符串文字,这意味着它存储在可执行文件中的只读位置。在任何情况下,您都不能修改它指向的数据。但是,您可以重新分配指针以指向其他内容。

这是不合法的 - *pmessage = 'N';- 很可能会出现段错误(请注意,您可以将数组语法与指针一起使用,*pmessage相当于pmessage[0])。

如果您使用 gcc 使用-S标志编译它,您实际上可以看到"now is the time"存储在程序集可执行文件的只读部分中。

需要指出的另一件事是,当将数组作为参数传递给函数时,数组会衰减为指针。以下两个声明是等价的:

void foo(char arr[]);

void foo(char* arr);

于 2013-06-17T03:13:48.953 回答
0

关于如何使用指针以及数组和指针的区别,我推荐你阅读《专家 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 )。

于 2013-06-17T03:21:33.663 回答
0

从函数返回字符串的更好方法是分配动态内存(使用 malloc)并用所需的字符串填充它......将此指针返回给调用函数,然后释放它。

示例代码:

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

这在没有静态字符串的情况下有效。

于 2013-06-17T05:42:08.903 回答