数组&指针&函数(转自C语言习题与解析 第3版 李春葆编著)

1、要点归纳

数组是相同类型的元素的组合。任何数组在使用前必须先声明。一旦声明后,系统就会为它在内存中分配一个所申请大小的存储空间。

如果定义数组时,前面加上static(说明为静态数据)关键词,则系统为所有数组元素赋值0,否则,数组元素取值不定。

对二维数组的初始化两个维的长度不能同时为0。另外允许省略第一维的长度,但不允许省略第二维的长度。

c语言中没有居中输出,有左对齐和右对齐输出。%8d是右对齐,%-8d是左对齐,数字8代表输出宽度。

2、字符数组和字符串数组

字符数组的结尾是ASCII为0的字符,即空字符,表示成‘\0’。字符数组的初始化:char str[5]={‘A’,’B’,’C’,’D’,’\0’};char str[5]={"ABCD"};也可以用这种方法:char str[]="ABCD";

字符串数组的每一个元素都是一个字符串,字符串数组是二维数组。例如:char str[2][10];说明str是一个具有两个字符串元素的一维数组。每个字符串的长度为10个字符,包括字符串结束符。一维字符串数组元素的使用,使用的是第一个下标元素,例如:static char name[2][8]={"mary","smith"};结果为name[0]="mary",name[1]="smith"。

3、字符串处理函数

puts(字符数组):向终端输出一个字符串;

gets(字符数组):从终端输入一个字符串到字符数组中,并返回一个函数值,该函数值为字符数组的起始地址;

strcat(字符数组1,字符数组2):用于连接两个字符串;

strcpy(字符数组1,字符数组2):用于将字符数组2拷贝到字符数组1中去。(字符数组2中的全部内容拷贝到字符数组1)

注意:不能将一个字符串常量或字符数组直接赋值给字符数组,而应该勇悍strcpy。

strcmp(字符串1,字符串2):比较两个字符串;

strlen(字符串):返回字符串的实际长度,不包括‘\0’在内;注意和sizeof()区别;

strlwr(字符串):将字符串中大写字母转换为小写字母;

strupr(字符串):将字符串中小写字母转换为大写字母。

4、指针运算

所谓指针运算实际上是地址的运算。

指针与整数的加减运算:p+n;表示p+n*sizeof(type);

指针相减:在c语言中,两个地址量相减,并非两个地址值之间直接做减法运算,而是两个指针所指地址之间的数据个数。

指针的关系运算:指针可以和0之间进行等于或不等于的关系运算,即p==0或p!=0,用来判定指针p是否是一个空指针。所谓空指针是指没有为其分配所指的地址空间,所以*p=1是错误的。当为指针声明时即char *p此时p已经指向了具体的存储单元,但该单元中没有给确定的值。

5、指针和数组

在C语言中,指针和数组之间的关系十分密切。他们都可以用来处理内存中连续存放的一系列数据。他们在访问内存时采用统一的地址计算方法。

访问数组元素的一般形式:数组名[下标];进一步得到访问数据元素的一般形式:地址量[整数n];访问数据表达式a[i]的运算过程是,首先计算a+i得到第i个数据的地址,然后访问该地址中的数据。与*(a+i)等价。

数组名是一个地址常量,不能对其做任何运行。

int a[10],*pa=a;a[i],*(a+i),pa[i],*(pa+i)四种表示法全部等价。另外pa是变量,a是常量。

对于字符数组只能对各个元素赋值,不能直接对字符数组进行整体赋值,char a[10];a="good bye!"是错误的。

而对于字符指针,既可以用字符串常量进行初始化,又可以直接用字符串常量赋值。char *str;str="good bye!"是正确的。

这里的初始化和赋值有区别。

数组元素中的[]是一个变址运算符,相当于*(+),如b[j]相当于*(b+j)。

声明一个指针时,并没有分配它所指向的存储空间。而数组声明后,就分配了存储空间。所以,声明一个指针后要先分配存储空间,再向存储空间中写数据。

声明、定义、初始化?

char a[]="1234";char *a="1234";前者sizeof(a)=5,后者等于4;

6、函数

C语言中函数分为库函数和自定义函数,库函数可以直接使用而不必再定义。

void类型的函数无返回值,不能包含return语句。默认的类型为int。

对于编译单位(可以单独编译的源文件)来讲,函数可以分为内部函数和外部函数:

extern函数:叫外部函数,extern可以省略,一般的函数都默认说明为extern,其基本特征是,该函数可以被其它编译单位的函数调用。

static函数:内部函数,其基本特征是,只限于本编译单位内的其它函数调用,而不允许其它编译单位的函数调用它。

函数参数的计算顺序是从右向左进行的。

形参只能是变量,实参可以是常量、变量或表达式。

若只在主函数中说明函数f,则只能在主函数中正确调用函数f。

若在主函数前说明函数f,则在主函数及其后的其它函数中都能正确调用函数f。

%3.0f:输出一个浮点数,总共3位,小数为0位。

 

从作用域的角度看,C语言的变量分为局部变量和全局变量:

局部变量:在函数内或符合语句内定义的变量成为局部变量,或内部变量。作用范围是:

全局变量:在函数外定义的变量称为全局变量,或外部变量。作用范围:从定义的位置开始到整个源文件的结束。

变量的存储类别,即变量在内存中的存储方法,即明确了变量在内存中的存储位置,从而明白了变量的作用域和生存期。在内存中用户使用的存储空间有程序代码区、静态存储区和动态存储区(堆栈)三部分组成。数据主要存放在静态存储区和动态存储区:动态存储区用来存放函数调用时所需保存的函数信息、自动类型的局部变量等;静态存储区主要用来存放全局变量及静态类型的局部变量。

 

局部变量的存储类别:auto局部变量也称auto变量也称自动变量,当在函数内部或复合语句内部定义局部变量时,没有指定存储类型或使用了auto说明符,则定义的变量为自动变量,自动变量的存储单元被分配在动态存储区。这类局部变量的作用域是从定义位置起,到函数体或复合语句的结束。这种变量必须赋初值。不同函数中使用了同名变量也不会影响。

register变量:register变量也叫寄存器变量,寄存器变量也属于自动变量。与一般的auto变量的区别是用register说明的变量存储在CPU的寄存器中。

static局部变量:在函数体或复合语句内部用static来说明一个变量时,该变量成为静态局部变量。静态局部变量的作用域与auto、register变量一样,但有两点本质上的区别:

(1)在程序运行期间,静态局部变量在静态存储区间占据着永久性的存储单元。即使退出函数后,下次再进入该函数时,静态局部变量仍使用原来的存储单元。由于不释放存储单元,所以该存储单元中的值得以保留,可以继续使用存储单元中的原来的值。由此可见,静态局部变量的生存期一直延长到程序运行结束。

(2)静态局部变量的初值是在编译时赋给的,在执行期间不再赋给初值。对未赋初值的静态局部变量,C编译系统自动赋初值为0。

全局变量的存储类别:全局变量有以下几种存储类别。

static全局变量:用static声明的全局变量成为静态全局变量,静态全局变量仅限于本编译单位使用,不被其它编译单位所使用。

extern全局变量:在全局变量之前加上extern的作用有两种:

(1)在同一编译单位内用extern说明符来扩展全局变量的作用域:全局变量定义了以后,当引用它的函数在前面时,应该在引用它的函数中用extern对此全局变量进行说明。以便通知编译程序:该变量是一个在外部定义了的全局变量,已经分配了存储单元,就不需要再为它另外开辟存储单元。

(2)在不同编译单位内用extern说明符来扩展全局变量的作用域:每个C程序总是由许多函数组成,这些函数可以分别存放在不同的源文件中,每个源文件可以单独编译。这些可以单独编译的源文件称为“编译单位”。每一个程序由多个编译单位组成,并且在每个文件中都需要引用同一个全局变量时,为了防止变量名重新定义,应在其中一个文件中定义所有的全局变量,其它用到这些全局变量的文件可以用extern对这些变量进行说明,表示这些变量已在其它便以单位中定义,以通知编译系统不必再为他们开辟存储单元。

另外外部变量的隐含类别是extern。

最终目的是确定变量的作用域!

7、函数的数据传递

形参为局部变量,它分配有存储空间。在调用函数时,C语言将实参传递的值存储到自己的存储空间中。参数传递为单向的值传递,但值又分为普通数据和地址值两种。

函数的数据传递方式:

(1)数据复制方式传递数据

即传递普通数据,把数据本身作为实参传递给形参,被调用函数运行完后,并不将形参的结果返回给实参。实参和形参占用不同的存储空间。所以对形参的操作不会影响到实参。

(2)地址传递方式传递数据

即传递地址数据,不是传递的数据本身,而是存储该数据的地址。这样的话,对形参的操作会影响实参。应为数据在调用函数中和被调用函数中使用的是同一存储空间。

(3)return传递数据

从被调用函数传递数据给调用函数,一般采用函数的返回值来实现。返回值数据可以使普通数据,也可以是地址值。

(4)全局变量传递数据

在函数外部定义的变量是全局变量。它在所有函数中都可见,因此可以利用这个特性在函数间传递数据。

数组在函数间的传递:

(1)一维数组作为函数参数(这也是一种地址传递方式)

当一维数组名作函数实参时,形参除了应该是指针外,还有另外两种形式。例如:int a[M];fun(a);则:fun(int *pa)或fun(int a[])或fun(int a[M])。但编译器都将后两种形式处理成第一种形式。

(2)二维数组作为函数参数

当二维数组作为函数实参时,对应的形参必须是一个数组指针变量。例如a[M][N],fun(a)则:fun(int (*pa)[N])或fun(a[][N])或fun(a[M][N])。数组指针int (*a)[4];指针数组int *a[4];

 

实参和形参若类型不一致,编译时将报错。正确答案是编译时不报错,执行时报错。

声明一个指针时并没有给他分配存储空间,应先用malloc函数分配存储空间,再赋值。

从字符‘3’转换到数字3的方法是:‘3’-‘0’。

8、指针型函数

函数返回值的数据类型决定了该函数的数据类型。例如返回值为数值类型时为数值型函数;返回值为字符型时为字符型函数;返回值为某种类型的数据的数据的地址时为指针型函数,例如int *fun(int a,int b)。

malloc函数分配的存储空间多少对程序的影响有多大?

9、指向函数的指针

指针除了可以保存数据的存储地址外,还可以保存函数的存储首地址(函数的执行入口地址)。指针变量指向了保存函数的入口地址时,他就指向了该函数,所有称这种指针为指向函数的指针。例如:int (*func)( )。另外上式中还说明函数的返回数据是int型数据。

函数指针和变量指针的不同:变量指针指向的是内存的数据存储区;而函数指针指向的是内存的程序代码存储区。因此*的运算分别是访问数据和执行程序。

函数指针的作用主要是:在函数间传递执行的函数,这种传递不是传递任何数据,而是传递函数的执行地址。此时实参是函数名,形参应是函数指针。

return ((*func)(x,y)):先算出表达式的值,表达式的值时执行函数fun的返回值。分步来理解。

float (*c(void))[6]:c是一个无参指针函数,返回值为指向有6个单精度浮点型元素的数组的指针;比较float (*c)[6];

double (*d[6])(void):d是一个含有六个元素的指针数组,其数组元素是指向返回值为双精度浮点型的无参函数。

 

如何通过函数指针执行函数:

void fun(int *p,int *q)

{

}

main()

{

int x,y;

void (*b)();

b=fun;

(*b)(&x,&y);

}

//b=fun;(*b)(&x,&y);这两句等价于执行fun(&x,&y);

10、递归函数

递归函数又称自调用函数,其特点是在函数内部直接或间接的自己调用自己。

一般地一个递归模型是由递归出口和递归体两部分组成,前者确定递归到何时为止,后者确定递归的方式。例如:f(1)=1(递归出口);f(n)=n*f(n-1) n>1(递归体);

进行递归设计时,要先给出递归模型,然后再转为对应的C语言函数。递归模型设计的步骤如下:

(1)对原问题f(s)进行分析,假设出合理的"较小问题"f(s');

(2)假设f(s')是可解的,在此基础上确定f(s)的解,即给出f(s)与f(s')之间的关系;

(3)确定一个特殊情况(如f(1)或f(0))的解,以此作为递归出口。

11、命令行参数

在函数执行时,通过命令行把参数传递给程序,并控制程序的执行。

main可带两个名为argc和argv的参数,以便建立于操作系统之间的联系。

你可能感兴趣的:(C语言,元素,p,的)