C语言学习之指针、数组和函数那点事儿
函数、数组和指针
书中示例:编写一个处理数组的函数,要求该函数能够返回数组中所有元素之和。
total=sum(marbles);
这里的marbles为数组名,表示首元素的地址
注意:
除了以下两种情况数组名表示的不是首元素地址,其余都是:
1、&数组名 数组名不是首元素的地址,表示整个数组。&数组名:取出的是整个数组的地址
2、sizeof(数组名) 数组名表示整个数组 sizeof(数组名)
所以既然能用数组名表示地址,那么也能用数组名表示指针。
上代码:
#include <stdio.h>
#define SIZE 10
int sum(int* ar, int n);
int main(void)
{
int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26, 31, 20 };
long answer;
answer = sum(marbles, SIZE);
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes.\n",
sizeof marbles);
return 0;
}
int sum(int* ar, int n)
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += *(ar+i);
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
另一类写法
#include <stdio.h>
#define SIZE 10
int sum(int ar[], int n);
int main(void)
{
int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26, 31, 20 };
long answer;
answer = sum(marbles, SIZE);
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes.\n", sizeof marbles);
return 0;
}
int sum(int ar[], int n)
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
显然,运行后效果是一样的,所以int* ar能表示为int ar[]
运行结果显示,ar的大小为8字节,即在该系统中用于存储地址的内存大小为8字节
两个重要的符号:
strien是求字符串长度的,只针对字符串长度,是一种库函数,使用时必须引用头文件
sizeof计算变量、数组类型的,大小单位是字节,是一种操作符
使用指针形参
函数处理数组时必须知道何时开始,何时结束
第一种表达方式是用一个指针形参标识数组的开始,用一个整数形参表明待处理的元素个数
int sum(int* ar, int n);
answer = sum(marbles, SIZE);
第二种表达方式是分别使用一个指针形参标识数组的开始,一个指针形参指明数组的结束处
int sump(int * start, int * end);
answer = sump(marbles, marbles + SIZE);//结束处指向数组最后一个元素的后面
所以上例也可以写成:
#include <stdio.h>
#define SIZE 10
int sump(int * start, int * end);
int main(void)
{
int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26, 31, 20 };
long answer;
answer = sump(marbles, marbles + SIZE);
printf("The total number of marbles is %ld.\n", answer);
return 0;
}
int sump(int * start, int * end)
{
int total = 0;
while (start < end)
{
total += *start; // 使用*解引用,再把数组元素的值加起来
start++; // 让指针指向下一个元素
}
return total;
}
由于一元运算符*和++的优先级与++的优先级相同,所以根据结合律从右往左的规则,所以也可以写成:
total += *star++;//先把指针指向位置上的值加到total上,然后再递增指针
后缀的位置影响十分大,上代码:
#include <stdio.h>
int data[2] = { 100, 200 };
int moredata[2] = { 300, 400 };
int main(void)
{
int * p1, *p2, *p3;
p1 = p2 = 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);
return 0;
}
运行结果是;
*p1 = 100, *p2 = 100, *p3 = 300
*p1++ = 100, *++p2 = 200, (*p3)++ = 300
*p1 = 200, *p2 = 200, *p3 = 301
总结出其中规律就是:
*p++:先使用指针指向位置上的值,然后再递增指针
*++p:先递增指针,再使用递增指针指向位置上的值
(*p)++:先使用指针指向位置的值,再将该值递增
指针表示法与数组表示法
由以上例子分析可以看出,处理数组的函数实际使用的是指针作为实参,而函数的编写可以选择使用两种表示法。只不过使用数组表示法的意图会更加明显
在C语言中,ar[i]与(*ar+1)是等价的,只不过当ar为指针变量时,才使用ar++这样的表达式
指针表示法(尤其是与递增运算符一起使用时)更加接近机器语言,因此一些编译器在编译时能生成效率更高的代码
指针操作
八种操作:
#include <stdio.h>
int main(void)
{
int urn[5] = { 100, 200, 300, 400, 500 };
int * ptr1, *ptr2, *ptr3;
ptr1 = urn; // 把一个地址赋给指针
ptr2 = &urn[2]; // 把一个地址赋给指针
// 解引用指针,以及获得指针的地址
printf("pointer value, dereferenced pointer, pointer address:\n");
printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
// 指针加法
ptr3 = ptr1 + 4;
printf("\nadding an int to a pointer:\n");
printf("ptr1 + 4 = %p, *(ptr1 + 4) = %d\n", ptr1 + 4, *(ptr1 + 4));
ptr1++; // 递增指针
printf("\nvalues after ptr1++:\n");
printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
ptr2--; // 递减指针
printf("\nvalues after --ptr2:\n");
printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p\n", ptr2, *ptr2, &ptr2);
--ptr1; // 恢复为初始值
++ptr2; // 恢复为初始值
printf("\nPointers reset to original values:\n");
printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2);
// 一个指针减去另一个指针
printf("\nsubtracting one pointer from another:\n");
printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %td\n", ptr2, ptr1, ptr2 - ptr1);
// 一个指针减去一个整数
printf("\nsubtracting an int from a pointer:\n");
printf("ptr3 = %p, ptr3 - 2 = %p\n", ptr3, ptr3 - 2);
return 0;
}
指针函数
指针函数:返回指针类型的函数
char*getWork(char C){
switch(c){
case'A':return "Apple";
case'B':return "Banana";
case'C':return "Cat";
case'D':return "Dog";
default:return "None";
}
}
int main(void){
char input;
printf("please input a charactar:");
input=getchar();
printf("%c\n", input);
printf("%s\n", getWord(input));
getchar();
return 0;
}
另一种十分类似的写法(但这种写法是不对的):
char*getWork(char C){
{
char str1[]="Apple";
char str2[]="Banana";
char str3[]="Cat";
char str4[]="Dog";
char str5[]="None";
switch(c){
case'A':return str1;
case'B':return str2;
case'C':return str3;
case'D':return str4;
default:return str5;
}
}
int main(void){
char input;
printf("please input a charactar:");
input=getchar();
printf("%c\n", input);
printf("%s\n", getWord(input));
getchar();
return 0;
}
不要返回局部变量的指针(数组)
在该例中,str1是局部变量,这个字符数组在子程序结束后,它对应的存储空间会立即释放
函数指针
函数指针:指向函数起始地址的指针
函数指针定义:
函数返回类型名(指针变量名)(函数形参类型列表)
如:int(fptr)(int,int);
指针函数的使用:
1、指针变量名=被指向函数名;
如:fptr=maxx;
2、函数调用:指针变量名(函数调用时的实际参数);
如:int x=fptr(5,8);
看不懂?上代码:
#include<stdio.h>
int square(int num){
return num*num;
}
int main(void){
int num;
int(*fp)(int);
printf("please input a number:");
scanf_s("%d", &num);
fp=square;//建立联系
printf("fp=0x%x, %d\n",fp,(*fp)(num));//调用*fp指向的函数并传入num这一实参
return 0;
}
函数名等于函数的地址。通过fp=square;语句使得fp指向函数的入口,因此通过fp指针就能找到函数squar的代码存放的位置,从而调用并执行这段代码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了