今日学到 Day 4

A、memset

memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。

void *memset(void *s, int c, size_t n); 
  • s指向要填充的内存块。
  • c是要被设置的值。
  • n是要被设置该值的字符数。
  • 返回类型是一个指向存储区s的指针。
  • 一、不能任意赋值
  • memset函数是按照字节对内存块进行初始化,所以不能用它将int数组出初始化为0和-1之外的其他值(除非该值高字节和低字节相同)。
    其实c的实际范围应该在0~255,因为memset函数只能取c的后八位给所输入范围的每个字节。也就是说无论c多大只有后八位二进制是有效的。

    =================================================================================================
    对于int a[4];
    memset(a, -1, sizeof(a)) 与 memset(a, 511, sizeof(a)) 所赋值的结果一样都为-1:
    因为 -1 的二进制码为(11111111 11111111 11111111 11111111);511 的二进制码为(00000000 00000000 00000001 11111111);
    后八位均为(11111111),所以数组中的每个字节都被赋值为(11111111)。
    注意int占四个字节,例如a[0]的四个字节都被赋值为(11111111),那么a[0](11111111 11111111 11111111 11111111),即a[0] = -1。

    先来看两个例子:
    例一:对char类型的数组a初始化,设置元素全为’1’

    int main(){
        char a[4];
        memset(a,'1',4);
        for(int i=0; i<4; i++){
            cout<<a[i]<<" ";
        }
        return 0;
    }

  • 实际输出:1 1 1 1
    例二:对int类型的数组a初始化,设置元素值全为1

    int main(){
        int a[4];
        memset(a,1,sizeof(a));
        for(int i=0; i<4; i++){
            cout<<a[i]<<" ";
        }
        return 0;
    }

  • 实际输出:16843009 16843009 16843009 16843009

  •  对于第二个程序,数组a是整型的,一般int所占内存空间为4个字节,所以在使用memset赋值时,下面的语句是错误的:

    int a[4];
    memset(a,1,4);
    由于memset函数是以字节为单位进行赋值的,所以上述代码是为数组a的前4个字节进行赋值,那么所得到的执行结果就只能是:16843009 0 0 0

    正确的memset语句应为:

    memset(a,1,16); //int所占内存为4字节的情况
    memset(a,1,sizeof(a));
    至于为什么不是预期得到的1,将在下面的第二点进行说明。

    当然,不同的机器上int的大小可能不同,所以最好用sizeof()函数。


    2、为什么第一个程序可以正确赋值1而第二个不可以?

    这就又回到了刚刚说的第一个问题,memset函数中只能取c的后八位赋给每个字节。

    第一个程序中,数组a是字符型的,字符型占据的内存大小就是1Byte,而memset函数也是以字节为单位进行赋值的,所以输出正确。
    第二个程序中,数组a是整型的,整型占据的内存大小为4Byte,而memset函数还是按照字节为单位进行赋值,将1(00000001)赋给每一个字节。那么对于a[0]来说,其值为(00000001 00000001 00000001 00000001),即十进制的16843009。

  • B、段错误


  • 1 使用非法的内存地址(指针),包括使用未经初始化及已经释放的指针、不存在的地址、受系统保护的地址,只读的地址等,这一类也是最常见和最好解决的段错误问题,使用GDB print一下即可知道原因。

    2 内存读/写越界。包括数组访问越界,或在使用一些写内存的函数时,长度指定不正确或者这些函数本身不能指定长度,典型的函数有strcpy(strncpy),sprintf(snprint)等等。

    3 对于C++对象,应该通过相应类的接口来去内存进行操作,禁止通过其返回的指针对内存进行写操作,典型的如string类的c_str()接口,如果你强制往其返回的指针进行写操作肯定会段错误的,因为其返回的地址是只读的。

    4 函数不要返回其中局部对象的引用或地址,当函数返回时,函数栈弹出,局部对象的地址将失效,改写或读这些地址都会造成未知的后果。

    5 避免在栈中定义过大的数组,否则可能导致进程的栈空间不足,此时也会出现段错误,同样的,在创建进程/线程时如果不知道此线程/进程最大需要多少栈空间时最好不要在代码中指定栈大小,应该使用系统默认的,这样问题比较好查,ulimit一下即可知道。这类问题也是为什么我的程序在其他平台跑得好好的,为什么一移植到这个平台就段错误了。

    6 操作系统的相关限制,如:进程可以分配的最大内存,进程可以打开的最大文件描述符个数等,在Linux下这些需要通过ulimit、setrlimit、sysctl等来解除相关的限制,这类段错误问题在系统移植中也经常发现,以前我们移植Linux的程序到VxWorks下时经常遇到(VxWorks要改内核配置来解决)。

    7 多线程的程序,涉及到多个线程同时操作一块内存时必须进行互斥,否则内存中的内容将不可预料。

    8 在多线程环境下使用非线程安全的函数调用,例如 strerror 函数等。

    9 在有信号的环境中,使用不可重入函数调用,而这些函数内部会读或写某片内存区,当信号中断时,内存写操作将被打断,而下次进入时将无法避免地出错。

    10 跨进程传递某个地址,传递的都是经过映射的虚拟地址,对另外一个进程是不通用的。

    11 某些有特殊要求的系统调用,例如epool_wait,正常情况下使用close关闭一个套接字后,epool会不再返回这个socket上的事件,但是如果你使用dup或dup2操作,将导致epool无法进行移除操作,此时再进行读写操作肯定是段错误的。

  • C、qsort

  • 1.整形数组排序

    注意:

    1.比较函数的参数类型为void* ,我们要进行强制类型转换!且要解引用才能得到对应的值! 

    2.若我们想排成降序,只需要写成e2-e1即可

  • void qsort ( 

        void* base, //要排序的目标数组
        size_t num,     //待排序的元素个数
        size_t width,    //一个元素的大小,单位是字节
        int(*cmp)(const void* e1, const void* e2)

    );        

    其中cmp是函数指针,cmp指向的是:排序时,用来比较两个元素的函数。需要自己编写。
    ————————————————

  • int cmp_int(const void* e1, const void* e2)
    {
        return *(int*)e1 - *(int*)e2;
    }
    void test1()
    {
        int arr[] = { 9,8,7,6,7,5,4,8 };
        int sz = sizeof(arr) / sizeof(arr[0]);
        qsort(arr, sz, sizeof(arr[0]), cmp_int);
        Print(arr, sz);
    }
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值