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

  • 9
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值