C语言函数,数组与指针

函数,数组和指针

接着上一次的总结,现在写一个函数,该函数的功能是返回数组中所有元素的和。调用函数应该是什么样子?
可能是这样的:
total=sum(marbles);

那么该函数的原型是什么?数组名是该数组首元素的地址,所以实际参数marbles是一个存储int类型的地址,应该把它赋予一个指针形式的参数。该形参是一个指向int类型的指针。如下:

int sum(int *ar);//对应的函数原型

sum()获得了该数组首元素的地址。但是,该参数并未包含数组元素个数的信息,我们有两种方式让函数获得这一信息。

第一,在函数代码中直接写上固定数组的大小。

int sum(int *ar)
{
    int i;
    int total=0;
    for(i=0;i<10;i++) //假设数组有10个元素
    	total+=ar[i];    //ar[i]与*(ar+i)
    return total;
}

该函数上面的定义,限制了智能计算10个int类型的元素。
第二种方法:把数组大小作为第二个参数。这是非常常用的方法,也非常常见

int sum(int *ar,int n)  //更常用的方法
{
   int i;
   int total;
   for (i=0;i<n;i++)
    	total+=ar[i];
    return total;
}

上述写法,第一个参数告诉函数该数组的地址和数据类型,第二个形参告诉函数该数组中元素的个数

关于函数的形参,还有一点要关注。只有在函数原型或函数定义开头中,才可以用int ar[ ]替代int *ar;这两种形式都表示ar是一个指向int类型的指针但是,int ar[ ]只能用于声明形式参数。第二种形式(int ar[ ])表示了指针ar指向的不仅仅是一个int类型的值,还是一个int类型数组的元素。

关于声明数组形参:

因为数组名是该元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,int ar[ ]和inr *ar才能解释成一样的

所以以下4种原型是等价的:
int sum(int *ar,int n);
int sum(int *,int );
int sum(int ar[ ],int n);
int sum(int [ ],int );

指针形参

函数要处理数组必须要知道开始和结尾。sum()函数使用一个指针形参标记数组的开始。用一个整数形参表明待处理数组的元素个数。除此之外,还有一种方法:传递两个指针。
一个指针指明开始。一个指明结束处。例如以下实现:

#include<stdio.h>
#define SIZE 10
int sump(int *start,int *end);
int main(void)
{
	int marbles[SIZE]={10,20,5,39,4,16,11,19,26,30,15};
	long answer;
	
	answer=sump(marbles,marbles+SIZE);
	
	printf("%ld\n",answer);
	return 0;
}
int sump(int *start,int *end)
{
	int total=0;
	while(start < end)
	{
	 	total+=*start;
	 	start++;
	}
	return total;
}	

赋值表达式: total+=*start 把首元素10加给total。然后表达式start++递增指针变量start,使其指向数组的下一元素。start位指向int的指针,递增1相当于其值递增int类型的大小。
另外,循环体可以进一步简写为:
total+=*start++
这里就又涉及到指针运算中的优先级

一元运算符星号和++优先级相同,但结合律是从右至左,所以start++先求值。然后才是星号start。即为:指针先递增后指向。也就是先把指针指向的值加到total上,再递增指针。若反过来,就为先递增指针,再使用指针指向的值。

如果是(*start)++则是先使用start指向的值,在递增该值,而不是递增指针。指针将一直指向同一个位置,但是该位置上的值发生了变化。

再附上一个栗子,帮助更好的理解运算优先级。

#include<stdio.h>
int data[2]={100,200};
int moredata[2]={300,400};
int main(void)
{
	int  *p1,*p2,*p3;
	p1=p1=data;
	p3=moredata;
	
	printf("*p1=%d,*p2=%d,*p3=%d\n",*p1,*p2,*p3);
	printf("*p1++=%d,*++p2=%d,(*p3)++=%d\n",*p1++,*++p2,(*p3)++);
	printf("*p1=%d,*p2=%d,*p3=%d\n",*p1,*p2,*p3);
	
}	

运行结果:

*p1=100, *p2=100 ,*p3=300
*p1++=100, *++p2=100, (*p3)++=300
*p1=200, *p2=200, *p3=301      

以上除了*p3改变了数组元素的值,再首地址上加1,其余均为指针移动。

指针的基本操作

以下为整理的指针变量的8个基本操作

  • 赋值
    可以把地址赋给指针,例如,用数组名,带地址运算符,(&)的变量名,另一个指针赋值。但地址类型也要和指针兼容,至少避免不明智的类型转换。
  • 解引用
    *运算符给出指针指向地址上存储的值。
  • 取地址
    指针变量也有自己的地址和值,对指针而言,&运算符给出指针本身的地址。
  • 指针与整数相加
    可以使用+运算符把指针与整数相加/整数与指针相加(先后顺序不同),无论是前者还是后者,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初地址相加。若相加结果超出了初始指针指向的数组范围,计算结果未定义。
  • 递增/递减指针
    递增指向数组元素的指针可以让该指针移动至数组的下一个元素。递减同理。
  • 指针减去一个整数
    可以用-运算符,从一个指针中减去一个整数。指针必须是第一个运算对象,证书是第二个运算对象。该整数将乘以指针指向类型的大小,然后用初始地址减去乘积。
  • 指针求差
    计算两个指针的差值,通常求差的两个指针分别指向同意数组的不同元素。通过计算求出两元素之间的距离。差值单位与数组类型的单位相同。
  • 比较两个指针
    使用关系运算符比较两个指针的值,前提是两个指针指向相同类型的对象。

最后在强调一点,不要使用未初始化的指针!!
错误示例:

int *pt;//未初始化
*pt=5;   //严重错误

上面的是很严重的错误。由于创建一个指针时,系统之分配了储存指针本身的内存,并未分配储存数据的内存,所以在使用前,必须用已分配的地址初始化它
这两种方法可解决未初始化的问题:
1.可用一个现有变量的地址初始化指针。
2.或者使用malloc()函数分配内存。

题外话:本想着一次性把和指针有关的东西一次写完,又觉得写在一个博客里把有的重点堆在一起,日后复习看起来感觉很乱。就单独把指针变量的基本操作与指针形参单独罗列出来记录。重拾C并且进一步学习C是为了给接下来自学数据结构与算法打一个厚实的基础,不得不说C primer plus真的是很好的一本书,知识真的很详细,不过有的还是没有讲到,不过都等着我发现了,进一步挖掘最底层的东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值