C程序设计语言(K&R)笔记

C/C++ 专栏收录该内容
29 篇文章 0 订阅



1.表达式中float类型的操作数不会自动转换为double类型。一般来说,数学函数(如math.h)使用双精度类型的变量。使用float类型主要是为了在使用较大数组时节省存储空间,有时也为了节省机器执行时间(双精度算数元算特别费时)。
2.scanf函数调用时,字符串类型不需要写‘&’,因为其本身即为地址。
3.EOF可能被定义为不同的值,使用EOF等标准符号可以增强程序的可移植性,常见值有-1。
4.根据某种特定的状态做不同的行为,可以用define定义状态量,然后定义int state。如:
#define IN 0
#define OUT 1


...
if(state == IN)
    ...
else   //OUT
    ...

5. ~(~0<<n)  得到最右边n位为1,其他位是0.
6.条件表达式
 每10行一个换行符:
for(i=0;i<n,i++)  
    printf("%6d%c",a[i],(i%10==9||i==n-1)? '\n':' ');
for(i=0;i<n,i++)  
    printf((i%10==9||i==n-1)? "%6d\n":"%6d ",a[i]);


是否复数的s
printf("You have %d item%s.\n",n,n==1?"":"s");

7.在switch语句中,case的作用只是一个标号。
8.break只能跳出其所在的一级到上一级,而不是所有层次。
9.  getch和ungetch    超前读并放回一个字符
#define BUFSIZE 100


char buf[BUFSIZE];//for ungetch to buffer
int bufp=0;//the next spare index of buf


int getch(void)
{
    return (bufp>0) ? buf[--bufp] : getchar();
}


void ungetch(char c)
{
    if(bufp >= BUFSIZE-1)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++]=c;
}


10. 一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问他。外部变量的定义必须指定数组的长度,但extern声明则不一定要指定数组的长度。外部变量的初始化只能出现在其定义中。
11.在函数中,可以以程序块结构的形式定义变量,例如for语句的花括号。
12.在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义。
对于外部变量和静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(在概念上讲是在程序开始执行前进行初始化)。
对于自动变量和寄存器变量,则在每次进入函数或程序块时都将被初始化,可以不是常量表达式。

数组的初始化,如果初始化表达式的个数比数组元素少,则对外部变量、静态变量和自动变量来说,未被初始化的元素将被初始化为0。若初始化表达式的个数比数组元素多,则是错误的。

13.将一个数作为字符串打印

/*print decimal as string*/
void printd(int n)
{
    if(n<0){
        putchar('-');
        n=-n;
    }
    if(n/10)
        printd(n/10);
    putchar(n%10+'0');
}

14.引号分开的两个字符串合并打印

printf("hello"" world!");
输出:


15.宏定义中形式参数不能用带引号的字符串替换。但是如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。

预处理器中运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。

#include <stdio.h>

#define dprint(expr) printf(#expr " =%g\n",expr)
#define paste(front,back) front ## back

int main()
{
    double x=2.0,y=3.0,x2=1.0;
    dprint(x/y);
    dprint(paste(x,2));

    return 0;
}
输出

16. 获取一个整型

#include <stdio.h>
#include <ctype.h>

//assign the next integer to *pn
int getint(int *pn)
{
    int c,sign;

    while(isspace(c=getc(stdin)))//skip spaces
        ;
    if(!isdigit(c) && c!=EOF && c!='+' && c!='-'){
        ungetc(c,stdin);//it's not a number
        return 0;
    }
    sign=(c=='-')? -1:1;
    if(c=='+'||c=='-')
        c=getc(stdin);
    for(*pn=0;isdigit(c);c=getc(stdin))
        *pn=10**pn+(c-'0');
    *pn*=sign;
    if(c != EOF)
        ungetc(c,stdin);

    return c;
}
17.通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快,但另一方面,用指针实现的程序理解起来困难一些。
18.C语言保证,0永远不是有效的数据地址,因此,返回值0可用来表示发生了异常事件。指针与整数之间不能相互转换,但0是唯一的例外:常量0可以赋值给指针,指针也可以和常量0进行比较。程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量0是指针的一个特殊值。符号常量NULL定义在标准头文件<stddef.h>。

19.strcpy简单实现

void strcpy(char *d,char *s)
{
    while(*d++ = *s++)
        ;
}
20.strcmp简单实现

int strcmp(char *s,char *t)
{
    for(;*s == *t;s++,t++)
        if(*s=='\0')
            return 0;
    return *s-*t;
}

21.函数指针疑问,下面一句什么意思

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

What's happening here is indeed a cast. Lets ignore the ternary for a second and pretend that numcmp is always used. For the purpose of this question, functions can act as function pointers in C. So if you look at the type of numeric it is actually

(int (*)(int*,int*))

In order for this to be properly used in qsort it needs to have void parameters. Because the types here all have the same size with respect to parameters and return types, it's possible to substitute on for the other. All that's needed is a cast to make the compiler happy.

(int (*)(void*,void*))(numcmp )

22.typedef的作用

看上去整洁美观;使程序参数化,以提高程序的可移植性;为程序提供更好的文档,更容易理解。

23.一个联合只能用它第一个成员类型的值来初始化。

24.位字段提供了一种在字内直接定义和访问字段的能力,而不用使用按位方式的逻辑运算。

struct {
        unsigned int isk :1;
        unsigned int ise :1;
        unsigned int iss :1;
    } flags;

25.minprintf函数(处理变长参数表)

#include <stdarg.h>
#include <stdio.h>

/* minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
    va_list ap; /*points to each unnamed arg in turn*/
    char *p,*sval;
    int ival;
    double dval;

    va_start(ap,fmt);//make ap point to the first unnamed arg
    for(p=fmt;*p;p++){
        if(*p!='%'){
            putchar(*p);
            continue;
        }
        switch(*++p){
        case 'd':
            ival=va_arg(ap,int);
            printf("%d",ival);
            break;
        case 'f':
            dval=va_arg(ap,double);
            printf("%f",dval);
            break;
        case 's':
            for(sval=va_arg(ap,char*);*sval;sval++)
                putchar(*sval);
            break;
        default:
            putchar(*p);
            break;
        }
    }
    va_end(ap);//clean up when done
}







1.表达式中float类型的操作数不会自动转换为double类型。一般来说,数学函数(如math.h)使用双精度类型的变量。使用float类型主要是为了在使用较大数组时节省存储空间,有时也为了节省机器执行时间(双精度算数元算特别费时)。
2.scanf函数调用时,字符串类型不需要写‘&’,因为其本身即为地址。
3.EOF可能被定义为不同的值,使用EOF等标准符号可以增强程序的可移植性,常见值有-1。
4.根据某种特定的状态做不同的行为,可以用define定义状态量,然后定义int state。如:
#define IN 0
#define OUT 1


...
if(state == IN)
    ...
else   //OUT
    ...

5. ~(~0<<n)  得到最右边n位为1,其他位是0.
6.条件表达式
 每10行一个换行符:
for(i=0;i<n,i++)  
    printf("%6d%c",a[i],(i%10==9||i==n-1)? '\n':' ');
for(i=0;i<n,i++)  
    printf((i%10==9||i==n-1)? "%6d\n":"%6d ",a[i]);


是否复数的s
printf("You have %d item%s.\n",n,n==1?"":"s");

7.在switch语句中,case的作用只是一个标号。
8.break只能跳出其所在的一级到上一级,而不是所有层次。
9.  getch和ungetch    超前读并放回一个字符
#define BUFSIZE 100


char buf[BUFSIZE];//for ungetch to buffer
int bufp=0;//the next spare index of buf


int getch(void)
{
    return (bufp>0) ? buf[--bufp] : getchar();
}


void ungetch(char c)
{
    if(bufp >= BUFSIZE-1)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++]=c;
}


10. 一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问他。外部变量的定义必须指定数组的长度,但extern声明则不一定要指定数组的长度。外部变量的初始化只能出现在其定义中。
11.在函数中,可以以程序块结构的形式定义变量,例如for语句的花括号。
12.在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义。
对于外部变量和静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(在概念上讲是在程序开始执行前进行初始化)。
对于自动变量和寄存器变量,则在每次进入函数或程序块时都将被初始化,可以不是常量表达式。

数组的初始化,如果初始化表达式的个数比数组元素少,则对外部变量、静态变量和自动变量来说,未被初始化的元素将被初始化为0。若初始化表达式的个数比数组元素多,则是错误的。

13.将一个数作为字符串打印

/*print decimal as string*/
void printd(int n)
{
    if(n<0){
        putchar('-');
        n=-n;
    }
    if(n/10)
        printd(n/10);
    putchar(n%10+'0');
}

14.引号分开的两个字符串合并打印

printf("hello"" world!");
输出:


15.宏定义中形式参数不能用带引号的字符串替换。但是如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。

预处理器中运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。

#include <stdio.h>

#define dprint(expr) printf(#expr " =%g\n",expr)
#define paste(front,back) front ## back

int main()
{
    double x=2.0,y=3.0,x2=1.0;
    dprint(x/y);
    dprint(paste(x,2));

    return 0;
}
输出

16. 获取一个整型

#include <stdio.h>
#include <ctype.h>

//assign the next integer to *pn
int getint(int *pn)
{
    int c,sign;

    while(isspace(c=getc(stdin)))//skip spaces
        ;
    if(!isdigit(c) && c!=EOF && c!='+' && c!='-'){
        ungetc(c,stdin);//it's not a number
        return 0;
    }
    sign=(c=='-')? -1:1;
    if(c=='+'||c=='-')
        c=getc(stdin);
    for(*pn=0;isdigit(c);c=getc(stdin))
        *pn=10**pn+(c-'0');
    *pn*=sign;
    if(c != EOF)
        ungetc(c,stdin);

    return c;
}
17.通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快,但另一方面,用指针实现的程序理解起来困难一些。
18.C语言保证,0永远不是有效的数据地址,因此,返回值0可用来表示发生了异常事件。指针与整数之间不能相互转换,但0是唯一的例外:常量0可以赋值给指针,指针也可以和常量0进行比较。程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量0是指针的一个特殊值。符号常量NULL定义在标准头文件<stddef.h>。

19.strcpy简单实现

void strcpy(char *d,char *s)
{
    while(*d++ = *s++)
        ;
}
20.strcmp简单实现

int strcmp(char *s,char *t)
{
    for(;*s == *t;s++,t++)
        if(*s=='\0')
            return 0;
    return *s-*t;
}

21.函数指针疑问,下面一句什么意思

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

What's happening here is indeed a cast. Lets ignore the ternary for a second and pretend that numcmp is always used. For the purpose of this question, functions can act as function pointers in C. So if you look at the type of numeric it is actually

(int (*)(int*,int*))

In order for this to be properly used in qsort it needs to have void parameters. Because the types here all have the same size with respect to parameters and return types, it's possible to substitute on for the other. All that's needed is a cast to make the compiler happy.

(int (*)(void*,void*))(numcmp )

22.typedef的作用

看上去整洁美观;使程序参数化,以提高程序的可移植性;为程序提供更好的文档,更容易理解。

23.一个联合只能用它第一个成员类型的值来初始化。

24.位字段提供了一种在字内直接定义和访问字段的能力,而不用使用按位方式的逻辑运算。

struct {
        unsigned int isk :1;
        unsigned int ise :1;
        unsigned int iss :1;
    } flags;

25.minprintf函数(处理变长参数表)

#include <stdarg.h>
#include <stdio.h>

/* minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
    va_list ap; /*points to each unnamed arg in turn*/
    char *p,*sval;
    int ival;
    double dval;

    va_start(ap,fmt);//make ap point to the first unnamed arg
    for(p=fmt;*p;p++){
        if(*p!='%'){
            putchar(*p);
            continue;
        }
        switch(*++p){
        case 'd':
            ival=va_arg(ap,int);
            printf("%d",ival);
            break;
        case 'f':
            dval=va_arg(ap,double);
            printf("%f",dval);
            break;
        case 's':
            for(sval=va_arg(ap,char*);*sval;sval++)
                putchar(*sval);
            break;
        default:
            putchar(*p);
            break;
        }
    }
    va_end(ap);//clean up when done
}
26.cat
int cat(int argc,char* argv[])
{
    FILE *fp;
    void filecopy(FILE*,FILE*);
    char *prog=argv[0];

    if(argc==1)//no arguments
        filecopy(stdin,stdout);
    else{
        while(--argc>0){
            if((fp=fopen(*++argv,"r"))==NULL){
                fprintf(stderr,"cat: can't open %s\n",*argv);
                return 1;
            }
            else{
                filecopy(fp,stdout);
                fclose(fp);
            }
        }
    }
    if(ferror(stdout)){
        fprintf(stderr,"%s:error writing stdout\n",prog);
        return 2;
    }
    return 0;
}

void filecopy(FILE *ifp, FILE *ofp)
{
    int c;

    while((c=getc(ifp))!=EOF)
        putc(c,ofp);
}








  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值