zoukankan      html  css  js  c++  java
  • 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
    ",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
    ");
    }
    
    void OS_Windows_print()
    {
            printf("Windows - Microsoft
    ");
    
    }
    void OS_HP-UX_print()
    {
            printf("HP-UX - Hewlett Packard
    ");
    }
    
    int main()
    {
            int num;
            printf("Enter the number (1-3):
    ");
            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 :-)
    ");
                            break;
            }
    
            return 0;
    }

    现象:

    编译不通过。

    错误点讲解:

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

    第三题

    enum {false,true};
    
    int main()
    {
            int i=1;
            do
            {
                    printf("%d
    ",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
    ");

    即会输出”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()) != ‘
    ’ && c != EOF);
    }

    另:

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

    #include <stdio.h>
    int main()
    {
        char str[10] = {0};
        char ch = 1;
        scanf(“%s”, str);
        printf(“%s
    ”,  str);
        scanf(“%[^
    ]”, str);
    printf(“%s
    ”,  str);
    scanf(“%c”, &ch);
    printf(“%d
    ”, 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
    ",h(f(1,2)));
              printf("%s
    ",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)”
  • 相关阅读:
    linux 系统tar文件压缩打包命令
    linux如何查看所有的用户和组信息?
    go语言之行--golang操作redis、mysql大全
    Redis集群的5种使用方式,各自优缺点分析
    docker-compose搭建redis哨兵集群
    windows版 navicat_15.0.18 安装
    redis aof数据持久化
    redis rdb数据持久化
    03.redis 事务
    02 redis 三种特殊的数据类型
  • 原文地址:https://www.cnblogs.com/tanghuimin0713/p/3987517.html
Copyright © 2011-2022 走看看