第一题

  #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. 常见的http头信息

    请求头:用于告诉服务器,客户机支持的数据类型 accept-charset:用于告诉服务器,客户机采用的编码 accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式 Host:客户机 ...

  2. 使用BeanUtils操作Bean属性

    package com.wzh.test.beanutils; import java.lang.reflect.InvocationTargetException; import java.text ...

  3. UILabel使用技巧

    UILabel的各种属性与方法的使用(转) #import "LabelTestViewController.h" @implementation LabelTestViewCon ...

  4. *(volatile unsigned long *) 语法

    #define GPJ2CON (*(volatile unsigned long *) 0xE0200280) 开始对于上面的语法不太理解,但通过拆分,我们可以看的更明白: 1.(unsigned ...

  5. windows timeGetTime() 函数 获取系统从开机到现在的毫秒时间值

    #include <windows.h> #include <iostream> #pragma comment( lib,"winmm.lib" ) in ...

  6. jquery on off 方法

    $("p").on("click",function(){alert("The paragraph was clicked.");}); $ ...

  7. Python的安装与基本语法

    一,Python简介      Python是一种计算机程序设计语言,都是使用C语言实现,但是比C语言容易学习,易于阅读.Python可以应用于众多领域,整体呈上升趋势,广泛使用Python来做的事一 ...

  8. ZOJ 3329 【概率DP】

    题意: 给你三个均匀k面筛子. 分别有k1 k2 k3个面,每个面朝上的概率是相等的. 如果第一个筛子出现a第二个筛子出现b第三个筛子出现c那么置零. 否则在当前和加上三个点数之和. 求当前和大于n需 ...

  9. (medium)LeetCode 241.Different Ways to Add Parentheses

    Given a string of numbers and operators, return all possible results from computing all the differen ...

  10. Isilon OneFS Simulator Install Guide

    Isilon build for storage data Use VMware converter to convert node1 to ESX(参考silon_OneFS_Simulator_I ...