C语言:缓冲区和指针

4
我正在编写一个小程序,需要实现以下功能:
  1. 询问用户要输入多少个数字(以动态方式存储在两个数组中)
  2. 逐步输入数字,如果是正数则进入bufferP,如果是负数则进入bufferN
  3. 如果缓冲区已满,则将其大小加倍
  4. 最后输出两个缓冲区中的内容
以下是我的代码:
#include <stdlib.h>
#include <stdio.h>

int main(){
    int length = 0;
    printf("Number of data: \n");
    scanf("%d", &length);
    int *bufferP = (int*)malloc(2*sizeof(int));
    int *bufferN = (int*)malloc(2*sizeof(int));
    int number = 0;
    for (int i = 0 ; i <= length ; i++){
        scanf("%d", &number);
        if(number < 0){
            if(*bufferN == NULL){
                printf("No more place");
                exit(0);
            }
            *bufferN= number;
            *bufferN++;
        }

        if(number >= 0){
            if(*bufferP == NULL){
                printf("No more place");

            }
            *bufferP = number;
            *bufferP++;
        }

    }

    int res =0;
    printf("tab negative : ");
    for (int i = 0; bufferN[i] != NULL; i++)
    {
        res = bufferN[i]; 
        printf("%d\n", res );
    }
    printf("tab positive : ");
    for (int i = 0; bufferP[i] != NULL; i++)
    {
        res = bufferP[i]; 
        printf("%d\n", res );
    }
}

我遇到了以下错误:

 In function ‘main’:
rev_S3_ptr.c:14:16: error: comparison between pointer and integer [-Werror]
    if(*bufferN == NULL){
                ^~
rev_S3_ptr.c:19:4: error: value computed is not used [-Werror=unused-value]
    *bufferN++;
    ^~~~~~~~~~
rev_S3_ptr.c:23:16: error: comparison between pointer and integer [-Werror]
    if(*bufferP == NULL){
                ^~
rev_S3_ptr.c:28:4: error: value computed is not used [-Werror=unused-value]
    *bufferP++;
    ^~~~~~~~~~
rev_S3_ptr.c:35:29: error: comparison between pointer and integer [-Werror]
  for (int i = 0; bufferN[i] != NULL; i++)
                             ^~
rev_S3_ptr.c:41:29: error: comparison between pointer and integer [-Werror]
  for (int i = 0; bufferP[i] != NULL; i++)

所以我的问题是:

  1. 缓冲区像一个标签一样工作,对吗?那么为什么我不能只说如果指针为空,它就是满的,然后做 *buffer++ ?
  2. 在“没有更多空间”的情况下,我必须将缓冲区的大小加倍,但我不知道该怎么做。

你熟悉结构体吗? - Tom's
关于您的第一个问题,如果您查看C运算符优先级表,代码*bufferN++几乎与bufferN++; *bufferN相同。这就是为什么编译器会报错“error: value computed is not used”,因为值*bufferN未被使用。 - Alejandro Blasco
4个回答

2
这个版本怎么样?
#include <stdlib.h>
#include <stdio.h>

int main(){
    int length = 0;
    printf("Number of data: \n");
    scanf("%d", &length);
    int bufferP_size = 2, bufferP_loc = 0;
    int *bufferP = (int*)malloc(bufferP_size*sizeof(int));
    int bufferN_size = 2, bufferN_loc = 0;
    int *bufferN = (int*)malloc(bufferN_size*sizeof(int));
    int number = 0;
    for (int i = 0 ; i <= length ; i++) {
        scanf("%d", &number);
        if(number < 0) {
            if(bufferN_loc == (bufferN_size - 1)) {
                int *temp_buffer;
                printf("No more room in negative buffer, extending\n");
                bufferN_size *= 2; 
                temp_buffer = (int*)realloc(bufferN,bufferN_size*sizeof(int));
                if (temp_buffer == NULL) {
                    printf("Memory allocation failed, aborting\n");
                    free(bufferP);
                    free(bufferN);
                    exit(EXIT_FAILURE);
                }
                bufferN = temp_buffer;
            }
            bufferN[bufferN_loc++] = number;
        }

        if(number >= 0) {
            if(bufferP_loc == (bufferP_size - 1)) {
                int *temp_buffer;
                printf("No more room in positive buffer, extending\n");
                bufferP_size *= 2; 
                temp_buffer = (int*)realloc(bufferP,bufferP_size*sizeof(int));
                if (temp_buffer == NULL) {
                    printf("Memory allocation failed, aborting\n");
                    free(bufferP);
                    free(bufferN);
                    exit(EXIT_FAILURE);
                }
                bufferP = temp_buffer;
            }
            bufferP[bufferP_loc++] = number;
        }
    }

    int res =0;
    printf("tab negative : ");
    for (int i = 0; i < bufferN_loc; i++)
    {
        res = bufferN[i]; 
        printf("%d\n", res );
    }
    printf("tab positive : ");
    for (int i = 0; i < bufferP_loc; i++)
    {
        res = bufferP[i]; 
        printf("%d\n", res );
    }
    free(bufferP);
    free(bufferN);
    exit(EXIT_SUCCESS);
}

重要变化如下:

  • 读取缓冲区时,不要丢弃缓冲区指针的开头
  • realloc可用于扩展缓冲区
  • 你不能使用缓冲区指针来判断是否到达缓冲区末尾,必须自己跟踪缓冲区大小
  • 存储当前位置以及缓冲区大小,这样就可以跟踪读写的位置了

为了编写健壮的代码,应该检查从malloc()realloc()调用返回的值是否为(!=NULL)。对于realloc(),应该将返回值分配给一个“temp”变量,并在那里执行有效性检查,然后再分配给目标变量。否则,当realloc()失败时,指向已分配堆内存的原始指针将丢失,导致内存泄漏。 - user3629249
我已经在检查realloc的返回值,并按照您的建议将其分配给临时变量。对于malloc的调用与原始代码相同 - 我同意它们应该检查是否返回了NULL。 - Michael Firth

1

代码中存在以下问题:

  1. malloc没有在分配的内存末尾插入NULL。因此,检查 if(*bufferN == NULL) 将不起作用。指针将会解引用内存并可能得到垃圾值。
  2. 错误 rev_S3_ptr.c:14:16: error: comparison between pointer and integer [-Werror] if(*bufferN == NULL){ 发生是因为您正在将值 (*bufferN) 的地址与NULL(用于nil地址)进行比较。
  3. 代码 rev_S3_ptr.c:19:4: error: value computed is not used [-Werror=unused-value] *bufferN++; 抛出错误,因为只需要增加地址。解引用无助于此。
  4. 语句 bufferP[i] 表示 *(bufferP+i),因此与NULL进行比较时出现错误。

1
考虑以下更改:

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

int main(){
    int length = 0;
    printf("Number of data: \n");
    scanf("%d", &length);

    int bufferP_cap = 2, bufferN_cap = 2;
    int bufferP_size = 0, bufferN_size = 0;
    int* bufferP = (int*)malloc(bufferP_cap * sizeof(int));
    int* bufferN = (int*)malloc(bufferN_cap * sizeof(int));

    int number = 0;
    for (int i = 0 ; i < length ; i++){  // '<' instead of '<='
        scanf("%d", &number);
        if(number < 0){
            if ( bufferN_size == bufferN_cap){
                bufferN_cap *= 2;
                bufferN = (int*)realloc(bufferN, sizeof(int)*bufferN_cap);
            }
            bufferN[bufferN_size++] = number;
        }
        else {
            if ( bufferP_size == bufferP_cap){
                bufferP_cap *= 2;
                bufferP = (int*)realloc(bufferP, sizeof(int)*bufferP_cap);
            }
            bufferP[bufferP_size++] = number;
        }
    }

    printf("tab negative : ");
    for (int i = 0; i < bufferN_size; i++)
        printf("%d\n", bufferN[i]);

    printf("tab positive : ");
    for (int i = 0; i < bufferP_size; i++)
        printf("%d\n", bufferP[i]);

    free(bufferN);
    free(bufferP);
}

你可能需要再次阅读问题的这一部分:“在“没有更多空间”的情况下,我必须将缓冲区的大小加倍,但我不知道该怎么做。”如果没有动态内存分配,这可能不会真正起作用。 - Gerhardh
谢谢,这大大改善了代码,但是当我执行时,出现了“分段错误(core dumped)”的错误。 - trachnar
@Gerhardh,谢谢,我确实错过了这个点。更新了代码以支持realloc。 - grapes
@grapes 请检查malloc/realloc的失败情况,不要强制转换它们的返回值,并使用"sizeof(*bufferN)"而不是"sizeof(int)",因为bufferN类型的更改会导致静默错误。 - Tom's
@Tom,谢谢。已修复故障。关于强制类型转换返回类型 - 我知道不强制类型转换的理由,但我在VS上测试了此示例,必须这样做。sizeof(*p)毫无疑问是一个很好的选择,但在这个示例中,我不想将指针声明和初始化跨越两行(紧凑的示例很重要),而使用类似t* a = malloc(100 * sizeof(*a))的构造看起来不太友好。 - grapes

0

让我们从开始开始:

int *bufferP = (int*)malloc(2*sizeof(int));
int *bufferN = (int*)malloc(2*sizeof(int));

这是正确的,但你不应该强制转换(不要使用(int*))malloc()的结果。搜索“cast malloc result”。另外,如果你打算根据需要增长数组,为什么要分配2个插槽?为什么不是10或100?为什么不只有一个?

让我们继续:

if(*bufferN == NULL){
  printf("No more place");
  exit(0);
}

在这里,你应该考虑bufferN,而不是它所指向的内容。NULL用于检查指针本身,而不是指针所指向的值;这就是编译器抱怨的原因。无论如何,请注意,bufferN永远不会自动地改变值,因此不要尝试比较它以了解缓冲区是否已满。唯一的方法是计算你放入了多少项。
最后:
        *bufferN= number;
        *bufferN++;

你正确地将数字存储在指针所指向的位置。可能没问题。但是在下一行中,你想要增加指针的值,但同时你也对其进行了解引用。这不是错误,但不寻常 - 实际上编译器再次报错。

缓冲区就像一个选项卡,对吗?

不对。缓冲区是一个内存区域,由你操作,可能使用指针,而这些指针又由你操作。你必须跟踪发生的情况。

在“没有更多空间”时,我必须将缓冲区的大小加倍,但我不知道该怎么做。

你可以使用realloc()来实现。

我不想为你完成整个工作,但我可以解释你需要什么。我将讨论单个缓冲区,另一个缓冲区相同。

首先,像你现在所做的那样,为单个插槽分配一个缓冲区。并引入一个计数器(int)来计算缓冲区中有多少个数字。计数器最初为0。

当用户输入一个数字时,你可以这样存储它:

bufferN[counterN] = number;
counterN++;

此时,您已经知道缓冲区已满。我们从一个只有一个(和空闲)插槽的缓冲区开始,现在没有了...为什么不调整缓冲区大小以再次添加一个空闲插槽呢?我们知道缓冲区中有counterN个项目,缓冲区已满,因此我们需要一个新的缓冲区,其中包含counterN+1个插槽;realloc()可以满足我们的需求。

请注意,在缓冲区中读取和写入的语法bufferN[...]很容易且安全,因为您始终知道(通过counterN)缓冲区的大小以及其中包含的项目数量。而且您不会触及指针本身。

还有其他方法可以做到这一点,但这是其中之一-不是最干净的,但是合理的。我希望这可以帮助您。

编辑:在看到另一个回复后,我意识到要求是将缓冲区的大小加倍。我上面的建议不符合此要求。对不起...这需要更多的工作...我们(您)必须计算不仅添加到缓冲区中的项目数量,还要计算其大小。简单地说,另一个整数变量来管理。好吧,有一个技巧可以避免这种情况,但让我们不要管它:-)


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