【问题标题】:function pointer operations between 2 c source files causing compile-time errors2个c源文件之间的函数指针操作导致编译时错误
【发布时间】:2024-01-11 15:38:01
【问题描述】:

我正在做一个小项目,以真正帮助我更好地理解 c 指针/指针函数。我正在处理的应用程序包含 2 个源 (.c) 文件和 1 个头 (.h) 文件。我遇到的问题是尝试将 TurtleShell.c 编译为“.o”文件时出现以下错误:

[ahopkins@localhost TurtleShell]$ gcc -c TurtleShell.c -o TurtleShell.o
TurtleShell.c: In function ‘main’:
TurtleShell.c:12: error: ‘GetString’ undeclared (first use in this function)
TurtleShell.c:12: error: (Each undeclared identifier is reported only once
TurtleShell.c:12: error: for each function it appears in.)

通常,根据我的理解,这意味着我忘记声明放在 main 之后的函数,或者我忘记将我的头文件包含为 #include "GetString.h" 和/或头文件没有与使用它的源文件位于同一目录(或 /usr/local/include、/usr/local),但是,这三个文件确实位于同一目录中,并且已声明头文件。

需要注意的一点是,我正在尝试通过函数指针访问 GetString()。我过去在同一个源文件中使用过函数指针,但这是我第一次真正尝试为使用函数指针的应用程序使用多个源文件,因此我在此过程中做了一些假设。源码如下:

GetString.c:

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

/*
This function handles getting a string from the user by allocating each
character in the string to a char array. This array is guaranteed to
grow as large as it needs as well as trim itself down to only the needed
amount of memory to store the char array once the null terminator is
processed.
*/

int GetString(void)
{
    //Set initial array length reasonably. size_t is used due to it's ability
    //to allow an array to grow as large as it needs.
    size_t strLength = 32;
    char *stringPtr = malloc(strLength);
    if (stringPtr == NULL)
    {
        fprintf(stderr, "Unable to allocate memory to hold char array. Exiting!\n");
        return 1;
    }
    printf("Enter some input: ");
    int c = EOF;
    unsigned int i = 0;
    //Checks the value of c (user character input) to see if RETURN or CTRL+C/Z was entered
    while ((c = getchar()) != '\n' && c != EOF)
    {
        //Adds the character entered into the next index of the char array
        stringPtr[i++] = (char) c;
        //Check if we have reached the end of the allocated memory for the char array
        if (i == strLength)
        {
            //multiply the current amount of memory allocated by 2.
            strLength *= 2;
            if ((stringPtr = realloc(stringPtr, strLength)) == NULL)
            {
                fprintf(stderr, "Unable to expand memory to hold char array. Exiting!\n");
                return 2;
            }
        }
    }
    //End of input. This adds the null terminator to terminate the char array
    stringPtr[i] = '\0';
    //Check if we have any unused memory allocated for the array left. If so, we
    //shrink it down to be the size of the input including the null terminator
    if (sizeof(stringPtr) < strLength)
    {
        stringPtr = realloc(stringPtr, i);
    }
    printf("\n\nString value: %s\n\n\n", stringPtr);
    //Memory cleanup time
    free(stringPtr);
    stringPtr = NULL;
    return 0;
}

TurtleShell.c:

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

int main(void)
{
    int running = 1;
    while(running)
    {
        //Create a function pointer for GetString() so we can manipulate the outputted string from GetString
        int (*GetStringPtr)(void);
        GetStringPtr = &GetString;
        char *string = malloc(GetStringPtr());
        free(string);
        string = NULL;
    }
}

GetString.h:

#ifdef GETSTRING_H_INCLUDED
#define GETSTRING_H_INCLUDED
extern int GetString(void);
#endif

我知道这与我尝试创建函数指针的方式有关,就像我将 TurtleShell.c 更改为以下格式一样,它可以按预期工作:

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

int main(void)
{
    int running = 1;
    while(running)
    {
        char *string = malloc(GetString());
        free(string);
        string = NULL;
    }
}

我对 C 语言非常熟悉,因为我只用它写了大约 3 周,所以我可能遗漏了一些明显的东西。我已经用谷歌搜索了这个问题,但我发现很难正确地用词来找到相关结果。非常感谢任何帮助。

P.S - 我很清楚那里有很多好的 GetString() 类型函数我可能会使用,但是,这是用于学习的,所以我正在根据需要构建自己的函数以增加教育这些练习的价值。

【问题讨论】:

    标签: c++ c function pointers header-files


    【解决方案1】:

    Getstring.h 中的#ifdef 应该是#ifndef

    【讨论】:

    • 净结果:“我忘记包含我的头文件”
    • 你太棒了!我不能相信这是一个错字。我想我应该庆幸它没有变得更糟。只是出于好奇,为什么没有函数指针它可以工作?
    • @AnthonyHopkins - 使用-Wall 标志编译将显示没有函数指针,GetString() 被假定为您的头文件将定义它。但是对于函数指针,这个假设是不成立的。
    【解决方案2】:

    以下行没有意义: char *string = malloc(GetString());

    GetString() 返回 0、1 或 2。 这意味着您分配的内存长度为 0、1 或 2 个字节。

    如果您想获取您在 GetString() 中读取的字符串,请使用一个参数,一个指向 char 指针的指针。 在这种情况下,您根本不需要函数指针。

    int GetString(char** string)
    {
      size_t strLength = 32;
      *string = malloc(strLength);
      ...
    }
    

    不要在函数末尾释放字符串。 在您的主要功能中执行此操作。 您可以通过将 char* 传递给他的函数来获取字符串,如下所示:

    char *string;
    int ret = GetString(&string); // Get the address of 'string'
    

    【讨论】:

    • 感谢您在此答案中付出的努力,但是,当前代码现在可以按预期工作。根据您的回答,这种行为是不可能的。我错过了什么吗?
    • 主函数中的变量(字符指针)“字符串”永远不会指向输入的字符串。相反,它将指向未初始化的内存。
    • @AnthonyHopkins 哪个“行为”?在 Rémis answer 的帮助下,您的代码现在可以编译。但它应该做什么,我仍然无法理解。代码中的某些 cmets 没有意义。函数指针不允许您访问函数的局部变量,但您在 main 中的评论听起来像是您的意图。相反,您只需根据 GetString 返回的值分配一个长度为长度的新字符数组,正如 Johannes 解释的那样。实现这一点甚至不需要函数指针。
    • 最神秘的是你(除了永远不会返回指向创建的字符串的指针)甚至在GetString结束之前释放它的内存来丢弃读取的字符串。因此,即使您的程序中的其他东西以某种方式获取了该字符串的地址,它也不会再存在了。
    • 预期行为是打印用户当前输入到终端的字符串。显然我有这个不正确的。一旦我明白了,那就是更大更好的事情了。我来自 Java 背景,所以我认为这是一种将局部变量从一个函数获取到另一个函数的方法。正如我上面所说,我做了一些假设。我感谢您的澄清,因为我现在知道我需要做什么。正如我之前所说,实施只是一种学习体验。我知道我不需要它,但它是练习实现指针函数的好方法。