c语言笔记.docx
- 文档编号:11942767
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:28
- 大小:315.26KB
c语言笔记.docx
《c语言笔记.docx》由会员分享,可在线阅读,更多相关《c语言笔记.docx(28页珍藏版)》请在冰豆网上搜索。
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
那么在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属于 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("
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 笔记