zoukankan      html  css  js  c++  java
  • 读书笔记之:你必须知道的495个C语言问题

    《你必须知道的495个C语言问题》这本书中列出了495个C语言中的问题,这些问题都比较都代表性,这是真实的有人提出的问题,然后作者给出了解答。这个有对应的网站:http://c-faq-chn.sourceforge.net/

    2.12 怎样向数据文件读写结构体
      使用fwrite()编写结构相对简单
      fwrite(&some_struct,sizeof somestruct,1,fp);
      对应的fread函数可以再把它读出来,此处fwrite受到一个结构的指针并把这个结构的内存映像作为字节流写入文件。sizeof操作符计算出结构占用的字节数。
      但是这样用内存映像写出的数据文件却是不能够移植的,尤其是当结构中包含浮点成员或指针的时候。结构的内存布局跟机器和编译器都有关。不同的编译器可能使用不同数量的填充位,不同机器上基本类型的大小和字节顺序也不尽相同。因此,作为内存映像写出的结构在别的机器上(甚至是被别的编译器编译之后)不一定能被读回来。
      同时注意如果结构包含任何指针(char*字符串或指向其他数据结构的指针),则只有指针值会被写入文件。当它们再次被读回来的时候可能已经失效。最后为了广泛的可移植性,你必需用"b"标志打开文件。
    2.15 如何确定域在结构中的字节偏移量
    ANSI C在<stddef.h>中定义了offsetof()宏,利用offsetof(structs,f)可以计算出域f在结构s中的偏移量。
    其实现方式为:
    #define offsetof(type,f) (int)&(((type*)0)->f)
    看下面的解说
    struct AAA
    {
        int i;
        int j;
    };
    struct AAA *pAAA;
    pAAA=new AAA;
    这时,pAAA实际上是一个Pointer, 指向某一确定的内存地址,比如0x1234;
    而 pAAA->i 整体是一个int型变量,其地址是&(pAAA->i) ,'&'为取址运算符;
    那么&(pAAA->i)一定等于0x1234,因为i是结构体AAA的第一个元素。
    而&(pAAA->j)一定是0x1234 + 0x4 = 0x1238; 因为sizeof(int) = 4;
    这个做法的巧妙之处就是:它把"0"作为上例中的pAAA,那么 &(pAAA->j)就是j的offset

    6.6 动态分配多维数组
    方案一:分配一个指针数组,然后把每个指针初始化为动态分配的行:
       int **array1=malloc(nrows*sizeof(int*));
       for(i=0;i<nrows;i++)
           array1[i]=malloc(ncolumns*sizeof(int));
       ...
       for(i=0;i<nrows;i++)
           free(array1[i]);
        free(array1);

    方案二:数组内容连续,但是后来重新分配行的时候会比较困难:
       int **array2=malloc(nrows*sizeof(int*));
       array2[0]=malloc(nrows*ncolumns*sizeof(int));
       for(i=1;i<nrows;i++)
           array2[i]=array2[0]+i*ncolumns;
        free(array2[0]);
        free(array2);

    11.28 malloc(0)有什么用?返回一个空指针还是指向0字节的指针
    ANSI/ISO标准声称它可能返回任意一种,其行为由实现定义。

    13.15 怎样生成一个随机数
    标准C库有一个随机数生成器rand。
    下面是Park和MIller提供的"最小标准"的可移植随机数生成器的C语言实现:
    #define a 16807
    #define m 2147483647
    #define q (m/a)
    #define r (m%a)
    static long int seed=1;
    long int PMrand()
    {
        long int hi=seed/q;
        long int lo=seed%q;
        long int test=a*lo-r*hi;
        if(test>0)
            seed=test;
        else
            seed=test+m;

        return seed;
    }
    如果要返回(0,1)范围内的浮点数,需要修改:
    double PMrand()
    ....
    return (double)seed/m;

    13.16 怎样获得某一范围内的随机整数?
    直接使用这种方法:rand()%N(试图返回从0到N-1的整数)不好,因为许多随机数生成器的低位并不随机。
    一种较好的方法是:
    (int)((double)rand()/((double)RAND_MAX+1)*N)
    如果不希望使用浮点数,另一种方法是:
    rand()/(RAND_MAX/N+1)
    这两种方法都是需要知道RAND_MAX,并且假设N要远远小于RAND_MAX.如果N值接近RAND_MAX而随机数生成器的范围又不是N的整数倍,那么这些方法都会失效,某些输出会比其他的频率更高。
    [M,N]范围内的随机整数:
    M+rand()/(RAND_MAX/(N-M+1)+1)

    13.17 每次执行程序,rand都返回相同的数字序列,为什么?
    这是多数伪随机数生成器的一个特征,它们生成的随机数总是从同一个数字开始,然后是同一个序列。如果不需要这种可预测性,可以调用srand用真正随机的值来初始化模拟随机数生成器的种子。
    srand((unsigned int)time((tim_t*)NULL));
    这个代码可能存在的问题是:time()返回的time_t可能是浮点值,转换到无符号整数时有可能上溢,导致不可移植。

    13.18 我需要随机的真/假值,所以,我就直接用rand()%,可是我得到交替的0,1,0,1....?
    低劣的伪随机数生成器在低位中并不随机,很不幸,某些系统就提供这样的伪随机数生成器。实际上,周期为2^e的纯线性同余随机数生成器的低n位会以2^n为周期重复,而很多e位机的随机数就是这样写出来的。因此,最好使用高位。

    13.20 产生高斯分布的随机数

    14.6 如何取整
    简单的方法是(int)(x+0.5)

    改进方法:(int)(x<0?x-0.5:x+0.5)

    14.7

     20.8 实现位数组或集合

    20.9 判断及其的字节顺序是大端还是小端
    方法一:使用指针
    int x=1;
    if(*(char*)&x==1)
        printf("little-endian\n");
    else
        printf("big-endian\n");
    方法二:使用联合
    union{
        int i;
        char c;
    }x;
    x.i=1;
    if(x.c==1)
        printf("little-endian\n");
    else
        printf("big-endian\n");

    20.10 调换字节

     

    20.13 计算出整数中为1的位的个数
    使用查表的方式,并且采用每4位为一个单位
     static int bitcounts[]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
     int bitcount(unsigned int u)
     {
         int n=0;
         for(;u!=0;u>>=4)
             n++bitcounts[u&0xf];
         return n;
     }
      20.18 交换两个变量的值

    20.24 实现异或的宏
    #define XOR(a,b) ((a)&&!(b)||!(a)&&(b))
    #define XOR(a,b) (!!(a)^!!(b)) //两次取反,严格0/1规范化
    #define XOR(a,b) (!!(a)!=!!(b))
    #define XOR(a,b) (!(a)^!(b))
    #define XOR(a,b) (!(a)!=!(b))
    #define XOR(a,b) ((a)?!(b)::!!(b))
    20.25 C语言中没有循环移位操作符
    部分原因是C语言的类型大小没有精确定义,但是,对大小已知的机器字进行循环移位很有意义。
    用两个常规移位和一个按位或操作就可以实现循环移位
    如:
    (x<<13)|(x>>3)
    对一个16位的机器字进行循环左移13位


    20.26 C语言的词法分析
    规则是:在一个简单的从左到右扫描中的任何时刻,最长的记号被划分,不管最终的结果是否有意义。

     

     

     

     

       

     

  • 相关阅读:
    webpack基础
    LeetCode232. 用栈实现队列做题笔记
    mysql 时间加减一个月
    leetcode 1381. 设计一个支持增量操作的栈 思路与算法
    LeetCode 141. 环形链表 做题笔记
    leetcode 707. 设计链表 做题笔记
    leetcode 876. 链表的中间结点 做题笔记
    leetcode 143. 重排链表 做题笔记
    leetcode 1365. 有多少小于当前数字的数字 做题笔记
    LeetCode1360. 日期之间隔几天 做题笔记
  • 原文地址:https://www.cnblogs.com/xkfz007/p/2603332.html
Copyright © 2011-2022 走看看