c学习笔记(二)

14、数组:C中定义数组必须指定长度,并且长度必须是常量或者符号常量(#define)。不能动态定义长度。如inta=10;int b[10];或用scanf输入一个长度都是不行的。

       一维数组在初始化时可以只初始化部分,其余未指明的默认为0(长度不可省)。当给出全部元素时,可不指定长度,其长度即为元素个数。当定义一个全为0的数组可以inta[10]={0};

       用{}初始化数组,必须是在声明时inta[3]={1,2,3};不能分开:int a[2];a={1,2}有误。

       二维数组定义可以只指定列数,也可只初始化部分。初始化部分对于稀疏矩阵定义提供很大方便。

       关于数组初始化(针对指定部分初始化):数组初始化必是全部初始化(未初始化时内容不可知),int型数组未指定的初始化为0,而char型数组未制定的初始化为’\0’。

15、字符数组与字符串

       (1)定义一维字符数组可以定义整型数组一样,对每个元素逐个定义或者只定义一部分:

       charc[5]={‘c’,’h’,’i’,’n’,’a’};

       chara[10]={ ‘c’,’h’,’i’,’n’,’a’};

       若定义字符数组不初始化其元素值是不可预知的。对于上述a数组,元素个数少于声明的长度,则剩余元素全为’\0’(空字符)。初始化也可以不指定长度,但要写出全部元素。

       字符数组的另一种初始化方式就是用字符串——C中,字符串用字符数组来表示,对字符串所有操作都是通过对字符数组实现的(没有string数据类型)

       char a[]={“china”};等价于charc[]={ ‘c’,’h’,’i’,’n’,’a’,’\0’};后者即为a数组在内存中存储形式。

       对字符数组用”字符串”初始化时系统在数组最后自动添加’\0’字符作为字符串结束标志。所以chara[]={“china”};得到的是长度为6的数组。而对于char c[]={‘c’,’h’,’i’,’n’,’a’};是长度为5的数组。可以对一个长度为10的数组,用字符串的方式初始化长度为5的字符串,剩余的为’\0’。这个数组长度为10,但有效字符长度为5。

       字符数组并不要求它的最后一个字符是’\0’,甚至可以不包含’\0’。至于是否需要加’\0’,取决于需求。

       (2)%c对字符数组逐个输入或输出;%s对字符串一次输入或输出。

对于数组char a[]={'c','h','i','n','a','\0','!'};采用%c和%s输出:

       for(;i<7;i++)

              printf("%c",a[i]);//输出china!

       printf("\n%s",a);//输出china,遇到'\0'即停止输出

       puts(a);//输出china,遇到'\0'即停止输出

       用scanf(“%s”,c);输入一个字符串时,c不需要取地址符号。因为c是数组,数组名即数组的首地址!不需要&取地址,但加上&也不会出错。(c预先定义长度)

若scanf(“%s%s%s”,a,b,c);输入三个字符串,字符串之间以空格作为间隔。故对单独一个字符数组出入带空格的字符串如”helloworld”,只接收hello。因为遇到空格即结束了。此时可以用gets(数组名)来输入带空格的字符串!

对于一个不带’\0’的数组,用scanf或puts输出时会出现乱码。因为没有结束符

(3)字符串处理函数

①puts(字符数组)

②gets(字符数组)

③strcat(数组1,数组2)(<string.h>)字符串连接函数:将str2连接到str1数组中。去除str1最后的’\0’字符,将str2放在str1中。主要str1要足够大。

④strcpy(arr1,str2)和strncpy(arr1,tr2,n)字符串复制函数:将str2的内容复制到arr11中,会覆盖arr11开头的内容。若arr1长度大于str2则剩余字符为原arr11内容。strncpy是将str2前n个字符复制到arr11中。C不支持str1=str2,因为str1和str2都是数组的首地址,而Java中数组是引用类型,s1=s2是让s2引用指向s1的地址。

strcpy中arr11必须是数组名,而str2可以是数组名,也可以是字符串常量。

⑤strcmp(str1,str2)字符串比较函数:通过对应字符的ASCII码比较,知道遇到’\0’。返回0、1或-1。

⑥strlen(str1)字符串长度函数,返回字符数组实际长度(不包括’\0’):

char a[10]=”china”;strlen(a);

返回不是10,也不是6,而是5。

⑦strlwr(arr)和strupr(arr)字符串大小写转换。如果函数中用的字符串常量则可能出现地址不能为written的错误。

⑧strstr(str1,str2)字符串匹配函数:在str1中找到str2第一次出现的位置。

注:以上函数的参数都是char型指针,可以是数组或字符串,在程序中使用字符串常量会生成一个指向字符的常量指针,当一个字符串常量出现于一个表达式中时,表达式所使用的值就是这些字符所存储的地址,而不是字符本身。因此,可以把字符串常量复制给一个指向字符的指针,后者指向这些字符所存储的地址。返回值可能是指针或整数。

16、函数在定义时不指定类型,系统会默认是为int型。函数定义中的返回值与return的值类型不同并不会编译错误,而是将return的值转型为定义类型后返回。如定义的函数返回类型为int,return的是float,则调用函数返回的值类型为int。

       函数调用实参根据编译系统的不同可能会从左至右或从右至左的顺序对实参求值,而一个实参求值过程可能会对另一个实参产生影响。

       函数声明:编译系统从上至下进行,所以被调函数定义在主调函数之后时,要在主调函数中声明(这个声明被称为“函数原型”),当编译系统遇到函数调用时会检查函数调用的合法性(参数个数、类型是否匹配)。函数原型可以不写形参名,而只写参数类型。若被调函数定义在主调函数之前,则不需要在主调函数中声明。

       可以在文件开头(在#include下,函数外面)统一对被调函数声明。则在各主调函数中调用不必声明。(某些函数会出现在多个其他函数中被调用)

       C函数不能直接返回数组,只能通过返回指针的方式实现返回数组

17、参数传递与sizeof()

       ①基本数据类型作为参数时,按值传递。即复制一个与实参形同的数据给形参,形参与实参相互独立,只是数值相等。

       数组作为参数时,按址传递。即传递的是实参数组的首地址,就也是第一个元素的地址。所以实参和形参是共用一个地址的,形参的变化会引起实参的变化。

       由于②的特性。在使用sizeof(a)求数组长度时需要注意:

       sizeof获得的是对象占用内存的字节数。

       当a作为实参时,sizeof(a)获得a数组所占的字节数,sizeof(a)/sizeof(a[0])即可求得数组a的元素个数;

       当a是作为函数的形参(比如实参是b),由于②,此时a只是b数组的首元素,所以sizeof(a)得到的是b[0]元素的字节数!sizeof(a)/sizeof(a[0])不能求得a数组的长度。故要在函数的参数列表中加入一个数组长度参数,在调用该函数时可用sizeof(b)/sizeof(b[0])作为实参传入。

       ③二维数组作为形参时,必须指定数组的列数!二维数组在内存是以多个一维数组存放,必须指定这些一维数组的长度。而行数不用指定(指定行数并不起作用),更不能只指定行数。因为实参和形参是类型相同、长度相同的若干行一维数组,有几行并不是系统关心的。所以实参和形参的行数可以不同,列数必须相同。

       function(int  array[][5]);function(int array[3][5]);

       二维数组传入函数时,也是传入数组的首元素地址。对于二维数组a[2][2]aa[0]a[0][0]三者是相等的!

18、局部变量和全局变量

       定义在函数中的变量称为局部变量。或在函数的复合语句中({}里的语句块)。

       定义在函数外的变量是全局变量。整个源程序文件可能有#开头的预处理、变量声明、函数定义(声明)。函数定义不算执行语句,所以可以在一个函数的定义后面声明变量。全局变量的作用范围是定义变量的位置到源文件结束

       全局变量在函数之间充当了桥梁,在一个函数中改变会影响在另一个函数中的使用。一个函数只能返回一个值,对全局变量的修改可是实现返回多个值。但这个也增加了函数间的耦合性,不利于程序的移植。

       局部变量与外部变量同名时,会覆盖全局变量,在局部变量的作用范围内,全局变量失效。

19、变量存储类别

       全局变量和局部变量的划分是从作用域角度出发。从变量的生存期(存储方式)出发,可以分为静态存储方式和动态存储方式。

       内存中供用户使用存储空间有程序区、静态存储区、动态存储区。

存储类别

定义

数据类型

作用时间

 

 

静态存储方式

程序运行时系统分配固定的存储空间

全局变量存放在静态存储区

从程序开始执行到结束释放,占据固定存储单元

 

 

 

动态存储方式

程序运行时根据需要动态分配存储空间

函数形参:在调用函数时给形参分配存储空间;

自动变量;

函数调用时的现场保护和返回地址等;

 

在函数调用时动态分配存储空间,函数结束即释放空间

       C中每一个变量和函数都有两个属性:数据类型和数据的存储类型。数据类型即intchar等,存储类型即静态存储和动态存储。有分为autostaticregisterextern 4种。

       1auto变量

       在函数中不专门声明为static的变量都是auto变量,即平时省略了auto关键字的局部变量。这种变量动态地分配存储空间,数据存储在动态存储区。函数中的形参和变量都属此类。

       autoint a,b;等价于inta,b;

       2static声明局部变量

       在函数中用static声明的局部变量在函数调用结束后并不会释放,在下一次调用该函数时,该static变量保持上一次调用的结果

       ②静态局部变量存放在静态储存区。在整个程序运行期间都不释放。旨在编译时赋一次初值,之后沿用上一次的结果。而自动变量的赋值不是在编译时进行,而在调用函数时进行。

       ③静态局部变量在定义时不指定初值会在编译时自定赋值(0或空字符)。而对于auto变量来说不赋初值是一个不确定的值。

       ④虽然静态局部变量在函数结束后不释放,但其他函数并不能引用它。

       staticint f=1;

在需要动态规划等需要保存上一状态的算法中可以使用静态局部变量。

初始化一个变量后只引用不改变其值,使用static局部变量比较方便,免去了频繁赋值。

3register变量

register变量是不同于存放在内存中的一般变量,它是存放在处理器的寄存器中的,这样可以减少读取时间。对于一个频繁使用(在一个长的循环中)的变量可以提高效率。(存放在内存中的变量在调入CPU使用一次后又会放回内存,下次使用再调入,耗时)。寄存器变量视为动态存储。

registerlong f=1;

只有局部自动变量和形参可以作为寄存器变量,在调用函数时会占用一些寄存器,函数结束后释放寄存器。静态局部变量不能作为register,因为不能即放在内存的静态存储区又放在寄存器中。(并不常用,一些优化的编译器会自动将频繁使用的变量放在寄存器中,而不需要指定register

4extern外部变量

外部变量是函数外边定义的全局变量。编译时将外部变量分配在静态储存区。

extern关键字是用于声明一个全局变量,使其作用域得到扩展。

①一个源文件内的外部变量作用域扩展

一个全局变量定义在一个函数的后面,则在该函数中是不能引用该全局变量的。但是如同调用函数一样,在调用函数中声明被调函数则可调用定义在调用函数后面的函数,一个全局变量也可以通过用extern声明的方式在其前面定义的函数中被引用。如

function(){

       void fff();

extern A,B;//外部变量声明,类似对函数的声明。声明时可以省略变量类型。

printf(“%d”,A+B);

}

intA=1,B=2;

voidfff(){

       ……

}

②不同源文件中引用同一个变量

在一个C程序的两个文件中,不能定义相同名称的全局变量,否则会出现“重复定义”的错误。而如果一个文件要用到另一个文件中已定义的全局变量,就可以用extern声明并引用。此时将一个变量的作用域扩展到了另一个文件。

extern全局变量被声明的多个文件共享,每个文件程序的执行都可能影响这个extern的值。

5static声明的全局变量

当一个文件中的变量不想被其他文件引用,可以用static声明外部变量,即静态外部变量。这种变量适用于模块化的程序,可以使独立文件之间的变量名互不干扰——在两个文件中,不用static声明的外部变量不能重名,而都用static声明的变量可以同名。作用域为本文件。

综上,静态变量(静态局部和静态外部)和外部变量(全局变量)是在编译时分配空间的,分配在静态存储区。extern只用于变量的声明,而非定义。autostaticregister是在定义变量时“修饰”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值