• [c语言]-注意创建指针时的内存区域是栈内存 or 堆内存


       之前用c语言写了个udp_socket的代码,但是一直报错,花了好长时间才找到原因,特此记录。

       上代码:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include "string.h"
     4 #include <sys/socket.h>
     5 #include <netinet/in.h>
     6 #include <unistd.h>
     7 #include <arpa/inet.h>
     8 
     9 
    10 void createSockAddr(char *ip,int port ,struct sockaddr * tmp){
    11     //创建sockaddr_in结构体变量
    12     struct sockaddr_in addr;
    13     //每个字节都用0填充
    14     memset(&addr,0,sizeof(addr));
    15 
    16     //使用IPv4地址
    17     addr.sin_family=AF_INET;
    18     //具体的IP地址
    19     addr.sin_addr.s_addr=inet_addr(ip);
    20      //端口
    21     addr.sin_port=htons(port);
    22 
    23     tmp=(struct sockaddr *)&addr;
    24 }
    25 
    26 
    27 int main(void)
    28 {
    29     /* code */
    30 
    31     // 1. 声明 udp socket,
    32     int udp_sock=socket(AF_INET,SOCK_DGRAM,0);
    33     if(udp_sock<0){
    34         perror("get socket fail 
    ");
    35         exit(EXIT_FAILURE);
    36     }
    37 
    38     // 2. 创建本地sockaddr
    39     struct sockaddr * local_addr;
    40 
    41     createSockAddr("127.0.0.1",9002,local_addr);
    42 
    43     if(local_addr==NULL){
    44         perror("get local_addr fail 
    ");
    45         exit(EXIT_FAILURE);
    46     }
    47 
    48     // 3. 监听端口
    49     int bind_result=bind(udp_sock,local_addr,sizeof(struct sockaddr_in ));
    50 
    51     if(bind_result == -1){
    52         printf("bind fail!
    ");
    53         return -1;
    54     }
    55 
    56 
    57     // 2. 创建server addr
    58     struct sockaddr * server_addr;
    59 
    60     createSockAddr("127.0.0.1",2000,server_addr);
    61 
    62     if(server_addr==NULL){
    63         perror("get server_addr fail 
    ");
    64         exit(EXIT_FAILURE);
    65     }
    66 
    67     // 5. 连接server
    68     int conn_result=connect(udp_sock,server_addr,sizeof(struct sockaddr ));
    69 
    70     if(conn_result < 0 ){
    71         perror("connect fail 
    ");
    72         exit(EXIT_FAILURE);
    73     }
    74 
    75     // 6, 发送消息
    76     char * msg="hello i'm c_client";
    77     size_t msg_len=strlen(msg);
    78     ssize_t  w_result=sendto(udp_sock,msg,msg_len,0,server_addr,sizeof(struct sockaddr ));
    79 
    80     if(w_result==-1){
    81         perror("send fail 
    ");
    82         exit(EXIT_FAILURE);
    83     }
    84 
    85     close(udp_sock);
    86 
    87     return 0;
    88 }
    View Code

      有问题的就是struct sockaddr 指针的创建方式:

      首先在 createSocketAddr函数的外部声明了一个 struct socketaddr  的指针变量 local_addr,然后想的是复用代码,就写了个 createSocketAddr 函数,将指针当做参数传进去以便赋值。

      有经验的一定会发现这种写法存在问题,并且编译器也会发出一个类似 “返回了栈内存的变量地址”的警告。

      错误的原因正是由于指针指向的数据结构是在函数createSocketAddr ( ) 内部直接创建的,所以该地址是在栈内存中,当退出函数时由于出栈会导致该地址其实属于一种“无效内存”。这种"无效内存"中的数据可能会被分配给其他数据结构从而改变了数据,或者直接产生警告或者报错。

     正确的写法应该是在堆内存中申请一块区域用来存放数据,通过指针将该结构传入函数内部中进行处理,并且执行完后由于是申请的堆内存,一定要手动free回收;

     正确的代码

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include "string.h"
     4 #include <sys/socket.h>
     5 #include <netinet/in.h>
     6 #include <unistd.h>
     7 #include <arpa/inet.h>
     8 
     9 
    10 struct sockaddr_in * createSockAddr(char *ip,int port ){
    11     // 使用malloc从堆内存中分配一块 struct sockaddr_in 的区域
    12     struct sockaddr_in * tmp = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
    13 
    14     // 填充 tmp 的内存区域
    15     //每个字节都用0填充
    16     memset(tmp,0,sizeof(struct sockaddr_in));
    17 
    18     //使用IPv4地址
    19     (*tmp).sin_family=AF_INET;
    20     //具体的IP地址
    21     (*tmp).sin_addr.s_addr=inet_addr(ip);
    22      //端口
    23     (*tmp).sin_port=htons(port);
    24 
    25     return tmp;
    26 }
    27 
    28 
    29 int main(void)
    30 {
    31     /* code */
    32 
    33     // 1. 声明 udp socket,
    34     int udp_sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    35     if(udp_sock<0){
    36         perror("get socket fail 
    ");
    37         exit(EXIT_FAILURE);
    38     }
    39 
    40     // 2. 创建本地sockaddr, 初始化指针==NULL,表示指针指向未分配的地址0x00,不能对该地址进行 (*操作),相当于指针未分配空间进行初始化;
    41 
    42     struct sockaddr_in * local_addr=createSockAddr("127.0.0.1",9002);;
    43 
    44     if(local_addr==NULL){
    45         perror("get local_addr fail 
    ");
    46         exit(EXIT_FAILURE);
    47     }
    48 
    49     // 3. 监听端口
    50     int bind_result=bind(udp_sock,(const struct sockaddr *)local_addr,sizeof(struct sockaddr_in ));
    51 
    52     if(bind_result == -1){
    53         perror("bind fail 
    ");
    54         exit(EXIT_FAILURE);
    55     }
    56 
    57 
    58     // 2. 创建server addr
    59     struct sockaddr_in * server_addr =  createSockAddr("10.108.132.161",2000);;
    60 
    61 
    62     if(server_addr==NULL){
    63         perror("get server_addr fail 
    ");
    64         exit(EXIT_FAILURE);
    65     }
    66 
    67     // 5. 连接server
    68     int conn_result=connect(udp_sock,(const struct sockaddr *)server_addr,sizeof(struct sockaddr_in ));
    69 
    70     if(conn_result < 0 ){
    71         perror("connect fail 
    ");
    72         exit(EXIT_FAILURE);
    73     }
    74 
    75     // 6, 发送消息
    76     char * msg="hello i'm c_client
    ";
    77     
    78     ssize_t  w_result=write(udp_sock,msg,strlen(msg));
    79 
    80     if(w_result<0){
    81         perror("send fail 
    ");
    82         exit(EXIT_FAILURE);
    83     }
    84 
    85     free(server_addr);
    86     free(local_addr);
    87     close(udp_sock);
    88 
    89     return 0;
    90 }
  • 相关阅读:
    基于xtrabackup的PointInTime Recovery备份恢复
    使用折半查找法删除
    五月第二周
    MySQL 5.5 外键不能引用分区表主键
    MYSQL 登录漏洞,Percona Server说明
    Detectron2学习笔记 Sanny.Liu
    取客户MAP地址
    DataReader转实体<T>
    从程序员到翻译的感受
    .net中的浅拷贝和深拷贝
  • 原文地址:https://www.cnblogs.com/zhaohuaxishi/p/14822124.html
Copyright © 2020-2023  润新知