【问题标题】:Using pointers with functions and structs将指针与函数和结构一起使用
【发布时间】:2010-12-12 20:19:44
【问题描述】:

您好,我正在自学 C,我对以下代码有点困惑,因为我不知道我是否正确理解了代码。如果有人能阅读我的解释并在我错了时纠正我,我将非常感激。

代码来自头文件。此时程序的功能应该是无趣的,因为我的理解问题是关于指针和函数返回的值。所以首先我要在我的员工结构中声明 3 个 char 数组和一个整数。

struct employee
{
    char firstname[11];
    char lastname[11];
    char number[11];
    int salary;
}


头文件中声明了 5 个函数。第一个函数接受 4 个值(3 个指针和一个 int)并返回一个指向结构的指针。第二个函数获取一个指向“struct employee”的指针返回一个指针指向struct employee 中数组“char firstname”的元素。函数 3 和 4 对其他两个数组执行相同的操作。
函数 5 获取指向 struct employee 的指针,但返回一个 int 而不是指针。所以它只是在结构中使用声明的变量。

struct employee* createEmployee(char*, char*, char*, int); //1
char* firstname (struct Employee*);                        //2
char* lastname (struct Employee*);                         //3
char* number (struct Employee*);                           //4
int salary (struct Employee*);                             //5

【问题讨论】:

  • 没有函数体很难说它们做了什么,但签名显示了你解释的内容。问题是什么?
  • 我的问题是我的解释是否正确。我不确定我是否理解在这种情况下指针是如何工作的。

标签: c


【解决方案1】:

您的理解非常正确。为了更准确和/或减少对英语的滥用,函数 2-4 返回一个指向相应数组元素的指针。

这个想法是每个数组的内容代表某种文本,每个元素对应一个文本字符,直到第一次出现零值(用于标记“字符串”的结尾)。请记住,这里没有提供 Unicode,甚至没有指定编码:我们假设 ASCII,并且对于不在 0..127 范围内的任何字节,所有的赌注都没有。 char 类型的更好名称是 byte;但那时我们真的不知道更好。 (您还应该知道,char 是独立于 signed charunsigned char 的类型,char 可能已签名,也可能未签名。)

这样,名称和数字可以是任意长度最多十(保留第 11 个字节,以便为具有零值的“空终止符”留出空间),并返回指针可用于检查 - 和修改;但这可能不是一个好主意 - 数组中的数据是 Employee 结构的一部分。

作为Employeesalary 返回的int 是结构中的副本。所以虽然我们可以看到整个值,但这让我们修改结构中的值。当然,只要我们有struct 定义和Employee 实例,就没有保护;我们可以直接访问成员而不是通过函数。

在 C 中,我们通过提供这些定义来获得“封装”和“数据隐藏”。相反,我们只需将struct Employee; 和函数声明放在头文件中,并将结构定义放在实现文件中。现在调用代码对Employee 一无所知;仅关于它可以做什么

【讨论】:

    【解决方案2】:

    几乎是对的,除了当你读取函数时,输入“char*”应该被读取为一个字符串,虽然从技术上讲它是一个指针。对于整数,实际值被传递,所以你就在那里。

    所以,

    struct employee* createEmployee(char*, char*, char*, int);
    

    这意味着该过程创建了一个员工,其中传递了四个输入(前三个是字符串 (Char*),可能是名字、姓氏和数字/ID,最后一个是薪水。)

    【讨论】:

    • 指向 char 的指针是指向 char 的指针。其他任何事情都只是惯例。 C中没有真正的字符串类型。
    • 同意,C 中没有字符串类型。但这是程序的意图。传入名字,姓氏作为字符串。你这样做的方法是传递指针。
    【解决方案3】:

    仅通过读取函数签名无法确定返回的值/指针是引用来自结构的相同值/指针,还是函数正在创建副本并返回指向新值的指针。这就是为什么在头文件中将文档编写为带有函数签名的 cmets 也是一个好主意。

    【讨论】:

    • 虽然如此,但以这种方式编写 C 代码会很奇怪且不习惯。如果您不希望调用者通过返回的指针修改您的数据,您应该使用语言功能明确说明 - 即适当地使用 const 关键字。
    • 同意,为我辩护,C 是我的第二语言:P
    【解决方案4】:

    您的解释是有道理的,除了 #2-4:因为它们返回 char*,它们不能返回 char 数组的 一个元素(后者的正确类型只是 char )。这三个函数中的每一个都返回 指向 char 的指针。假设它们返回指向相应数组的第一个元素的指针,这基本上是数组在 C 中传递的方式。

    【讨论】:

    • 您是否错过了问题中加粗的“元素”之前的“指针”一词?
    • @Karl 我没有(它不存在)。 OP已根据我的回答编辑了问题。
    • 我告诉你,这里到处都是忍者。 ;)
    • 无论如何,虽然这确实是“基本上如何在 C 中传递数组”,但如果不提及所有陷阱就解释这一点是不负责任的 - 一点知识是一件危险的事情。 Ordo,请通读c-faq.com/aryptr/index.html(以及链接的部分;该页面只是一个目录)。
    【解决方案5】:

    1 中存在问题。我假设你在做什么:

    struct employee* createEmployee(const char* f, const char* l, const char* n,
    const int sal)
    {
         struct employee* e;
         strcpy(e->firstname, f);
         strcpy(e->lastname,  l);
         /* ... */
         return e;
    }
    

    这里的问题是传入的数组。传入任意大小的char数组是完全可行的;长度超过 11 的任何内容都会导致您开始覆盖 &(e->firstname[0])+11; 处的数据。过什么?确切地。你不知道(我也不知道,它会在运行时确定)。这可能会导致一些严重的问题。

    一种解决方法是使用stdlib.hstring.h 中的函数,即strlen() 来测试传入数据的长度,以确保它适合您的字段大小。

    一个更好的方法可能是写:

    int createEmployee(struct employee* e, const char* f, const char* l, const char* n, 
    const int sal)
    {
        int error = 0;
        if ( strlen(f) < 11 )
        {    
            strncpy(e->firstname, f);
        }
        else
        {
            error++;
        }
        /* ... */
    
        return error;
    }
    

    看看我做了什么?是的,它会起作用 - 作为指针传入的任何内容都可以修改。这不是通过引用传递,相当。编辑:正如 aix 所说,指针是“如何在 C 中传递数组”。

    另一种可能的方法是strncpy(),它将根据最后一个参数截断源字符串,因此strncpy(e-&gt;firstname, f, 11); 是安全的。

    您也可以尝试根据要求为字段大小动态分配内存。我猜你以后会/作为另一个挑战来学习。

    另外,另一个建议是使用 typedef 定义指向结构的指针。它使事情更具可读性,尽管您所做的方式对于学习的人来说绝对更清晰。

    【讨论】:

    • o_O 这里有几个问题: 没有迹象表明 OP 实际编写了,甚至 查看 任何实现代码(我认为他正在阅读示例); strcpystrlen 一样是标准库中的函数;错误报告不是问题,因为我们正在返回一个指针,所以如果有错误我们可以返回 NULL(而且谁在乎错误计数呢?); “输出参数”通常很难看;并且您的第一个示例被破坏了,因为 Employee 从未在任何地方实际分配过(并且当您传入指针时,调用者必须分配一个)。
    • @Karl 我不想一次性投入太多,但是如果您将 1、2、4 添加到错误计数中,您可以有效地使用二进制字符串中的位来表示哪个发生错误。
    • 是的,输出参数很难看,同意。我仍然认为它比在函数中创建结构更好,因为下一个逻辑步骤是在函数中分配动态数组......这可能会导致稍后忽略 free。是的,我没有在函数的任何地方创建员工结构的实例,但是不,函数本身没有损坏,就像你说的,调用者必须分配它。
    • 事实上,不,我不认为参数是丑陋的。我认为不一致是丑陋的。返回值也是指示函数是否有效的好方法。
    猜你喜欢
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 2014-03-14
    • 2018-02-11
    相关资源
    最近更新 更多