堆栈基础常识

安装netmap

参考网站:安装教程

字符转mac地址

   方法1:
#include <stdio.h>
#include <string.h>
#define ETH_ALEN 6
#define NTY_SELF_MAC	"00:0c:29:58:6f:f4"
int str2mac(char *mac, char *str) {
      char *p = str;
	  unsigned char value = 0x0;
	  int i = 0;
	  while (*p != '\0') { // '\0'可以作为字符串的结束标识字符
		if (*p == ':') {
			mac[i++] = value;
			value = 0x0;
		} else {
			unsigned char temp = *p;
			if (temp <= '9' && temp >= '0') {
				temp -= '0';
			} else if (temp <= 'f' && temp >= 'a') {
				temp -= 'a';
				temp += 10;
			} else if (temp <= 'F' && temp >= 'A') {
				temp -= 'A';
				temp += 10;
			} else {	
				break;
			}
			value <<= 4; //不断对vlaue的左移动四位(保证value永远为0x00)
			value |= temp;
		}
		p ++;
	}
	mac[i] |= value;
	return 0;
}
void print_mac(unsigned char *mac) { //unsigned char很关键
	int i = 0;
	for (i = 0;i < ETH_ALEN-1;i ++) {
		printf("%02x:", mac[i]);
	}
	printf("%02x", mac[i]);
}
struct arphdr {
    unsigned char smac[ETH_ALEN];
};
int main() {
    struct arphdr arph;
    //结构体初始化
    bzero(&arph, sizeof(struct arphdr));
    str2mac(arph.smac, NTY_SELF_MAC);
    print_mac(arph.smac);
    return 0;
}
输出的结果:00:0c:29:58:6f:f4

方法2:

#include <stdio.h>
#include <string.h>
static int hex2num(char c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	if (c >= 'A' && c <= 'F')
		return c - 'A' + 10;
	return -1;
}
void hexstr2mac(char *dst, char *src) {
	int i=0;
    while(i<6) {
        if(' ' == *src||':'== *src||'"'== *src||'\''== *src) {
            src++;
            continue;
        }
        *(dst+i) = ((hex2num(*src)<<4)|hex2num(*(src+1)));
	    i++;
        src += 2;        
    }
}
void print_mac(unsigned char *mac) {   //使用unsigned char很关键,保证正产输出
	int i = 0;
	for (i = 0;i < 5;i ++) {
		printf("%02x:", mac[i]);
	}
	printf("%02x", mac[i]);
}
int main(){
    char mac[6]={0};
    char* macstr="AA:BB:CC:DD:EE:FF";
    hexstr2mac(mac,macstr);
    print_mac(mac);
    return 0;
}

补充内容

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct S{
    int d;
    char name[4];
};
int main(){
    struct S *dd =malloc(sizeof(struct S)); //指针分配空间,有void*->struct S *dd的过程
    memset(&(dd->d), 0, sizeof(int));
    //memset(dd->name, 0, sizeof(8));
    dd->name[0]='0';
    dd->name[1]='1';
    dd->name[2]='2';
    dd->name[3]='3';
    printf("dd->d=%d,dd->d=0x%08x\n",dd->d,&(dd->d));
    printf("dd->name=0x%08x\n",dd->name);
    printf("&(dd->name)=0x%08x\n",&(dd->name));
    printf("&(dd->name[1])=0x%08x\n",&(dd->name[1]));
    printf("&(dd->name[2])=0x%08x\n",&(dd->name[1]));
    printf("&(dd->name[3])=0x%08x\n",&(dd->name[3]));
    printf("dd->name[1]=%d\n",dd->name[1]);
    printf("dd->name[2]=%d\n",dd->name[2]);
    return 0;
}
输出的结果:
dd->d=0,dd->d=0x14e4f2a0
dd->name=0x14e4f2a4
&(dd->name)=0x14e4f2a4
&(dd->name[1])=0x14e4f2a5
&(dd->name[2])=0x14e4f2a5
&(dd->name[3])=0x14e4f2a7
dd->name[1]=49
dd->name[2]=50
动态开辟内存的几种方式

C语言动态开辟的管理方式(malloc,calloc,realloc)
相同点:

  1. 都是C语言的库函数,在使用时都必须包含相应的头文件
  2. 都是从上申请空间,使用完成时必须要通过free来进行释放,否则,内存泄漏
  3. 申请空间成功后,返回空间的首地址,申请失败返回NULL,因此在使用时必须判空
  4. 返回值类型为void*,在使用时要进行强制类型转化
    注意事项:(1)malloc申请的内存,一旦申请成功,就只能为你所用,其他地方的malloc无法申请到这块内存;(2)free释放内存后,这块内存重获自由,程序其他地方调用malloc,有可能系统会把这块内存分配出去;(3)free释放内存后,这块内存里面的数据有可能变成垃圾值,有可能还是原来的数值,当然也有可能无法访问。
    free释放掉内存后,应该将指针置为空,避免乱用,被free掉的内存不要再去使用它,虽然有时候使用不会报错,如果进程终结, 对应的堆内存自动被回收, 不会泄露(解释malloc,不需要free也可以,程序结束,进程资源释放)。
    不同点:
    malloc:
#include<stdlib.h>
#include<malloc.h>
void *malloc( size_t size ); //注意指针函数

malloc只是开空间,参数是要申请空间的字节大小

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
void main( void )
{
   int *buffer;
   size_t size;
   buffer = (int *)malloc( 1000 * sizeof( int ) ) //动态开辟内存
   //对开辟的内存进行判断
   if( buffer == NULL )
      printf( "Can't allocate memory\n" );	
   if( buffer != NULL )
      printf( "Allocated 1000 int integers\n" );
   free( buffer );
}

calloc:

#include <malloc.h>
#include <stdlib.h>
void *calloc( size_t num, size_t size );

1.开辟空间后空间会被初始化为0.(这个很关键)
2.参数有两个,一个是申请元素的个数num,另一个表示单个元素类型的大小size

#include <stdio.h>
#include <malloc.h>
void main( void )
{
   long *buffer;
   buffer = (long *)calloc( 40, sizeof( long ) );
   if( buffer != NULL )
      printf( "Allocated 40 long integers\n" );
   else
      printf( "Can't allocate memory\n" );
   free( buffer );
}

realloc:

#include <malloc.h>
#include <stdlib.h>
void *realloc( void *memblock, size_t size );

realloc是进行空间大小调整的操作。(新的大小要大于原来的大小,否则会导致数据丢失
功能:先判断当前的指针是否有足够的连续空间,如果有,扩大memblock指向的地址,并且将memblock返回,如果空间不够,先按照size指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来memblock所指内存区域(注:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
注意事项:
1.如果memblock为NULL,则realloc()和malloc()类似。分配一个size的内存块,返回一个指向该内存块的指针。
2.如果size大小为0,那么释放memblock指向的内存,并返回NULL。
3.如果没有足够可用的内存用来完成重新分配(扩大原来的内存块或者分配新的内存块),则返回NULL。而原来的内存块保持不变

int main () 
{ 
     int* p2 = (int*)calloc(4, sizeof (int)); 
     //通过realloc来扩大p2的空间,并把新的地址赋值给p3
     int* p3 = (int*)realloc(p2, sizeof(int)*10);  
     free(p3 ); 
     return 0}

结论:
1.realloc失败的时候,返回NULL
2.realloc失败的时候,原来的内存不改变,不会释放也不会移动
3.假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址
4.如果size为0,效果等同于free()。(对指针本身进行释放)
5.传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
6.传递给realloc的指针可以为空,等同于malloc。

C语言动态开辟内存模板

struct callbacks {
       void (*log_fatal)(void *logger, const char *file, int line,
                          const char *function, const char *format, ...)
            __attribute__((format(printf, 5, 6)));
};
static void log_fatal(void *logger, const char *file, int line,
                      const char *function, const char *format, ...)
{
        va_list argp;

        va_start(argp, format);
        logging(file, line, function, FATAL, format, argp);
        va_end(argp);
}
#define LOG_FATAL(cb, fmt, args...) \
        (cb)->log_fatal((cb)->logger, __FILE__, __LINE__, __func__, fmt, ##args)
#define PLOG_FATAL(cb, fmt, args...) \
        LOG_FATAL(cb, fmt ": %s", ##args, strerror(errno))
typedef unsigned long size_t
void *calloc_or_die(size_t nmemb, size_t size, struct callbacks *cb)
{
        void *ptr = calloc(nmemb, size); //初始化状态为0
        if (!ptr)
                PLOG_FATAL(cb, "calloc"); //打印日志创建不成功
        return ptr;
}
struct flow *f = calloc_or_die(1, sizeof(struct flow), t->cb); //调用函数,t->cb回调函数,打印log日志

C++语言动态内存开辟的管理方式

new和delete
注:C++兼容C语言,所以C语言内存管理方式在C++中可以继续使用,但是有了C++机制的关键字,优先使用C++的关键字
申请内置类型空间:

void Test() 
{ 
      // 动态申请一个int类型的空间 
      int* ptr4 = new int; 
      // 动态申请一个int类型的空间并初始化为10 
      int* ptr5 = new int(10); //注意是圆括号
      delete ptr4;  //注意释放的方式
      delete ptr5; 
      //管理对象组
      // 动态申请3个int类型的空间 
      int* ptr6 = new int[3]; //注意是方括号
      delete[] ptr6; //注意释放的方式
}
class Test
{
public:
    Test(): _data(0)
    {
        cout<<"Test():"<<this<<endl;
    }
    ~Test()
    {
        cout<<"~Test():"<<this<<endl;
    }
private:
    int _data;
}
void Test1()
{
  //C语言动态开辟内存
	//申请一个Test类型空间
    Test *p1=(Test*)malloc(sizeof(Test)); //1个
    free(p1);
    //申请10个Test类型空间
    Test *p2=(Test*)malloc(sizeof(Test)*10); //10个
    free(p2);
}
void Test2()
{
  //C++语言动态开辟内存
	//申请一个Test类型的空间并初始化为10
    Test *p2=new Test(10); //注意赋值 1个
    delete p2;
    //申请10个Test类型的空间
    Test *p3=new Test[10]; //10个
    delete[] p3;
}

在申请自定义类型的空间时,new会自动调用构造函数,delete会自动调用析构函数
**注意事项:**申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和 delete[],搭配使用,避免出错。(对于内置类型来说,new[]开辟的空间用delete来释放程序不会崩溃,因为内置类型的空间开辟和释放不会调用构造函数和析构函数

malloc/free和new/delete的异同点

malloc/free和new/delete的共同点是:
都是从堆上申请空间,并且需要用户手动释放
malloc/free和new/delete不同的地方是:
1.malloc和free是函数,new和delete是操作符
2.malloc申请的空间不会初始化,new可以初始化(很关键)
3.malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
4.malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5.malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常(健壮性代码一定要对new开辟的内存进行抛异常处理)
6.申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

堆与栈的区别

1.堆空间的内存是动态分配的,一般存放对象,并且需要手动释放内存。需要程序员自己申请并且指明大小,如C语言的malloc函数。
2.栈空间的内存由系统自动分配,一般存放局部变量等,不需要手动管理内存。例如声明函数中的一个局部变量int b,系统在栈中自动为b开辟空间。

  • [1] 管理方式:
    对于栈来讲,由编译器自动管理,无需我们手动控制。
    对于堆来说,释放工作由程序员控制,容易产生memory warning。
  • [2]申请大小:
    栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域.即栈顶的地址和栈的最大容量是系统预先规好的。栈的大小是1M,如果申请空间超过栈的剩余空间时,将提示overflow.因此,能从栈获得的空间较小。(linux可以通过ulimit等命令查看)
    堆:堆是向高地址扩展的数据结构,是不连续的内存区域.这是因为系统是用链表来存储空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址.堆得大小受限于计算机系统中有效地虚拟内存.由此可见,堆获得的空间比较灵活,也比较大。
  • [3] 碎片问题:
    对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
    对于栈来讲,则不会存在这个问题,因为栈是先进后出得队列,它们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。
  • [4]分配方式:
    堆都是动态分配的,没有静态分配的堆。
    栈有两种分配方式:静态分配和动态分配.静态分配是编译器完成的,比如局部变量的分配.动态分配由alloc函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现,静态变量是不入栈的
  • [5]分配效率:
    栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
    堆则是C/C++函数库提供的,它的机制是很复杂的。例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

为什么栈比堆快?

从两方面来考虑:
1.分配和释放,堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞,碎片),这些都会花费一定的时间,具体可以看看MALLOC和FREE的源代码,他们做了很多额外的工作,而栈却不需要这些(有专门的系统指令)。
2.访问时间,访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次。另外,堆的内容被操作系统交换到外存的概率比栈大,栈一般是不会被交换出去的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值