分段错误:结构指针

问题描述 投票:0回答:1

我正在尝试学习如何使用 Winsock 在 C 上使用 SMTP。我遵循了 Microsoft 提供的guide,最终代码按原样完美运行。现在,我决定更进一步,通过获取代码的不同部分并将它们放入具有适当名称的函数中,使其更加“有条理”。 我很高兴看到我的代码编译成功,完全没有错误,但是在第一次运行它之后,VSC 调试器通知我出现了分段错误。有问题的行(到目前为止)是这一行:

89|  *iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);

现在我有点迷路了,因为你访问指针结构的方式就像指针一样“加起来”,在那里你可以有一个

**var1
。也许错误实际上并不在有问题的行本身,而是之前......我不知道,我迷路了。

到目前为止,这是我的代码:

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_PORT "25"

char data[500];

int initWinsock(int *, WSADATA *);
int makeSocket(int *, struct addrinfo *, struct addrinfo *, struct addrinfo *, SOCKET *);
int connSocket(int *, SOCKET *, struct addrinfo *, struct addrinfo *);
int clientReceive(int *, SOCKET *, char *);
int closeSocketListener(int *, SOCKET *);
int closeSocket(SOCKET *);

int main() {
    SOCKET ConnectSocket = INVALID_SOCKET;
    WSADATA wsaData;
    int iResult;
    struct addrinfo *result = NULL,
                    *ptr = NULL,
                    hints;
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    
    initWinsock(&iResult, &wsaData);

    makeSocket(&iResult, &hints, result, ptr, &ConnectSocket);

    connSocket(&iResult, &ConnectSocket, ptr, result);
    
    clientReceive(&iResult, &ConnectSocket, data);

    closeSocketListener(&iResult, &ConnectSocket);

    closeSocket(&ConnectSocket);

    system("pause");
    return 0;
}

int initWinsock(int *iResult, WSADATA *wsaData){
    // Initialize Winsock
    *iResult = WSAStartup(MAKEWORD(2,2), wsaData);
    if (*iResult != 0) {
        printf("WSAStartup failed: %d\n", *iResult);
        return 1;
    }
    return 0;
}

int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
    // Resolve the server address and port
    *iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
    if (*iResult != 0) {
        printf("getaddrinfo failed: %d\n", *iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to the first address returned by
    // the call to getaddrinfo
    ptr=result;

    // Create a SOCKET for connecting to server
    *ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

    if (*ConnectSocket == INVALID_SOCKET) {
        printf("Error at socket(): %d\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    return 0;
}

int connSocket(int *iResult, SOCKET *ConnectSocket, struct addrinfo *ptr, struct addrinfo *result){
    // Connect to server.
    *iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (*iResult == SOCKET_ERROR) {
        closesocket(*ConnectSocket);
        *ConnectSocket = INVALID_SOCKET;
    }

    // Should really try the next address returned by getaddrinfo
    // if the connect call failed
    // But for this simple example we just free the resources
    // returned by getaddrinfo and print an error message

    freeaddrinfo(result);

    if (*ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }
    return 0;
}

int clientReceive(int *iResult, SOCKET *ConnectSocket, char *out){
    *iResult = recv(*ConnectSocket, out, 200, 0);
    if (iResult > 0) {
        printf("Bytes received: %d\n", *iResult);
        return 0;
    } else if (iResult == 0) {
        printf("Connection closed\n");
        return 1;
    } else {
        printf("recv failed: %d\n", WSAGetLastError());
        return 2;
    }

    return -1;
}

int closeSocketListener(int *iResult, SOCKET *ConnectSocket){
    // shutdown the send half of the connection since no more data will be sent
    *iResult = shutdown(*ConnectSocket, SD_SEND);
    if (*iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(*ConnectSocket);
        WSACleanup();
        return 1;
    }
    return 0;
}

int closeSocket(SOCKET *ConnectSocket){
    // cleanup
    closesocket(*ConnectSocket);
    WSACleanup();
    return 0;
}
c pointers struct segmentation-fault winsock
1个回答
0
投票

在函数体范围内,所有参数都是局部变量。您可以将参数视为局部变量,即使它们不是,显然因为您可以从“外部”为它们赋值,这对于实际局部变量而言并非如此。

例1):

void foo(int bar)
{
    bar = 123;
}

没有什么奇怪的,你不会期望这个函数会影响这个函数之外的任何东西。

int var = 987;
foo(var);
printf("%i", var); // prints 987 as expected

但是有指针,例子2):

void foo(int *bar)
{
    *bar = 123;
}

这会影响外部价值。

int var = 987;
foo(&var);
printf("%i", var); // prints 123, again as expected

这个例子 3):

void foo(int *bar)
{
    int baz = 123;
    bar = &baz;
}

现在我们称它为:

int var = 987;
foo(&var);
printf("%i", var); // prints 987

怎么会这样?这条线

bar = &baz;
不应该覆盖 bar 的值吗?答案是应该而且确实如此,但 bar 只是“局部变量”,这就是为什么我们看不到外部效果的原因。原因与第一个示例完全相同。

当我们看第二个例子时,它确实会影响外部价值,唯一的区别是取消引用

只有当你在函数体外部取消引用你正在读取/写入值的指针时,请注意箭头运算符是指向结构的指针的特殊类型取消引用。

您的相关代码:

int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
    *iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
    // ...
    ptr=result;
    // ...
}

这一行:

ptr=result;
同例3,对“外”值没有影响

&result
表示局部变量的地址,让我们想象一下如何使用函数设置值
getaddrinfo

void getaddrinfo(/* ... */ int **bar)
{
    static int baz = 123;
    *bar = &baz;
}

现在如果我们调用那个函数:

int *var;
getaddrinfo(&var);
printf("%i", *var); // prints 123

但是:

void foo(int *bar)
{
    getaddrinfo(&bar);
    printf("%i", *bar); // prints 123
}
int _default = 987;
int *var = &_default;
foo(var);
printf("%i", *var); // prints 987, as if the "foo(var);" wasn't called at all

解决方案可能是:

void foo(int **bar)
{
    getaddrinfo(bar);
}
int *var;
foo(&var);
printf("%i", *var); // prints 123
© www.soinside.com 2019 - 2024. All rights reserved.