c语言笔记.docx

上传人:b****2 文档编号:2966467 上传时间:2023-05-05 格式:DOCX 页数:28 大小:315.26KB
下载 相关 举报
c语言笔记.docx_第1页
第1页 / 共28页
c语言笔记.docx_第2页
第2页 / 共28页
c语言笔记.docx_第3页
第3页 / 共28页
c语言笔记.docx_第4页
第4页 / 共28页
c语言笔记.docx_第5页
第5页 / 共28页
c语言笔记.docx_第6页
第6页 / 共28页
c语言笔记.docx_第7页
第7页 / 共28页
c语言笔记.docx_第8页
第8页 / 共28页
c语言笔记.docx_第9页
第9页 / 共28页
c语言笔记.docx_第10页
第10页 / 共28页
c语言笔记.docx_第11页
第11页 / 共28页
c语言笔记.docx_第12页
第12页 / 共28页
c语言笔记.docx_第13页
第13页 / 共28页
c语言笔记.docx_第14页
第14页 / 共28页
c语言笔记.docx_第15页
第15页 / 共28页
c语言笔记.docx_第16页
第16页 / 共28页
c语言笔记.docx_第17页
第17页 / 共28页
c语言笔记.docx_第18页
第18页 / 共28页
c语言笔记.docx_第19页
第19页 / 共28页
c语言笔记.docx_第20页
第20页 / 共28页
亲,该文档总共28页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

c语言笔记.docx

《c语言笔记.docx》由会员分享,可在线阅读,更多相关《c语言笔记.docx(28页珍藏版)》请在冰点文库上搜索。

c语言笔记.docx

c语言笔记

1.编译和链接

将程序转化为机器可执行的代码,C语言分为三个步骤:

A.预编译。

程序首先会交给预处理器,预处理器执行以#开头的指令,然后给程序添加指令,或者修改指令。

B.编译。

修改后的程序进入编译器,编译器会把程序翻译成机器指令(也就是目标代码),但是这样的程序还是不能执行的。

C.链接。

链接器把由编译器产生的目标代码和其他所需的代码整合到一起,这些附加代码包括程序中用到的库函数。

这样就产生了完全可执行的程序。

2.main函数中的exit和return

在main函数中,以两者结尾是一样的。

都是终止程序执行,并且向操作系统返回0。

不过exit需要引入stdlib.h库函数。

#include

#include

intmain(void)

{

printf("Helloworld");

exit(0);

//return0;

}

3.%i和%d

在printf中使用时,两者没有区别,但是在scanf中,%d只能接受10进制的整数。

但是%i还可以接受八进制和十六进制的整数。

#include

#include

intmain(void)

{

inti;

scanf_s("%i",&i);

printf("%d",i);

}

4.scanf函数

scanf本质上是一种“模式匹配”函数。

但是在VisualStudio中调用scanf函数时会给出这样的提示:

Thefunctionmaybeunsafe.Pleaseusingscanf_sinstead.

当用户从键盘输入时,程序并没有读取输入,而是把用户的输入放在一个隐藏的缓冲区中,由scanf来读取。

因此如果用户输入了多余的字符,scanf无法彻底完成模式匹配,scanf就会把字符放回缓冲区供后续scanf函数的读取。

1.编译和链接

将程序转化为机器可执行的代码,C语言分为三个步骤:

A.预编译。

程序首先会交给预处理器,预处理器执行以#开头的指令,然后给程序添加指令,或者修改指令。

B.编译。

修改后的程序进入编译器,编译器会把程序翻译成机器指令(也就是目标代码),但是这样的程序还是不能执行的。

C.链接。

链接器把由编译器产生的目标代码和其他所需的代码整合到一起,这些附加代码包括程序中用到的库函数。

这样就产生了完全可执行的程序。

2.main函数中的exit和return

在main函数中,以两者结尾是一样的。

都是终止程序执行,并且向操作系统返回0。

不过exit需要引入stdlib.h库函数。

#include

#include

intmain(void)

{

printf("Helloworld");

exit(0);

//return0;

}

3.%i和%d

在printf中使用时,两者没有区别,但是在scanf中,%d只能接受10进制的整数。

但是%i还可以接受八进制和十六进制的整数。

#include

#include

intmain(void)

{

inti;

scanf_s("%i",&i);

printf("%d",i);

}

4.scanf函数

scanf本质上是一种“模式匹配”函数。

但是在VisualStudio中调用scanf函数时会给出这样的提示:

Thefunctionmaybeunsafe.Pleaseusingscanf_sinstead.

当用户从键盘输入时,程序并没有读取输入,而是把用户的输入放在一个隐藏的缓冲区中,由scanf来读取。

因此如果用户输入了多余的字符,scanf无法彻底完成模式匹配,scanf就会把字符放回缓冲区供后续scanf函数的读取。

1.C语言中的布尔类型

在C语言中,是没有布尔类型的,0就是false,非0就是true。

于是,写习惯了Java/C#的我们自然会很不习惯,这个时候,我们不妨用宏定义来使我们的代码看起来更舒服一些。

1.#define BOOL int 

2.#define TRUE 1 

3.#define FALSE 0 

4. 

5.int main (void) 

6.{ 

7.    BOOL flag=TRUE; 

8.    if(flag) 

9.    { 

10.        printf("true"); 

11.    } 

12.    else 

13.    { 

14.        printf("false"); 

15.    } 

16.} 

 

在C99中,长期缺乏布尔类型的问题得到的解决,但是在目前,C99标准还没有得到很好的推广。

暂且不提。

2.limits.h

习惯了平台无关的我们,在学习C语言时,必须需要注意他的平台相关性。

其中很典型的例子就是在不同的平台下,不同的编译器下,int的取值范围是不同的。

在C#中,我们可以用Int32.MaxValue来获得,那么在C语言中,我们该怎么获得呢?

在C中,提供了一个头文件limits.h,里面有很多宏定义。

那么我们就很容易得到int类型的取值范围。

1.#include  

2.#include  

3.#include  

4. 

5.int main (void) 

6.{ 

7.    printf("int的最小值是:

%d;最大值是:

%d",INT_MIN,INT_MAX); 

8.    return 0; 

9.} 

 

 3.浮点类型

在C语言中,提供了三种浮点类型:

分别为

float:

单精度浮点数,

double:

双精度浮点数,

longdouble:

扩展精度浮点数。

在一般要求不严格的情况下,float就足够了,其次是double,longdouble几乎不会用到。

在C99中,浮点类型包括两种,分别为实浮点类型,就是我们上面提到的。

还有复数类型,分别对应为float_Complex等等。

4.字符类型

 

 

在C语言中,一般采用的ASCII编码,而字符类型又分为有符号型和无符号型。

在C语言标准中,对此并无规定,因此是由不同的编译器自己决定。

因此考虑到可移植性,如果涉及到符号相关,我们不要假设char是有符号还是无符号,而用signed和unsigned来显式标识。

由于在C语言中,字符实际上是被作为整数来处理的,因此在C89中,将字符类型和整数类型统称为整值类型。

C语言读入字符不会跳过空白字符。

我们可以看一个简单的例子:

1.int main (void) 

2.{ 

3.    char ch; 

4.    scanf("%c",&ch); 

5.    printf("%c",ch); 

6.    return 0; 

7.} 

我现在键入一个换行:

他也把换行符读入,然后打印出来。

那么我们如何读入一串字符串呢?

1.int main (void) 

2.{ 

3.    char ch='a'; 

4.    int count=0; 

5.    do 

6.    { 

7.        scanf("%c",&ch); 

8.        count++; 

9.    }while(ch!

='\n'); 

10.    printf("%d",count-1); 

11.} 

 

当然,我们也可以这样来写:

1.int main (void) 

2.{ 

3.    char ch='a'; 

4.    int count=0; 

5.    scanf("%c",&ch); 

6.    while(ch!

='\n') 

7.    { 

8.        count++; 

9.        scanf("%c",&ch); 

10.    } 

11.    printf("%d",count); 

12.} 

 

5.getchar和putchar

在C语言中,为我们提供了专门输入和输出字符的函数,也就是getchar()和putchar().

让我们看下getchar()和putchar()的定义。

_Check_return__CRTIMPint__cdeclgetchar(void);

_Check_return_opt__CRTIMPint__cdeclputchar(_In_int_Ch);

我们可以看到,其实他们返回的都是int类型的值。

OK,让我们看看他们都返回什么。

1.int main (void) 

2.{ 

3.    char c; 

4.    int result; 

5.    c=getchar(); 

6.    result = putchar(c); 

7.    printf("\nc:

%c;result:

%d",c,result); 

8.} 

我们可以看到,他们都是返回其字符的ASCII码。

那么,既然C语言为我们提供了这样专门的函数,一定说明他在读取和输出字符方法比scanf和printf有着特殊的优势。

A.由于getchar和putchar函数实现比较简单,因此较之效率更高。

B.为了额外的效率提升,通常getchar和putchar都是作为宏来实现的。

总之,他们相较之略显重量的scanf和printf效率更高。

另外,我们还可以用getchar来实现读取字符的C语言惯用法。

1.int main (void) 

2.{ 

3.    while(getchar()!

='\n'); 

4.    return 0; 

5.} 

 

这样的函数一直读到换行终止。

另外:

1.int main (void) 

2.{ 

3.    char c; 

4.    while((c=getchar())==' '); 

5.    switch(c) 

6.    { 

7.    case 'a':

 

8.        printf("a"); 

9.        break; 

10.    case 'q':

 

11.        printf("q"); 

12.        break; 

13.    default:

 

14.        printf("others"); 

15.        break; 

16.    } 

17.} 

 

我们也可以这样来实现忽略一切空白,当然也可以修改程序使之忽略其他字符。

另外,我们在前文说过,scanf在无法完成彻底模式匹配时,会把剩余的字符放到缓冲区,供下次读取。

那么我们来看这样一段代码:

1.int main (void) 

2.{ 

3.    int i ; 

4.    char c; 

5.    scanf("%d",&i); 

6.    c=getchar(); 

7.    printf("%c",c); 

8.} 

 

 

由此可知,getchar()也会首先打缓冲区里去读取字符。

1.typedef

在前文中,我们用宏定义来定义了一个BOOL类型,那么现在就用更专业的方式来定义类型。

typedefintBool;

intmain(void)

{

Boolflag=1;

if(flag)

{

printf("True");

}

else

{

printf("false");

}

}

typedef的作用就是类型定义(TypeDefinition)。

类型定义有以下三个优点:

1.易于阅读。

比如我们可以把定义一个Dollar,然后Dollarmoney=1000;这样比用int来得更容易阅读。

2.容易修改,如果有一天Dollar要改成double类型,那么我只需要修改类型定义就可以了。

3.便于移植。

在上文中,我们说过C语言是平台相关的,那么我们不妨定义一些Int32,Int64,这样当我们移植时,我们只需要修改这些类型定义就可以了。

那么类型定义较之宏定义有两个优点:

A.类型定义更为强大,特别是,数组和指针类型是不能定义为宏的。

B.在作用上,宏定义只是简单的字符串替换,而类型定义却带有一定的封装性。

2.sizeof运算符

这个运算符很简单,就不赘述了,只强调一点。

由于sizeof运算符返回的类型时size_t,这是一种自定义的类型,因此,为了防止不同的平台造成的不兼容问题。

我们最好在显示前将其进行一次类型转换。

intmain(void)

{

printf("%d",(int)sizeof(int));

}

1.数组大小

我相信,在C#/Java中,更多的人愿意用List来取代数组,一方面是List提供了较多的方法,另一方面也无需我们去指定数组的大小。

那么在C语言中,我们既然需要必须指定数组的大小,而一般来讲,很多数组大小事我们无法确定并且经常会发生变化的,那么我们最好的方式就是用宏定义来限定数组的大小。

#defineSIZE10

intmain(void)

{

inta[SIZE];

}

 

如果包含多个数组的话,用宏就很难记忆,那么我们就可以利用sizeof运算符。

intmain(void)

{

inta[]={1,3,4,55,6,7,89,9,0};

inti;

printf("%d",(int)sizeof(a)/(int)sizeof(a[0]));

for(i=0;i<(int)sizeof(a)/(int)sizeof(a[0]);i++)

{

a[i]=0;

}

for(i=0;i<(int)sizeof(a)/(int)sizeof(a[0]);i++)

{

printf("%d\n",a[i]);

}

}

注意,我们之前说过,sizeof返回的值是size_t,因此,我们在计算时,最好将其先强制类型转换为我们可以控制的类型。

 

2.数组初始化

一般情况下,我们初始化数组都是把整数数组初始化为0,那么我们一般会怎么做呢?

#defineSIZE5

intmain(void)

{

inta[SIZE]={0,0,0,0,0};

}

那么如过SIZE=100怎么办,那么很多人都会这样去做。

#defineSIZE100

intmain(void)

{

inta[SIZE];

inti;

for(i=0;i

{

a[i]=0;

}

}

其实我们完全不用麻烦,这么一句代码就可以搞定了。

#defineSIZE100

intmain(void)

{

inta[SIZE]={0};

}

在C99中,提供了一种初始化式,使得我们可以这样来写。

#defineSIZE100

intmain(void)

{

inta[SIZE]={[5]=100,[50]=49};

}

而其他的数字就都默认为0。

那么我们来考虑这样一段代码:

#defineSIZE10

intmain(void)

{

inta[SIZE]={1,2,3,4,5,[0]=6,7,8};

}

那么在C99中,这段代码的结果究竟是什么呢?

这个就需要我们来了解一下数组初始化式的原理。

其实,编译器在初始化式数组列表时,都会记录下一个待初始化的元素的位置,比如说在初始化index=0的元素时,会记录下1,这样以此类推,但是当初始化index=5的时候,首先根据他的初始化式记录下一个待初始化的元素时index=1,然后初始化index=0的元素为6。

那么也就是说:

最后的结果应该是{6,7,8,4,5,0,0,0,0,0}。

3.常量数组

当数组加上const就变成了常量数组,常量数组主要有两个好处。

1.告诉使用者,这个数组是不应该被改变的。

2.有助于编译器发现错误。

4.C99的变长数组

这是个很爽的东西,我们再也不必担心为数组指定大小而发愁了,指定大了会造成空间的浪费,指定小了又不够用。

在C99中,他的长度会由程序执行时进行计算。

方式如下:

intmain(void)

{

intsize;

inta[size];

scanf("%d",&size);

}

5.数组的复制

很多时候,我们需要把一个数组的元素复制到另一个数组上,我们大多数人第一个想到的就是循环复制。

#defineSIZE10

intmain(void)

{

inta[SIZE];

intb[SIZE];

inti;

for(i=0;i

{

a[i]=i;

}

for(i=0;i

{

b[i]=a[i];

}

for(i=0;i

{

printf("%d",b[i]);

}

}

其实还有一种更好的方法是使用memcpy方法,这是一个底层函数,它把内存的字节从一个地方复制到另一个地方,效率更高。

#include

#include

#include

#defineSIZE10

intmain(void)

{

inta[SIZE];

intb[SIZE];

inti;

for(i=0;i

{

a[i]=i;

}

memcpy(b,a,sizeof(a));

for(i=0;i

{

printf("%d",b[i]);

1.数组作为函数参数

函数是我们学习程序设计语言最基本的东西了,我在此不再赘述。

只讨论一种特殊情况,就是数组作为函数的参数传递。

我们都知道,其实在传递数组的时候,实际上是传递了数组首元素的指针。

明确了这一点之后,我们就可以思考下面的问题。

既然他只是传递了数组首元素的指针,那么他必然无法知道整个数组的大小,因此,我们如果希望在函数中用到数组的长度,必须要进行显式传递。

intSum(inta[],intsize)

{

inti,sum=0;

for(i=0;i

{

sum+=a[i];

}

returnsum;

}

 

那么既然,函数无法检测传入数组的长度,我们也可以利用这一个特性来计算数组前N个数的和,或者是利用这一特性来告诉函数,实际上,数组的有效长度要小于数组的真实长度。

2.C99中变长数组作为函数参数

首先在数组一节中,我们谈到了C99中的变长数组是个很好的东西。

那么我们来看看变长数组作为函数参数的情况。

我们看之前的代码,size和a[]并没有直接的联系,那么当变长数组作为参数就会解决这样的情况。

intSum(intsize,inta[size])

{

inti,sum=0;

for(i=0;i

{

sum+=a[i];

}

returnsum;

}

 

这个代码,则明确地表示了数组a的长度是size,也就是说在size和a[]之间建立起了直接的联系。

但是在这里我们需要注意一点,就是参数的顺序,长度一定要写在数组之前,否则会出现a[size]找不到size的错误。

在进行函数声明时,我们可以有以下几种方式:

intSum(int,inta[*]);

intSum(intn,inta[n]);

intSum(intn,inta[*]);

intSum(int,inta[]);

intSum(intn,inta[]);

 

个人比较推荐第一种,因为我觉得第一种最为简便,而且可以表明a是一个变长数组。

像第四种和第五种,我个人认为是两种很不好的方式。

3.C99中数组参数声明使用static

C99中允许在数组参数声明中使用关键字static。

例如:

intSum(inta[static10],intn)

{

}

 

从函数本身来讲,static并没有对函数的本身实现造成任何影响。

static10的含义是数组的长度至少是10。

那么当函数调用时,编译器会事先从内存中取出10个数,而不是在函数调用的时候才一次次的去取,这样就可以使函数的效率更高。

4.main函数的返回值

在初学C语言的时候,谭老的书上大部分都是这样的代码:

voidmain()

{

printf("Helloworld");

}

 

但是实际上,这段函数有两个缺陷:

A.从编程风格上来看,最好显式地声明main函数没有参数

B.main函数应该返回状态码,在某些操作系统中,程序终止时可以检测到状态码,来监视程序是否正常结束。

即使你不需要这个状态码,其他人也可能需要。

因此,这个函数最好这样来实现:

intmain(void)

{

printf("Helloworld");

return0;

}

 

还记得我们之前说过exit(0)么,我们之前说,在main函数中写return0和exit(0)是没有区别的。

那么我们就来看看return和exit的区别。

exit属于头文件,我们之前说过,0是状态码中成功的意思,那么为了更直观,C标准库为我们提供了这样的两个宏定义。

intmain(void)

{

printf("Helloworld");

exit(EXIT_SUCCESS);//成é功|

exit(EXIT_FAILURE);//失§败ü

}

 

让我们转向定义可以发现:

/*Definitionoftheargumentvaluesfortheexit()function*/

#defineEXIT_SUCCESS0

#defineEXIT_FAILURE1

 

中的这两个宏定义。

但是这两个值并不是固定的,而是由实现定义的。

另外,return和exit的一个最典型差异就是,在其他函数中调用return不会引起程序的终止,但是无论在哪里调用exit都会引起程序终止,我们看一个程序。

intmain(void)

{

printf("Begin\n");

BreakTest();

printf("

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 初中教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2