第一题

  #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 <=
(TOTAL_ELEMENTS-2)

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题】的更多相关文章

  1. C puzzles详解【51-57题】

    第五十一题 Write a C function which does the addition of two integers without using the '+' operator. You ...

  2. C puzzles详解【46-50题】

    第四十六题 What does the following macro do? #define ROUNDUP(x,n) ((x+n-1)&(~(n-1))) 题目讲解: 参考:http:// ...

  3. C puzzles详解【38-45题】

    第三十八题 What is the bug in the following program? #include <stdlib.h> #include <stdio.h> # ...

  4. C puzzles详解【34-37题】

    第三十四题 The following times. But you can notice that, it doesn't work. #include <stdio.h> int ma ...

  5. C puzzles详解【31-33题】

    第三十一题 The following is a simple C program to read and print an integer. But it is not working proper ...

  6. C puzzles详解【26-30题】

    第二十六题(不会) The following is a simple program which implements a minimal version of banner command ava ...

  7. C puzzles详解【21-25题】

    第二十一题 What is the potential problem with the following C program? #include <stdio.h> int main( ...

  8. C puzzles详解【16-20题】

    第十六题 The following is a small C program split across files. What do you expect the output to be, whe ...

  9. C puzzles详解【13-15题】

    第十三题 int CountBits(unsigned int x) { ; while(x) { count++; x = x&(x-); } return count; } 知识点讲解 位 ...

  10. 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 ...

随机推荐

  1. Tomcat启动过程原理详解

    基于Java的Web 应用程序是 servlet.JSP 页面.静态页面.类和其他资源的集合,它们可以用标准方式打包,并运行在来自多个供应商的多个容器.Web 应用程序存在于结构化层次结构的目录中,该 ...

  2. mongodb 数据导入导出

    mongoexport 命令异常方便简单强大! 连接数据库: jkmiao@jkmiao-ipin:~$ mongo 192.168.1.xx:xxx/jd_58tc_raw 1.  导出10条数据到 ...

  3. iOS推送通知的实现步骤

    一.关于推送通知 来源:http://blog.csdn.net/enuola/article/details/8627283 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序 ...

  4. English Notes

    1. Thanks for stopping by.慢走不送. 2. I don't like to judge a book by it's cover.我不喜欢以貌取人. 3. Rush hour ...

  5. .nil? .empty? .blank? .present? in Ruby on Rails

    We get confused when there are many options to choose from. Same is the case when it comes to use an ...

  6. samba共享修改匿名用户为非nobody

    samba共享修改匿名用户为非nobody 1)linux的samba用户,如果开启匿名用户登陆共享权限,security 设置为 share ,配置如下:[root@centos69:~]$grep ...

  7. this.Invoke

    this.Invoke(new Action(() =>      {              }));

  8. jxl的API

    jxl的API 使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵Excel文件并不是一件容易的事.在Web应用日益盛行的今天,通过Web来操作Exce ...

  9. Android下实现控件的叠加显示

    <FrameLayout android:layout_width="fill_parent" android:layout_height="match_paren ...

  10. OC对象中的getter方法中不能用self.

    @interface boy:NSObject { int _age; } - (void)setAge:(int)age; - (int)age; @end @implementation boy ...