Linux系统函数累积

本文解析了select、offsetof、container_of、os_memmove等系统函数的功能与应用,详细介绍了这些函数的内部实现机制,并通过示例代码展示了它们的实际用途。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-1 .select()函数

select(I/O多工机制)
定义函数 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);

由于select只返回一个表示总数的整数,那么如果一次select多个socket句柄,
或者函数执行时即指定了readfds,也指定了writefds,函数本身将无法告诉程序
到底哪个socket句柄可读或可写。

fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,【内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪个句柄可读】。

// Linux系统函数  http://net.pku.edu.cn/~yhf/linux_c/    

// 1.c          // Linux系统函数  http://net.pku.edu.cn/~yhf/linux_c/    
#include <sys/types.h> 
#include <sys/time.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 




int main() 
{ 
    char buffer[128]; 
    int result, nread; 
    fd_set inputs, testfds;  // fd_set宏   定义了unsigned long []数组 默认大小128字节

// fd_set default  size = 128
    printf("fd_set default  size =%d  \n",(int)(sizeof(*inputs))); 
    struct timeval timeout; 


    struct timespec tpstart;
    struct timespec tpend;
    long timedif;


     FD_ZERO(&inputs);//用select函数之前先把集合清零  
     FD_SET(0,&inputs);//把要检测的句柄——标准输入(0),加入到集合里。
//文件               文件描述符
//输入文件—标准输入       0(缺省是键盘,为0时是文件或者其他命令的输出)
//输出文件—标准输出       1(缺省是屏幕,为1时是文件)
//错误输出文件—标准错误    2(缺省是屏幕,为2时是文件


     while(1) 
    { 
       testfds = inputs; 
       timeout.tv_sec = 10; 
       timeout.tv_usec = 500000;
       clock_gettime(CLOCK_MONOTONIC, &tpstart);
       printf("select begin \n"); 
//int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
//FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
//FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真 ///检测fd是否在set集合中,不在则返回0  
//FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
//FD_ZERO(fd_set *set); 用来清除描述词组set的全部位 
//返回值    如果参数timeout设为NULL则表示select()没有timeout   
//执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1     
       result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout); 
       clock_gettime(CLOCK_MONOTONIC, &tpend);
       timedif = 1000000 *(tpend.tv_sec-tpstart.tv_sec)+(tpend.tv_nsec-tpstart.tv_nsec)/1000;
       printf("select end  return = %d  timecost = %ld 微秒 \n", result,timedif);
       switch(result) 
       { 
       case 0: 
           printf("timeout \n"); 
      break;
  case -1: 
           perror("select"); 
           exit(1); 
       default: 
           if(FD_ISSET(0,&testfds)) 
           { 
               ioctl(0,FIONREAD,&nread);//取得从键盘输入字符的个数,包括回车。 
               if(nread == 0) 
               { 
                  printf("keyboard done\n"); 
                  exit(0); 
               } 


               nread = read(0,buffer,nread); 
               buffer[nread] = 0; 
               printf("read %d from keyboard [包括回车]  === you input: %s", nread, buffer); 
         } 
         break; 
      } 
   } 
   return 0;
} 
/*
编译: gcc 1.c -o 1.out


打印输出及说明:


select begin 
fafca  【键盘输入】
select end  return = 1  timecost = 2435370 微秒   【return = 1 表示有一个标准输入有数据  2435370 微秒 表示select运行了2.4秒】
read 6 from keyboard === you input: fafca
select begin 
cdss
select end  return = 1  timecost = 7258312 微秒 
read 5 from keyboard === you input: cdss
select begin 
select end  return = 0  timecost = 10503457 微秒   
timeout     【timeout表示 当前的 select的 timeout 时间已到  超时了  所以从 select退出】
select begin




*/

-2 .offsetof 宏定义

原型 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
说明 获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。

#include <stdio.h>

// 获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

struct student
{
    char gender;
    int id;
    int age;
    char name[20];
};

struct teacher
{
    char sex;
    char gender;
    long birthday;
    int teach_class;
    int emplyee_id;
    int age;
    char name[20];
};

void main()
{
    int gender_offset, id_offset, age_offset, name_offset;

    gender_offset = offsetof(struct student, gender);
    id_offset = offsetof(struct student, id);
    age_offset = offsetof(struct student, age);
    name_offset = offsetof(struct student, name);

    printf("gender_offset = %d\n", gender_offset);
    printf("id_offset = %d\n", id_offset);
    printf("age_offset = %d\n", age_offset);
    printf("name_offset = %d\n", name_offset);

    printf("=================================\n");
    int sex_offset, birthday_offset, teach_class_offset, emplyee_id_offset,tage_offset,tname_offset;
    sex_offset = offsetof(struct teacher, sex);
    birthday_offset = offsetof(struct teacher, birthday);
    teach_class_offset = offsetof(struct teacher, teach_class);
    emplyee_id_offset = offsetof(struct teacher, emplyee_id);
    tage_offset = offsetof(struct teacher, age);
    tname_offset = offsetof(struct teacher, name);


    printf("teacher sex_offset = %d\n", sex_offset);
    printf("teacher birthday_offset = %d\n", birthday_offset);
    printf("teacher teach_class_offset = %d\n", teach_class_offset);
    printf("teacher emplyee_id_offset = %d\n", emplyee_id_offset);
    printf("teacher age_offset = %d\n", tage_offset);
    printf("teacher name_offset = %d\n", tname_offset);

}
/*
函数输出:
gender_offset = 0
id_offset = 4
age_offset = 8
name_offset = 12
=================================
teacher sex_offset = 0
teacher birthday_offset = 8
teacher teach_class_offset = 16
teacher emplyee_id_offset = 20
teacher age_offset = 24
teacher name_offset = 28
*/

-3 .container_of 宏定义

定义 container_of在linux内核的include/linux/kernel.h中定义
根据 “结构体(type)变量”中的”域成员变量(member)的指针(ptr)”来获取指向整个结构体变量的指针。
原型

#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

说明
(01) typeof( ( (type *)0)->member ) 取出member成员的变量类型。
(02) const typeof( ((type *)0)->member ) *__mptr = (ptr) 定义变量__mptr指针,并将ptr赋值给__mptr。经过这一步,__mptr为member数据类型的常量指针,其指向ptr所指向的地址。
(04) (char *)__mptr 将__mptr转换为字节型指针。
(05) offsetof(type,member)) 就是获取”member成员”在”结构体type”中的位置偏移。
(06) (char )__mptr - offsetof(type,member)) 就是用来获取”结构体type”的指针的起始地址(为char 型指针)。
(07) (type )( (char )__mptr - offsetof(type,member) ) 就是将”char 类型的结构体type的指针”转换为”type 类型的结构体type的指针”。

#include <stdio.h>
#include <string.h>

// 获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

// 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

struct student
{
    char gender;
    int id;
    int age;
    char name[20];
};

void main()
{
    struct student stu;
    struct student *pstu;

    stu.gender = '6';
    stu.id = 1;
    stu.age = 26;
    strcpy(stu.name, "ABCDEF");

    // 根据"id地址" 获取 "结构体的地址"。
    pstu = container_of(&stu.id, struct student, id);

    // 根据获取到的结构体student的地址,访问其它成员
    printf("gender= %c\n", pstu->gender);
    printf("age= %d\n", pstu->age);
    printf("name= %s\n", pstu->name);
}
/*
输出结果:
gender= 6
age= 26
name= ABCDEF
*/

-4 .os_memmove()函数定义

定义 void * os_memmove(void *dest, const void *src, size_t n);

memmove的处理措施:
(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝
(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

wpa_supplicant 中使用的调用函数
os_memmove(&table->table[i], &table->table[i + 1],(table->count - i - 1) *sizeof(struct eloop_sock));
// 实现方法: 把地址 &table->table[i + 1] 作为起点之后的 【(table->count - i - 1) * sizeof(struct eloop_sock) 】【计算得到N】 N个 字节 复制到 地址 &table->table[i] 作为起点的后面 ,
// 作用: 从内存数组 &table 中删除结点 &table->table[i] 的作用
原型

void * os_memmove(void *dest, const void *src, size_t n)
{
    if (dest < src)  // dest 和 src地址 如果  dest 地址在前的话  直接调用 os_memcpy 就可以完成 os_memmove函数
    // memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上
        os_memcpy(dest, src, n); 
    else { // 如果src 在前的话  那么就可能 src复制的内容 把 dest内存地址包含进去了 形成内存踩踏异常
        /* overlapping areas */
        char *d = (char *) dest + n;
        const char *s = (const char *) src + n;
        // 以 (char *) dest + n 地址为数据填充起始地址 依次往前进行后续遍历                                   
        //以  (char *) src  + n 地址为数据来源起始地址 依次往前进行后续遍历
        while (n--)  // 从后往前进行数据拷贝  以防止内存践踏  村内重叠
            *--d = *--s;
    }
    return dest;
}

Demo测试

#include <stdio.h>
#include <string.h>

// 函数声明,没有会报错,或者把实现函数上移到main函数之前
// reoveCharFromStr函数从给定的字符串"0123456789" 删除对应的字符'5' 
// os_memmove(&str[i],&str[i+1],sizeof(char)*(len-i-1)); 内存区域情况为
// os_memmove(void *dest, const void *src, size_t n)
// dest内存在前   src内存区域在后   dest和src存在内存重叠的情况一
int removeCharFromStr(char* str , char num); 

//addCharFromStr函数从给定的字符串"0123456789" 增加对应的字符'5'到相同字符5后面
// os_memmove(&str[i+1],&str[i],sizeof(char)*(len-i+1)); 内存区域情况为
// os_memmove(void *dest, const void *src, size_t n)
// dest内存在后   src内存区域在前   dest和src存在内存重叠的情况二
int addCharFromStr(char* str , char num); 


// 把当前字符串的首字符 当成末尾字符
// os_memmove(&str[0],&str[1],sizeof(char)*(len-1)); 内存区域情况为
// dest内存在前   src内存区域在后   dest和src存在内存重叠的情况一
int firstToEnd(char* str );  

// 在字符串str1的末尾增加字符串 str2 
// os_memmove(&str1[strlen(str1)-1],&str2[0],sizeof(char)*(strlen(str2)));
//内存区域情况为  str1  和  str2 内存区域不重叠
int addToEnd(char* str1 ,char * str2);  


void* os_memmove(void *dest, const void *src, size_t n);

int main(void)
{
    char str[20] ;//= "0123456789" ;  // 方法一  char*的初始化
    char* str1 = strcpy(str,"0123456789");
    char mchar = '5';
    printf("前 str1=%s \n",str1);
    removeCharFromStr(str1,mchar);
    printf("后 str1=%s\n",str1);



    char* str2 = strcpy(str,"0123456789");
    printf("前 str2=%s\n",str2);
    firstToEnd(str2);
    printf("后 str2=%s\n",str2);


    char* str3 = strcpy(str,"0123456789");
    char mchar3 = '6';
    printf("前 str3=%s\n",str3);
    addCharFromStr(str3,mchar3);
    printf("后 str3=%s\n",str3);





    char strArr[20] ;
    char* str4 = strcpy(strArr,"0123456789");
    char* str5 = strcpy(str,"abcdef");
    printf("前 str4=%s\n",str4);
    addToEnd(str4,str5);
    printf("后 str4=%s \n",str4);

    return 0;
}



int addCharFromStr(char* str , char num){
    if (str != NULL) {
        if (strlen(str) == 0) {
            return -1;  // str 字符串为空 长度为0 
        }
    }else{
        return -1; //  // str 字符指针为空
    }



    int len = strlen(str);  
    int i = 0;
    for (i = 0; i < len; ++i)   
    {
        //printf("str[%d] = %c | address = %p \n",i,str[i],&str[i]);
        if(str[i] == num){
            //printf("getvalue: str[%d] = %c | address = %p \n",i,str[i],&str[i]);
            //printf("getvalue: str[%d] = %c | address = %p \n",i+1,str[i+1],&str[i+1]);
            os_memmove(&str[i+1],&str[i],sizeof(char)*(len-i+1));
            return 0;
        }
    }  
    return 1;
}




int addToEnd(char* str1 , char* str2){

    if (str1 != NULL || str2 != NULL ) {
        if (strlen(str1) == 0 || strlen(str2)) {
            return -1;  // str 字符串为空
        }
    }else{
        return -1;  // str1 str2 字符串指针地址空 
    }



    int len1 = strlen(str1);  
    int len2 = strlen(str2);  
    int i = 0;

    //&str1[len1]  表示str1字符串 字符结束标识 ‘\0’的位置
    //  &str2[0] 表示str2字符串起始的位置
    // sizeof(char)*(len2+1) 是因为需要把str2的字符串结束标识 ‘\0’ 也拷贝到str1 否则乱码
    os_memmove(&str1[len1],&str2[0],sizeof(char)*(len2+1)); 

    return 1;
}


int firstToEnd(char* str ){

    if (str != NULL) {
        if (strlen(str) == 0) {
            return -1;  // str 字符串为空 长度为0 
        }
    }else{
        return -1; //  // str 字符指针为空
    }

    int len = strlen(str);  
    int i = 0;
    char mChar = str[0];
    os_memmove(&str[0],&str[1],sizeof(char)*(len-1));
    str[len-1] = mChar;
    return 1;
}


int removeCharFromStr(char* str , char num){


    if (str != NULL) {
        if (strlen(str) == 0) {
            return -1;  // str 字符串为空 长度为0 
        }
    }else{
        return -1; //  // str 字符指针为空
    }

    int len = strlen(str);  
    int i = 0;
    for (i = 0; i < len; ++i)   
    {
        //printf("str[%d] = %c | address = %p \n",i,str[i],&str[i]);
        if(str[i] == num){
            //printf("getvalue: str[%d] = %c | address = %p \n",i,str[i],&str[i]);
            //printf("getvalue: str[%d] = %c | address = %p \n",i+1,str[i+1],&str[i+1]);
            os_memmove(&str[i],&str[i+1],sizeof(char)*(len-i-1));
            return 0;
        }
    }  
    return 1;
}

// char地址 转为  void * 
void*  os_memmove(void *dest, const void *src, size_t n)
{


    if (dest == NULL ) {
        return NULL;
    }
    if (src == NULL ) {
        return NULL;
    }
    if ( n <= 0 ) {
        return NULL;
    }
    // dest 和 src地址 如果  dest 地址在前的话  直接调用 os_memcpy 就可以完成 os_memmove函数
    // os_memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上
    if(dest == src)  return NULL;

    //printf("n=%d \n",n);
    //printf("destAddre=%p  *dest=%p  dest=%d > \n",&dest,(char*) dest,dest);
    //printf("srcAddre=%p  *src=%p  src=%d > \n",&src,(char*) src,src);
    // if((dest < src ) &&  ( (dest+n) > src )) // dest在内存前面 src在后面  dest后面的n个字节包含src
    if((dest < src ) &&  ( (((char *)dest)+n) > src )) 
    {
        /* overlapping areas */
        printf(" 情况一  dest在内存前  src在内存后 内存区域重叠  \n");
        char *d = (char *) dest ;
        const char *s = (const char *) src ;
        // 以 (char *) dest + n 地址为数据填充起始地址 依次往前进行后续遍历                                   
        //以  (char *) src  + n 地址为数据来源起始地址 依次往前进行后续遍历
        while (n--)  // 从后往前进行数据拷贝  以防止内存践踏  村内重叠
            *d++ = *s++;

        // 如果两个地址内存有践踏 执行就会出现错误输出  可分别注释之前操作 打开以下两行代码体会
        //memcpy  和  memmove 执行代码的差异
        //memcpy(dest, src, n);        // memcpy()函数必须确保两个地址不重叠 否则会发生内存践踏
        //memmove(dest, src, n);    // 两个地址 内存践踏  执行不会错误
    }
    // src 在内存前面 dest 在后面  src后面的n个字节包含dest
    //else if((src < dest) && ((src+n)  >dest)){ 
    else if((src < dest) && ((((char *)src) + n )  > dest)){ 
        /* overlapping areas */
        printf("情况二  src在内存前  dest在内存后 内存区域重叠  \n");
        char *d = (char *) dest + n;
        const char *s = (const char *) src + n;
        // 以 (char *) dest + n 地址为数据填充起始地址 依次往前进行后续遍历                                   
        //以  (char *) src  + n 地址为数据来源起始地址 依次往前进行后续遍历
        while (n--)  // 从后往前进行数据拷贝  以防止内存践踏  村内重叠
            *d-- = *s--;

        // 如果两个地址内存有践踏 执行就会出现错误输出  可分别注释之前操作 打开以下两行代码体会
        //memcpy  和  memmove 执行代码的差异
        //memcpy(dest, src, n);        // memcpy()函数必须确保两个地址不重叠 否则会发生内存践踏
        //memmove(dest, src, n);    // 两个地址 内存践踏  执行不会错误
    }

    else{  // dest 和 src 之间没有空间的重叠区域  ,直接使用 memcpy函数操作
        printf(" 情况三 内存区域不重叠  \n");
        //printf("--%s-----0---dest--%p\n",(char *)dest,&dest);
        //printf("---%s----1-----src--%p\n",(char *)src,&src);  
        memcpy(dest, src, n);  // 只有在 内存不重叠情况下使用 memcpy 才使得程序运行正确
        //printf("--%s-----2---dest--%p\n",(char *)dest,&dest);
        //printf("---%s----3-----src--%p\n",(char *)src,&src);

    }


    return dest;
}

//输出结果:
//前 str1=0123456789 
//情况一  dest在内存前  src在内存后 内存区域重叠  
//后 str1=0123467899
//前 str2=0123456789
//情况一  dest在内存前  src在内存后 内存区域重叠  
//后 str2=1234567890
//前 str3=0123456789
//情况二  src在内存前  dest在内存后 内存区域重叠  
//后 str3=01234567789
//前 str4=0123456789
//后 str4=0123456789 

-5 .os_realloc_array()函数定义

定义
struct eloop_sock *tmp;
// 重新分配空间使得增加一个Item
tmp = os_realloc_array(table->table, table->count + 1,sizeof(struct eloop_sock));
// 最终调用到
os_realloc(ptr, nmemb * size)

// WPA_Supplicant 实现函数
static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
{
//  (~(size_t) 0)  == (~(unsigned int) 0) == 4294967295
// if (unsigned int size && unsigned int nmemb > 4294967295 / unsigned int size) 
// 用于过滤  size =0 以及  nmemb 太大 大到超过极值的情况
    if (size && nmemb > (~(size_t) 0) / size)   
        return NULL;
    return os_realloc(ptr, nmemb * size);
}


//----------------------Demo--------------------------
#include <stdio.h>

int main()
{
    //unsigned int num = 134217728;
    unsigned int num = 1;
    unsigned int size  = 32;
    printf("~(unsigned int) 0)  = %u\n",(~(unsigned int) 0));
    printf("(~(unsigned int) 0) / size  = %d\n",(~(unsigned int) 0) / size );

    if (size  &&  num > (~(unsigned int) 0) / size){
    printf("NULL !!!! \n");
    }else{
    printf("os_realloc \n");
    }

   return 0;
}
//输出:
//~(unsigned int) 0)  = 4294967295
//(~(unsigned int) 0) / size  = 134217727
//os_realloc 
//----------------------Demo--------------------------

//malloc、calloc、realloc的区别
三个函数的申明分别是:
void* malloc(unsigned size);
void* realloc(void* ptr, unsigned newsize);
void* calloc(size_t numElements, size_t sizeOfElement);

(1)函数 malloc()
在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址.
(2)函数 calloc()
与malloc相似,参数sizeOfElement为申请地址的单位元素长度,numElements为元素个数,
即在内存中申请numElements*sizeOfElement字节大小的连续地址空间.
(3)函数 realloc()
给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度

http://blog.youkuaiyun.com/shuaishuai80/article/details/6140979
C语言跟内存申请相关的函数主要有 alloca 、calloc、malloc、free、realloc等.
<1> alloca 是向栈申请内存,因此无需释放.
<2>malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间
<3>calloc则将初始化这部分的内存,设置为0.
<4>realloc则对malloc申请的内存进行大小的调整.
<5>申请的内存最终需要通过函数free来释放.
当程序运行过程中malloc了,但是没有free的话,会造成【内存泄漏】.一部分的内存没有被使用
但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,使得系统可用内存不断减少
但是内存泄漏仅仅指程序在运行时,程序退出时,OS将回收所有的资源.
因此,适当的重起一下程序,有时候还是有点作用.

realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,对于缩小,则被缩小的那一部分的内容会丢失.realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.相反,realloc返回的指针很可能指向一个新的地址.

realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,问题就出来了,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上

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

int main(int argc, char* argv[])   
{   
    char a = 'a';
    char b = 'b';
    char *p1 = &a;
    char *p2 = &b;

    //查看字符数据
    printf("字符a的内容: %c\n",a); // 64位系统char的长度为1字节 
    printf("字符b的内容: %c\n",b);

    //查看字符的地址
    printf("字符a的内存地址为: %p\n",&a); // 64位系统char的长度为1字节 
    printf("字符b的内存地址为: %p\n",&b);

    //查看指针本身的地址
    printf("指针 p1 自身的内存地址为: %p\n",&p1); //64位系统指针的长度为8字节
    printf("指针 p2 自身的内存地址为: %p\n",&p2);

    //查看指针保存的内存地址(即指向的内存地址)【指针保存的都是内存地址】 
    printf("指针 p1的内容(内存地址): %p\n",p1); //64位系统指针的长度为8字节
    printf("指针 p2的内容(内存地址) %p\n",p2);
    // p1 =  &a  p2 = &b


    //*p 表示 *含义为查看指针p保存的数据(内存地址)对应的真实内存地址里面所保存的内容
    printf("指针 p1 显示指针内容指向的地址保存的内容为: %c\n",*p1); //64位系统指针的长度为8字节
    printf("指针 p2 显示指针内容指向的地址保存的内容为: %c\n",*p2);
    printf("=================================\n");

    char *p,*q,*t; 
    p = (char *)malloc(10); // 分配十个字节空间,头地址保存在指针p中  分配的地址在【堆内存】中
    printf("指针 p 自身的内存地址为: %p\n",&p);// 查看指针p 自己本身的地址
    printf("指针 p 保存的内存地址为: %p\n",p);// 查看指针p保存的地址  堆内存地址

    q = (char *)alloca(10); // 保存在栈内存中 和其他局部变量一样不需要主动释放 【栈地址】
    printf("指针 q 自身的内存地址为: %p\n",&q);// 查看指针p 自己本身的地址
    printf("指针 q 保存的内存地址为: %p\n",q);// 查看指针p保存的地址  堆内存地址


    t = (char *)realloc(p,1000);  // 扩展p指向的地址为起点 再增加 1000个字节
    printf("指针 t 自身的内存地址为: %p\n",&t);// 查看指针p 自己本身的地址
    printf("指针 t 保存的内存地址为: %p\n",t);// 查看指针p保存的地址 realloc分析的 堆内存地址
    printf("指针 t 保存的realloc函数分配的内存地址最后地址为: %p\n",t+1000); 
    return 0;   
}
//输出结果:
//字符a的内容: a
//字符b的内容: b
//字符a的内存地址为: 0x7fff0df67886     
//字符b的内存地址为: 0x7fff0df67887     //字符之间 地址相差1 即char占用一个字节空间
//指针 p1 自身的内存地址为: 0x7fff0df67888
//指针 p2 自身的内存地址为: 0x7fff0df67890  // 88->90 16进制 相差8个字节  64位系统指针占用8字节
//指针 p1的内容(内存地址): 0x7fff0df67886   // 指针的内容 保存着另一个内存地址  此处就是保存了字符a的内存地址
//指针 p2的内容(内存地址) 0x7fff0df67887
//指针 *p1 显示指针内容指向的地址保存的内容为: a
//指针 *p2 显示指针内容指向的地址保存的内容为: b  // *p2 就是表示指针自己保存的内存地址,对应这个地址上保存的内容
//=================================
//指针 p 自身的内存地址为: 0x7fff0df67898
//指针 p 保存的内存地址为: 0x1432010 // 通过 malloc  realloc  calloc 申请到的地址 【堆地址】区别于其他的【栈地址】
//指针 q 自身的内存地址为: 0x7fff0df678a0 //通过alloca申请到的是栈地址这个地址和布局变量地址一样 不推荐使用该函数
//指针 q 保存的内存地址为: 0x7fff0df67850
//指针 t 自身的内存地址为: 0x7fff0df678a8
//指针 t 保存的内存地址为: 0x1432010  // 从realloc函数重新分配到的一块内存地址
//指针 t 保存的realloc函数分配的内存地址最后地址为: 0x14323f8

-6 .gettimeofday()时间获取函数

gettimeofday()函数的使用方法:
1.简介:
在C语言中可以使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙
2.函数原型:头文件 sys/time.h
int gettimeofday(struct timeval*tv,struct timezone *tz )
3.说明:
gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中
在gettimeofday()函数中tv或者tz都可以为空。如果为空则就不返回其对应的结构体。
4.函数执行成功后返回0,失败后返回-1,错误代码存于errno中。
struct timeval{
long tv_sec; //秒
long tv_usec;//微妙
};

struct timezone{
int tz_minuteswest;//和greenwich 时间差了多少分钟//
int tz_dsttime; //type of DST correction// 日光节约时间的状态?

}
时区结构中 tz_dsttime 在linux中不支持,应该置为0,通常将参数 tz 设置为NULL,表示使用当前系统的时区

http://www.runoob.com/try/runcode.php?filename=helloworld&type=c
https://tool.lu/coderunner/

//gettimeofday  时间获取函数解析

#include <stdio.h>
#include <malloc.h>  
#include <stdlib.h>
#include <time.h>
#include<sys/time.h>
#include<unistd.h> // 包含 sleep(秒)   usleep(毫秒)函数

typedef long os_time_t;
struct os_reltime {  // 封装的数据结构  各个OS可能不一样
    os_time_t sec;  // 秒 毫秒
    os_time_t usec;
};

struct os_time {  
    os_time_t sec;
    os_time_t usec;
};

int os_reltime_before(struct os_reltime *a,struct os_reltime *b)
{
    // 判断两个时间点 前后     a < b  的话 |  a 在 b之前  | a小于b  那么返回true
    // a的秒数long值小于b的秒数long值 那么 a时间在前   b时间在后 返回true  1  反之返回Flase 0 
    // a的秒数long值等于b的秒数long值  那么判断 毫秒数的long值 来判断 时间  
    // a时间在前  b时间在后 返回true  1  反之返回Flase 0 
    return (a->sec < b->sec) || (a->sec == b->sec && a->usec < b->usec);
}


int os_get_time(struct os_time *t)
{
    int res;
    struct timeval tv;
    res = gettimeofday(&tv, NULL); // 在Linux真正调用获取时间的函数
    t->sec = tv.tv_sec;
    t->usec = tv.tv_usec;
    return res;
}


int os_get_reltime(struct os_reltime *t)
{
    /* consider using performance counters or so instead */
    struct os_time now;
    int res = os_get_time(&now);
    t->sec = now.sec; // 获得now的秒值 
    t->usec = now.usec;// 获得now的毫秒秒值 
    return res;
}


// 获取两个时间点的间隔时间  // 一般来说  a 大于 b   才会被执行  结果放到最后的参数 os_reltime 中
void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,struct os_reltime *res)
{
    res->sec = a->sec - b->sec;
    res->usec = a->usec - b->usec;
    if (res->usec < 0) {
        res->sec--;
        res->usec += 1000000;
    }
}



int main()

{
    struct  timeval    mTimeval;
    struct  timezone   tz;
    gettimeofday(&mTimeval,&tz);
    printf("tv_sec: 秒数: %ld\n",mTimeval.tv_sec);
    printf("tv_usec:毫秒数 %ld\n",mTimeval.tv_usec);
    printf("中国在东八时区  格林尼治时间 英国时间 是东零西零时区 差8个小时  480分钟\n");
    printf("tz_minuteswest 和格林尼治时间(英国时间)相差分钟数:%d\n",tz.tz_minuteswest);
    printf("tz_dsttime:%d\n",tz.tz_dsttime); // 默认为0 

    struct os_reltime now1, now2, inter_time;
    os_get_reltime(&now1); // 获取现在的时间


    /*  // eloop_run() 方法中 判断是否有超时事件发生
        if (timeout) {
            os_get_reltime(&now);   // 获取现在的时间
                                       // 当前时间 和 超时链表的时间对比  如果 now 当前小 则还未发生超时  
            if (os_reltime_before(&now, &timeout->time)) 

            // 获取当前时间 和 timeout 链表中记录的时间的差值  放到 tv 结构体中 这个tv用于后续的select的超时时间
            // int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
            // 最后一个参数 设置了 超时 时间 
            os_reltime_sub(&timeout->time, &now, &tv); 
*/
    sleep(2); // 使程序睡两秒 
    //usleep(1500);   // 使程序睡1.5秒 
    os_get_reltime(&now2);
    os_reltime_sub(&now2, &now1, &inter_time);  // 获取 睡觉前后相差的时间值
    printf("sleep 相差秒数: %ld 秒 \n",inter_time.sec);
    printf("sleep 相差毫秒数: %ld 毫秒\n",inter_time.usec);
    return 0 ;



}


/*
输出结果:
tv_sec: 秒数: 1518073410
tv_usec:毫秒数 581093
中国在东八时区  格林尼治时间 英国时间 是东零西零时区 差8个小时  480分钟
tz_minuteswest 和格林尼治时间(英国时间)相差分钟数:-480
tz_dsttime:0
sleep 相差秒数: 2 秒 
sleep 相差毫秒数: 80 毫秒

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值