-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 毫秒
*/