读书笔记之:你必须知道的495个C语言问题

摘要:
《495个你必须知道的C语言问题》一书列出了495个C语言问题。这些问题很有代表性。这是一个由某人提出的真实问题,然后作者给出了答案。ANSI/ISO标准声称它可以返回空指针或0字节指针,其行为由实现定义。较差的伪随机数生成器在低阶中不是随机的。不幸的是,一些系统提供了这样的伪随机数生成器。因此,最好使用高位。

《你必须知道的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 产生高斯分布的随机数

读书笔记之:你必须知道的495个C语言问题第1张

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

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

14.7

读书笔记之:你必须知道的495个C语言问题第2张

读书笔记之:你必须知道的495个C语言问题第3张

读书笔记之:你必须知道的495个C语言问题第4张

读书笔记之:你必须知道的495个C语言问题第5张

读书笔记之:你必须知道的495个C语言问题第6张

读书笔记之:你必须知道的495个C语言问题第7张

读书笔记之:你必须知道的495个C语言问题第8张

读书笔记之:你必须知道的495个C语言问题第9张

读书笔记之:你必须知道的495个C语言问题第10张

读书笔记之:你必须知道的495个C语言问题第11张

读书笔记之:你必须知道的495个C语言问题第12张

 20.8 实现位数组或集合

读书笔记之:你必须知道的495个C语言问题第13张

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 调换字节

 读书笔记之:你必须知道的495个C语言问题第14张

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 交换两个变量的值

读书笔记之:你必须知道的495个C语言问题第15张

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

读书笔记之:你必须知道的495个C语言问题第16张

读书笔记之:你必须知道的495个C语言问题第17张

读书笔记之:你必须知道的495个C语言问题第18张

 

 

 

 

   

 

免责声明:文章转载自《读书笔记之:你必须知道的495个C语言问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇C中数组与指针及多维数组读书笔记之:C语言深度剖析下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

void及void指针含义的深刻解析

void的含义 void即“无类型”,void *则为“无类型指针”,能够指向不论什么数据类型。void指针使用规范①void指针能够指向随意类型的数据,亦就可以用随意数据类型的指针对void指针赋值。比如:int * pint;void *pvoid;pvoid = pint; /*只是不能pint= pvoid; */假设要将pvoid赋给其它类型指...

005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)

一.目的: 自己拼接IP头,TCP头,计算效验和,将生成的报文用原始套接字发送出去。 若使用tcpdump能监听有对方服务器的包回应,则证明TCP报文是正确的! 二.数据结构: TCP首部结构图: struct tcphdr结构体定义: 1 struct tcphdr //在#include <netinet/tcp.h>中定义 2 {...

004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)

一.大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去。 server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印。 二.其中: client端IP:192.168.11.104 端口:8600 serv...

C语言内存分布

C语言内存分布 典型的C语言程序内存表示分区共有5个部分: 正文段 Text segment 已初始化数据段(数据段)Initialized data segment 未初始化数据段(bss)Uninitialized data segment 堆 Stack 栈 Heap 具体分布图 各个分区的作用 正文段 CPU执行的机器指令部分 通常可共享...

C语言指定初始化器解析及其应用

指定初始化器的概念 C90 标准要求初始化程序中的元素以固定的顺序出现,与要初始化的数组或结构体中的元素顺序相同。但是在新标准 C99 中,增加了一个新的特性:指定初始化器。利用该特性可以初始化指定的数组或者结构体元素。 数组的指定初始化器 一维数组的指定初始化器 利用指定初始化器的特性,我们可以这样定义并初始化一个数组: int a[6] = {[4] =...

//利用指针删除母串中的指定子串

//利用指针删除母串中的指定子串 #include<stdio.h>#include<string.h> int fun(char *a,char *b) //函数功能是判断两字符串的首字符相同时,母串是否包含子串 { while(*a==*b&&*a&&*b) {...