关于函数返回值的几种情况


转自:http://patmusing.blog.163.com/blog/static/13583496020113191407531/

在一个函数的内部,return的时候返回的都是一个拷贝,不管是变量、对象还是指针都是返回拷贝,但是这个拷贝是浅拷贝。

 不是不能返回局部变量,是不能返回指向局部变量的指针和引用

1.     如果返回一个基本类型的变量,比如:

int a;

a = 5;

return a;

 

那么就会a的一个拷贝,即5返回,然后a就被销毁了。尽管a被销毁了,但它的副本5还是成功地返回了,所以这样做没有问题。

 

2.     但是对于非动态分配(new/malloc)得到的指针,像1那么做就会有问题,比如在某个函数内部:

int a[] = {1, 2};

return a;

那么也会返回指针a的一个拷贝,我们假定a的地址值为0x002345FC,那么这个0x2345FC是能够成功返回的。当return执行完成后,a就要被销毁,也就是0x002345FC所指向的内存被回收了。如果这时候在函数外面,去地址0x002345FC取值,那得到的结果肯定是不对的。这就是为什么不能返回局部指针的原因。返回局部变量的引用的道理和这个类似。

 

3.     对于返回(动态分配得到的)指针的另外一种情况,比如在函数内部:

int a = new int(5);

return a;

这样做是可以的。return a执行完后,a并没有被销毁(必须要用delete才能销毁a),所以这里返回的a是有效的。

 

4.     如果不是基本数据类型,比如:

class A

{

public:

               OtherClass * ...

};

 

如果在某个函数内部有一个A类的局部变量,比如:

A a;

return a;

这时候也会返回a的一个拷贝,如果A没有写深拷贝构造函数,就会调用缺省的拷贝构造函数(浅拷贝),这样做就会失败的;

如果A中提供了深拷贝构造函数,则这样做就是可以的。

 

实验代码如下:

#include <iostream>

using namespace std;

int some_fun1()

{

    int a = 5;

    return a;                   //OK

}

 

int* some_fun2()

{

    int a = 5;

    int *b = &a;

    return b;                   // not OK

}

 

int* some_fun3()

{

    int *c = new int(5);

    return c;                   // OK, return c执行完后并没被销毁必须要用delete才能销毁

}

 

class CSomething

{

public:

    int a;

    int b;

 

public:

    CSomething(int a, int b)

    {

        this->a = a; 

        this->b = b;

    }

};

 

class CA

{

private:

    CSomething* sth;            // 以指针形式存在的成员变量

                               

public:

    CA(CSomething* sth)

    {

        this->sth = new CSomething(sth->a, sth->b);

    }

 

    // 如果不实现深拷贝,请注释这个拷贝构造函数

    CA(CA& obj)

    {

         sth = new CSomething((obj.sth)->a, (obj.sth)->b);

    }

 

    ~CA()

    {

        cout << "In the destructor of class CA..." << endl;

        if (NULL != sth) delete sth;

    }

    void Show()

    {

        cout << "(" << sth->a << ", " << sth->b << ")" << endl;

    }

    void setValue(int a, int b)

    {

        sth->a = a;

        sth->b = b;

    }

    void getSthAddress()

    {

        cout << sth << endl;

    }

};

 

CA some_fun4()

{

    CSomething c(1, 2);

    CA a(&c);

    return a;                       // 如果CA没有实现深拷贝not OK如果实现深拷贝OK

}

 

int main(int argc, char* argv[])

{

    int a = some_fun1();

    cout << a << endl;              // OK

 

    int *b = some_fun2();

    cout << *b << endl;             // not OK即便返回结果正确也不过是运气好而已

 

    int *c = some_fun3();           // OK, return c执行完后c并没有被销毁必须要用delete才能销毁

    cout << *c << endl;

    delete c;

 

    CA d = some_fun4();             // 如果CA没有实现深拷贝not OK如果实现深拷贝OK

    d.Show();

 

 

    return 0;

}


<think>好的,我现在需要回答用户关于C语言中`recv`函数返回值的问题。用户之前已经问过关于`for`循环退出时变量值的问题,现在转向网络编程中的`recv`函数。这可能意味着用户正在学习网络编程,或者在进行相关的项目开发,需要理解`recv`的同返回情况来处理同的场景。 首先,我需要回顾`recv`函数的基本用法和定义。`recv`是用于从套接字接收数据的函数,属于Berkeley套接字API的一部分,通常在`sys/socket.h`头文件中声明。它的函数原型大致是`ssize_t recv(int sockfd, void *buf, size_t len, int flags);`。返回值类型是`ssize_t`,即有符号的整数类型,可以返回正数、0或-1。 接下来,用户询问返回值几种情况。根据我的知识,`recv`的返回值主要有三种情况: 1. **大于0**:表示成功接收到了数据,返回值是接收到的字节数。这时候用户需要处理接收到的数据,可能需要解析或存储到缓冲区中。 2. **等于0**:表示连接已经正常关闭。在TCP中,当对方调用了`shutdown`或`close`时,本地调用`recv`会返回0,这时候应该关闭套接字或进行清理操作。 3. **等于-1**:表示发生了错误。此时需要检查`errno`来确定具体的错误类型,比如`EAGAIN`或`EWOULDBLOCK`(在非阻塞模式下没有数据可读),`EINTR`(系统调用被信号中断),或者其他错误如`ECONNRESET`等。根据同的错误,处理方式也同,可能需要重试、记录错误或关闭连接。 但需要进一步验证这些情况是否正确,是否有遗漏的情况。例如,是否在某些情况返回值可能为其他负数?根据POSIX标准,`recv`返回-1表示错误,其他负值可能存在,因为`ssize_t`是有符号类型,但接收的数据长度可能为负数,所以只有-1是错误的情况另外,需要考虑同的套接字类型和模式,比如非阻塞套接字和阻塞套接字对`recv`行为的影响。例如,在非阻塞模式下,如果没有数据可读,`recv`会立即返回-1,并设置`errno`为`EAGAIN`或`EWOULDBLOCK`,而在阻塞模式下则会一直等待直到有数据到达或连接关闭。 还需要注意,当使用`flags`参数时,比如`MSG_PEEK`,是否会影响返回值。例如,使用`MSG_PEEK`时,数据会被读取但从缓冲区移除,所以后续的`recv`调用可能还会返回同样的数据,但这影响返回值的基本情况,只是改变了数据是否被消费的行为。 此外,同的操作系统可能有同的实现细节,比如某些系统可能定义额外的错误码,但主要的情况应该是一致的。例如,Windows中的`recv`函数可能返回`SOCKET_ERROR`,对应的错误码需要通过`WSAGetLastError()`获取,但用户的问题可能更关注于类Unix系统下的情况,但需要确认是否要涵盖跨平台的信息。 总结下来,`recv`的返回值主要有三种情况,但需要详细说明每种情况下的处理方式和可能的原因。同时,要提醒用户检查错误码`errno`以确定具体错误类型,并正确处理每种情况以确保程序的健壮性。 在回答时,应该按照结构清晰的步骤,先列出三种情况,再分别详细说明,最后总结关键点和处理建议。同时,使用中文和适当的Markdown格式,如加粗、列表、代码块等,使内容易于理解。还要注意用户可能需要的实际应用场景,比如如何处理错误、如何区分正常关闭和异常断开等。</think>在C语言的网络编程中,`recv`函数返回值主要有以下三种情况: --- ### **1. 返回值 $>0$:成功接收数据** - **含义**:表示从套接字中成功接收到了数据。 - **数值解释**:返回值是实际接收到的**字节数**。 - **示例**: ```c char buffer[1024]; ssize_t bytes_received = recv(sockfd, buffer, sizeof(buffer), 0); if (bytes_received > 0) { // 处理接收到的数据(字节数为 bytes_received) } ``` - **注意**:实际接收的字节数可能小于请求的字节数(取决于缓冲区剩余数据量)。 --- ### **2. 返回值 $=0$:连接正常关闭** - **含义**:对方已**主动关闭连接**(如调用`close()`或`shutdown()`)。 - **处理逻辑**: ```c if (bytes_received == 0) { // 关闭本地套接字,释放资源 close(sockfd); } ``` - **典型场景**:TCP协议中,对方发送了`FIN`报文,表示通信结束。 --- ### **3. 返回值 $=-1$:发生错误** - **含义**:接收过程中出现错误,需通过`errno`判断具体原因。 - **常见错误类型**: | 错误码 (`errno`) | 说明 | |-----------------------|--------------------------------------------------------------------| | `EAGAIN`/`EWOULDBLOCK` | 非阻塞模式下,无数据可读(需重试或等待)。 | | `EINTR` | 系统调用被信号中断(需重新调用`recv`)。 | | `ECONNRESET` | 连接被对方强制关闭(如进程崩溃或网络异常)。 | | `ENOTCONN` | 套接字未连接。 | - **错误处理示例**: ```c if (bytes_received == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 非阻塞模式下无数据,稍后重试 } else if (errno == EINTR) { // 被信号中断,重新调用recv } else { // 严重错误,关闭连接 close(sockfd); } } ``` --- ### **关键总结** | 返回值 | 含义 | 后续操作 | |------------|--------------------|--------------------------------------------| | $>0$ | 成功接收数据 | 处理数据 | | $0$ | 连接关闭 | 关闭套接字 | | $-1$ | 错误发生 | 检查`errno`,区分处理 | --- ### **注意事项** 1. **阻塞 vs 非阻塞模式**: - **阻塞模式**:可能长时间等待数据或连接关闭。 - **非阻塞模式**:可能立即返回`-1`并设置`errno = EAGAIN`。 2. **TCP粘包问题**:需自行处理数据边界(如定义协议头、长度前缀等)。 3. **跨平台差异**:Windows中错误码通过`WSAGetLastError()`获取,而非`errno`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值