C puzzles详解【1-5题】

第一题

  #include<stdio.h>

  #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
  int array[] = {23,34,12,17,204,99,16};

  int main()
  {
      int d;

      for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
          printf("%d\n",array[d+1]);

      return 0;
  }

知识点讲解:

  • 有关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 + 1) - (char *)(&_x)); })
或
size_t my_sizeof(x)
{
    typeof(x) _x;
    return (size_t)((char *)(&_x + 1) -(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-2)
改为
d <= (int)(TOTAL_ELEMENTS-2)

第二题

#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 1:
                        OS_Solaris_print();
                        break;
                case 2:
                        OS_Windows_print();
                        break;
                case 3:
                        OS_HP-UX_print();
                        break;
                default:
                        printf("Hmm! only 1-3 :-)\n");
                        break;
        }

        return 0;
}

现象:

编译不通过。

错误点讲解:

变量的名字中只允许出现字母、数字、下划线,且变量名只能以字母或下划线开头。“OS_HP-UX_print();”该函数名中出现非法字符‘-’,故编译出错。

第三题

enum {false,true};

int main()
{
        int i=1;
        do
        {
                printf("%d\n",i);
                i++;
                if(i < 15)
                    continue;
        }while(false);
        return 0;
}

知识点讲解:

《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(1)
          {
                  fprintf(stdout,"hello-out");
                  fprintf(stderr,"hello-err");
                  sleep(1);
          }
          return 0;
  }

现象:

只输出”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=0; i<2; i++){
      fork();
      printf("-");
   }

   return 0;
}
  • 标准输入缓冲区:属于行缓冲;

fflush对输入流为参数的行为未定义,不同的编译器对fflush的未定义行为实现不一样。不推荐使用fflush(stdin)刷新输入缓冲区。

经典的清空stdin缓冲区的方式为:

void fflush_stdin()
{
    char c = 0;
    while((c = getchar()) != ‘\n’ && c != EOF);
}

另:

scanf的一种用法:scanf("%[^\n]", str);接受除’\n’之外的所有字符,’\n’仍留在缓冲区中;

#include <stdio.h>
int main()
{
    char str[10] = {0};
    char ch = 1;
    scanf(“%s”, str);
    printf(“%s\n”,  str);
    scanf(“%[^\n]”, str);
printf(“%s\n”,  str);
scanf(“%c”, &ch);
printf(“%d\n”, ch);
return 0;
}

编译运行
输入:i love you
输出:i
       love you
      10
  • 标准错误缓冲区:无缓冲区;
  • 缓冲区设置函数:
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(1,2)));
          printf("%s\n",g(f(1,2)));
          return 0;
  }

知识点讲解:

参考: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(1,2))
->h(12)
->g(12)
->”12”

g(f(1,2))的展开顺序为:

g(f(1,2))
->”f(1,2)”
时间: 2024-08-23 15:38:25

C puzzles详解【1-5题】的相关文章

C puzzles详解【51-57题】

第五十一题 Write a C function which does the addition of two integers without using the '+' operator. You can use only the bitwise operators.(Remember the good old method of implementing the full-adder circuit using the or, and, xor gates....) 题目讲解: 参考:ht

C puzzles详解【38-45题】

第三十八题 What is the bug in the following program? #include <stdlib.h> #include <stdio.h> #define SIZE 15 int main() { int *a, i; a = malloc(SIZE*sizeof(int)); for (i=0; i<SIZE; i++) *(a + i) = i * i; for (i=0; i<SIZE; i++) printf("%d\

C puzzles详解【46-50题】

第四十六题 What does the following macro do? #define ROUNDUP(x,n) ((x+n-1)&(~(n-1))) 题目讲解: 参考:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=814501 用于内存对齐,n为2的幂. 第四十七题 Most of the C programming books, give the following example for the definitio

C puzzles详解【31-33题】

第三十一题 The following is a simple C program to read and print an integer. But it is not working properly. What is(are) the mistake(s)? #include <stdio.h> int main() { int n; printf("Enter a number:\n"); scanf("%d\n",n); printf(&quo

C puzzles详解【34-37题】

第三十四题 The following is a piece of C code, whose intention was to print a minus sign 20 times. But you can notice that, it doesn't work. #include <stdio.h> int main() { int i; int n = 20; for( i = 0; i < n; i-- ) printf("-"); return 0; }

C puzzles详解【16-20题】

第十六题 The following is a small C program split across files. What do you expect the output to be, when both of them compiled together and run? File1.c int arr[80]; File2.c extern int *arr; int main() { arr[1] = 100; return 0; } 题目讲解: 编译完运行发生段错. File1.

C puzzles详解【26-30题】

第二十六题(不会) The following is a simple program which implements a minimal version of banner command available on most *nix systems. Find out the logic used in the program. #include<stdio.h> #include<ctype.h> char t[]={ 0,0,0,0,0,0,12,18,33,63, 33

C puzzles详解【21-25题】

第二十一题 What is the potential problem with the following C program? #include <stdio.h> int main() { char str[80]; printf("Enter the string:"); scanf("%s",str); printf("You entered:%s\n",str); return 0; } 题目讲解: 易造成数组越界,sca

C puzzles详解【6-8题】

第六题 #include<stdio.h> int main() { int a=10; switch(a) { case '1': printf("ONE\n"); break; case '2': printf("TWO\n"); break; defa1ut: printf("NONE\n"); } return 0; } 题目讲解: “defalut”拼写错误. 注意a为int型,case后的’1’,’2’为char型. 第七

C puzzles详解【13-15题】

第十三题 int CountBits(unsigned int x) { int count=0; while(x) { count++; x = x&(x-1); } return count; } 知识点讲解 位运算 关于位运算的一些例子参考: http://www.ugcs.caltech.edu/~wnoise/base2.html 题目讲解 x&(x-1)常见的两种应用: 1)计算x二进制形式中1的个数,每循环一次,将x二进制形式最右边的1变成0: 2)判断x是否是2的幂,若x&