C puzzles详解【1-5题】
第一题
#include<stdio.h> #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {,,,,,,}; int main()
{
int d; for(d=-;d <= (TOTAL_ELEMENTS-);d++)
printf("%d\n",array[d+]); return ;
}
知识点讲解:
- 有关sizeof;
sizeof是C语言的关键字,sizeof的结果在编译阶段就已由编译器得出。sizeof不是函数,没有原型;sizeof(x)返回size_t类型的值,size_t != unsigned int,在32位系统中,size_t为无符号四字节数,在64位系统中size_t为无符号八字节数;
自定义sizeof的实现:
#define sizeof(var) (size_t)((typeof(var) *)0 + 1)
或
#define my_sizeof(x) \
({ \
typeof(x) _x; \
(size_t)((char *)(&_x + ) - (char *)(&_x)); \
})
或
size_t my_sizeof(x)
{
typeof(x) _x;
return (size_t)((char *)(&_x + ) -(char *)(&_x));
}
(typeof(var) *)0表示一个基址,还有两个类似的用法
#define FIELD_OFFSET(_struct_name_, _field_name_) ((unsigned int)&((_struct_name_ *)0->_filed_name_))
#define FIELD_SIZE(_struct_name_, _filed_name_) (sizeof((_struct_name_ *)0->_filed_name_))
几种容易混乱的形式
#define my_sizeof(var) (size_t)((char *)(&var + 1) - (char *)(&var))
#define my_sizeof(var) (size_t)((&var + 1) - (&var))
#define my_sizeof(var) ((size_t)(&var + 1) - (size_t)(&var))
解释如下:
假设(&var + 1)= 0x00000022, (&var) = 0x00000011
第一种形式:(size_t)((&var + 1) - (&var))
(&var + 1)和(&var)都为指向粒度为sizeof(var)的内存的指针,他们是以sizeof(var)为单位相减的,差值为1;
第二种形式: (size_t)((char *)(&var + 1) - (char *)(&var))
(&var + 1)和(&var)都为指向粒度为sizeof(char)的内存的指针,他们是以sizeof(char)为单位相减的,差值为0x11;
第三种形式:((size_t)(&var + 1) - (size_t)(&var))
(&var + 1)和(&var)虽然都为指向粒度为sizeof(var)的内存的指针,但他们分别被强制转换成(size_t)后,这两个地址就变成了普通的unsigned型整数,他们就被当做普通的整数来相减,差值为0x11。
- 整形常数,如-1, 2, 5等等默认为int型;
- 不同类型的数参加运算时,类型会自动转换;
下图摘自网络。就我目前的理解看来就是,精度低的转换为精度高的,表示数范围小的转换为表示数范围大的,有符号的转换为无符号的。
高 double ←← float
↑
↑
↑
long
↑
↑
↑ unsigned
↑ ↑
低
int ←←
char,short
自动转换顺序表
- 32位机和64位机中的数据类型长度。
数据类型 |
64 |
32 |
char |
1 |
1 |
char * |
8 |
4 |
short int |
2 |
2 |
int |
4 |
4 |
long |
8 |
4 |
long long |
8 |
8 |
float |
4 |
4 |
double |
8 |
8 |
size_t |
8 |
4 |
错误点讲解:
for后面有这样一条判断语句,我们来看一下它是如何进行自动类型转换的。
d <= |
1)各参数类型如下:
d: int
TOTAL_ELEMENT:
size_t
2: int
2)转换过程:
2由int转换为size_t,(TOTAL_ELEMENT-2)值为5,类型为size_t;d由int转换为size_t,负数在内存中以补码形式存在,故当d = -1时,d在内存中存的值为ffffffff,当d转换为size_t类型时,值为ffffffff。所以“d <= (TOTAL_ELEMENTS-2)”为false,不会进入for循环,也就不会打印数组信息。
解决方法:
d <= (TOTAL_ELEMENTS-)
改为
d <= (int)(TOTAL_ELEMENTS-)
第二题
#include<stdio.h> void OS_Solaris_print()
{
printf("Solaris - Sun Microsystems\n");
} void OS_Windows_print()
{
printf("Windows - Microsoft\n"); }
void OS_HP-UX_print()
{
printf("HP-UX - Hewlett Packard\n");
} int main()
{
int num;
printf("Enter the number (1-3):\n");
scanf("%d",&num);
switch(num)
{
case :
OS_Solaris_print();
break;
case :
OS_Windows_print();
break;
case :
OS_HP-UX_print();
break;
default:
printf("Hmm! only 1-3 :-)\n");
break;
} return ;
}
现象:
编译不通过。
错误点讲解:
变量的名字中只允许出现字母、数字、下划线,且变量名只能以字母或下划线开头。“OS_HP-UX_print();”该函数名中出现非法字符‘-’,故编译出错。
第三题
enum {false,true}; int main()
{
int i=;
do
{
printf("%d\n",i);
i++;
if(i < )
continue;
}while(false);
return ;
}
知识点讲解:
《The C Programming Language》 3.7节中对“continue”的解释如下:
it causes the next iteration of the enclosing for, while, or do loop to begin. In the while and do, this means that the test part is executed immediately; in the for, control passes to the increment step.
所以该程序执行到continue时,跳转到判断语句while(false)处执行。该程序的执行结果为1。
第四题
#include <stdio.h>
#include <unistd.h>
int main()
{
while()
{
fprintf(stdout,"hello-out");
fprintf(stderr,"hello-err");
sleep();
}
return ;
}
现象:
只输出”hello-err”。
原因:
“hello-out”被放在stdout缓冲区,stdout缓冲区为行缓冲。stderr无缓冲区。
若
fprintf(stdout,"hello-out");
改为
fprintf(stdout,"hello-out\n");
即会输出”hello-out”。
知识点讲解:
- 标准输出缓冲区:属于行缓冲;
下列情况下stdout缓冲区会被刷新:
1)人为刷新fflush(stdout);
2)main函数退出;
程序交回控制给操作系统之前C运行库必须进行清理工作,其中一部分是刷新stdout缓冲区。
3)scanf()执行前会清空stdout缓冲区。
另:
fork()创建子进程,子进程会继承父进程的缓冲区。fork调用,整个父进程空间会原模原样地复制到子进程中,包括指令、变量值、程序调用栈、环境变量、缓冲区等等。
经典面试题:
题目:请问下面的程序一共输出多少个“-”?
答案:8次
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h> int main(void)
{
int i;
for(i=; i<; i++){
fork();
printf("-");
} return ;
}
- 标准输入缓冲区:属于行缓冲;
fflush对输入流为参数的行为未定义,不同的编译器对fflush的未定义行为实现不一样。不推荐使用fflush(stdin)刷新输入缓冲区。
经典的清空stdin缓冲区的方式为:
void fflush_stdin()
{
char c = ;
while((c = getchar()) != ‘\n’ && c != EOF);
}
另:
scanf的一种用法:scanf("%[^\n]", str);接受除’\n’之外的所有字符,’\n’仍留在缓冲区中;
#include <stdio.h>
int main()
{
char str[] = {};
char ch = ;
scanf(“%s”, str);
printf(“%s\n”, str);
scanf(“%[^\n]”, str);
printf(“%s\n”, str);
scanf(“%c”, &ch);
printf(“%d\n”, ch);
return ;
} 编译运行
输入:i love you
输出:i
love you
- 标准错误缓冲区:无缓冲区;
- 缓冲区设置函数:
void setbuf(FILE * restrict stream, char * restrict buf);
int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);
setvbuf的mode参数有:
_IOFBF(满缓冲);
_IOLBF(行缓冲):如:stdio, stdout;
_IONBF(无缓冲):如:stderr;
第五题
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a) int main()
{
printf("%s\n",h(f(,)));
printf("%s\n",g(f(,)));
return ;
}
知识点讲解:
参考:C-FAQ 11.17 http://c-faq.com/ansi/stringize.html
- 宏定义中的两个符号”#”, ”##”
#: 将其后面的宏参数进行字符串化操作(stringfication),即对它所引用的宏变量通过替换后在其左右各加上一个双引号。
##:连接符(concatenator),用来将两个token连接为一个token。连接的对象是token就行,不一定是宏的变量。
- 当宏参数也为宏时,如果宏定义里对宏参数使用了#或##,宏参数不会被展开
看两个例子:
#define PARAM(x) x
#define ADDPARAM(x) INT_##x
要求展开宏PARAM(ADDRPARAM(1))
宏展开的顺序为:
PARAM(ADDRPARAM(1))
->PARAM(INT_1)
->“INT_1”
#define PARAM(x) #x
#define ADDPARAM(x) INT_##x
要求展开宏PARAM(ADDPARAM(1))
宏展开的顺序为:
PARAM(ADDPARAM(1))
->“ADDRPARAM(1)”
若宏定义中对宏参数使用了#或##,可对该宏再加一层宏,实现宏参数的展开。
#define _PARAM(x) #x
#define PARAM(x) _PARAM(x)
#define ADDPARAM(x) INT_##x
展开宏PARAM(ADDPARAM(1))的顺序为:
PARAM(ADDPARAM(1))
->PARAM(INT_1)
->_PARAM(INT_1)
->“INT_1”
题目讲解:
h(f(1,2))的展开顺序为:
h(f(,))
->h()
->g()
->””
g(f(1,2))的展开顺序为:
g(f(,))
->”f(,)”
C puzzles详解【1-5题】的更多相关文章
- C puzzles详解【51-57题】
第五十一题 Write a C function which does the addition of two integers without using the '+' operator. You ...
- C puzzles详解【46-50题】
第四十六题 What does the following macro do? #define ROUNDUP(x,n) ((x+n-1)&(~(n-1))) 题目讲解: 参考:http:// ...
- C puzzles详解【38-45题】
第三十八题 What is the bug in the following program? #include <stdlib.h> #include <stdio.h> # ...
- C puzzles详解【34-37题】
第三十四题 The following times. But you can notice that, it doesn't work. #include <stdio.h> int ma ...
- C puzzles详解【31-33题】
第三十一题 The following is a simple C program to read and print an integer. But it is not working proper ...
- C puzzles详解【26-30题】
第二十六题(不会) The following is a simple program which implements a minimal version of banner command ava ...
- C puzzles详解【21-25题】
第二十一题 What is the potential problem with the following C program? #include <stdio.h> int main( ...
- C puzzles详解【16-20题】
第十六题 The following is a small C program split across files. What do you expect the output to be, whe ...
- C puzzles详解【13-15题】
第十三题 int CountBits(unsigned int x) { ; while(x) { count++; x = x&(x-); } return count; } 知识点讲解 位 ...
- C puzzles详解【9-12题】
第九题 #include <stdio.h> int main() { float f=0.0f; int i; ;i<;i++) f = f + 0.1f; if(f == 1.0 ...
随机推荐
- 使用maven下载依赖包及maven常见问题汇总
最近下载了SPRING3.1.4,发现只有SPRING相关的源码,没有其依赖的jar包.SPRING依赖的jar相当多,自己一个一个的下载比较费劲,就仔细阅读了SPRING下载说明,新版本的SPRIN ...
- Python标准库07 信号 (signal包,部分os包)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在了解了Linux的信号基础之后,Python标准库中的signal包就很容易学习 ...
- Python标准库05 存储对象 (pickle包,cPickle包)
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 谢谢reverland纠错 在之前对Python对象的介绍中 (面向对象的基本概念 ...
- Python深入03 对象的属性
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python一切皆对象(object),每个对象都可能有多个属性(attribut ...
- nginx 环境搭建(基于linux)
Nginx是一种服务器软件,故而其最主要.最基本的功能当然是可以与服务器硬件结合,让程序员可以将程序放在Nginx服务器上,将程序发布出去,让成千上万的网民可以浏览.除此之外,Nginx是一种高性能的 ...
- lucene-源码分析
lucene-源码分析 http://www.cnblogs.com/forfuture1978/p/3940965.html
- java socket通讯(二)处理多个客户端连接
通过java socket通讯(一) 入门示例,就可以实现服务端和客户端的socket通讯,但是上一个例子只能实现一个服务端和一个客户端之间的通讯,如果有多个客户端连接服务端,则需要通过多线程技术来实 ...
- ElasticSearch 常用的查询过滤语句
query 和 filter 的区别请看: http://www.cnblogs.com/ghj1976/p/5292740.html Filter DSL term 过滤 term主要用于精确匹配 ...
- Python 的 List 要印出 中文 編碼
Python 的 List 如果有中文的話, 會印出 \xe4\xb8… 等等的編碼, 要如何印出中文呢(如下範例)? (Debug 方便查看) View Raw Code? >>> ...
- PetaPoco.Core.ttinclude修改
/// <summary> /// Adds the singular rule. /// </summary> /// <param name="rule&q ...