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

     

     

     

     

       

     

  • 相关阅读:
    Vue 函数
    VUE 基础语法
    C# txt文件操作
    C# 添加应用程序包
    Js 倒计时跳转
    Redis集群(主从集群)(一)
    JAVA基础总结001(《JAVA核心技术》)
    Linux学习001——文件和用户
    Linux——ELK集群搭建
    Linux安装jdk
  • 原文地址:https://www.cnblogs.com/xkfz007/p/2603332.html
Copyright © 2011-2022 走看看