C语言_数组、指针和函数

C语言里的函数指针、指针数组和函数指针数组 1.函数指针 函数指针也是一个指针变量,但这个指针存放的是一个函数的地址,以前的函数通过函数名来调用,现在也可以通过指针来调用,运用类比法理解,例如以前的**int p是一个整型变量,是一个指向一个整型数的指针 定义方式: 无参数的:void printwelcome(){} 指针方式:void (*p1)(); p1 = printwelcome;//把函数的名即地址赋给指针** 有参数的:int add(int a,int b){ } 指针方式:int (p2)(int a,int b){ } p2 阅读详情

数组、指针和函数

0、前言

之前大一的时候,学习C语言,学到数组的时候感觉还行,可以接受。但是学到指针和函数后,绕来绕去的,就开始懵圈了。

但是,指针的重要性不言而喻,检验C语言学得好不好,就看“指针和内存管理”了。

大一下之后呢,就一直学Java,虽说原理都是相近的,但是毕竟C的语法还是比较细的,而且最近因为一些原因呢,要用C语言实现算法,所以抽空补补了C的基础和语法,特此记录一下。

1、数组的内存中的存储

那些简单的数组定义、初始化等比较简单就不说明,我们重点来看数组在内存中的存储。

在此,我们先来补充一下内存地址原理,其实这个知识点是《计算机组成原理》的内容,在此简单提一下,顺带补充一句,基础很重要,小伙伴们要重视基础哦!

假设我们的机子32位,则会使用4字节来存储地址,例如”0x11223344”;若为64位,则用8字节来存储地址,例如“0x1122334455667788”。

以32位机为例:

在这里插入图片描述

接着,我们先写一段很简单的程序,然后我们再来看,VS2022调试中的内存映像。

#include<stdio.h>

int main() {
	int arr[5] = { 11,22,33,44,55 };
	for (int i = 0; i < 5; i++) {
		printf("%d\n", arr[i]);
	}

	return 0;
}

内存映像:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通过内存映像我们可以很清楚的看到,例如:

  • 一个int类型确实占4字节
  • 数组在内存中是顺序存放的,大小根据元素的类型和个数决定
  • 数组名就是数组的首地址,也就是数组第一个元素的地址

2、指针

2.1、指针和指针变量

指针:一个变量的地址称为该变量的指针,也就是说,指针就是变量的地址。

指针变量:用来存放为“地址”的变量。

int num = 18;
int *point = &num;

假设32位机,num存放的地址为“0x 11 22 33 44”。

我们就说,变量num的地址(指针)是“0x 11 22 33 44”;指针变量point的值是“0x 11 22 33 44”。

我们都知道int类型的变量num占4个字节(32位机),那么指针变量point占多大的空间呢?答若为32位机,寻址范围为32位即4字节;若为64位机,则寻址范围为64位即8字节。

在这里插入图片描述

在这里插入图片描述


2.2、操作符

  1. 取地址操作符为“&”,也称“引用”,通过该操作符我们可以获取一个变量的地址值;

  2. 取值操作符为“*”,也称“解引用”,通过该操作符我们可以得到一个地址对应的数据。

#include<stdio.h>

int main() {

	int num = 15;
	int* point = &num;

	printf("num: %d\n", num);
    //若编译器不支持%p,可以用%u、%lu来代替
	printf("&num: %p\n",&num);
	printf("point: %p\n",point);
	printf("*point: %d\n",*point);

	return 0;
}
/*
结果:
num: 15
&num: 00EFFCBC
point: 00EFFCBC
*point: 15
*/

在这里插入图片描述


2.3、指针操作

2.3.1、指针和整数相加减

我们以加法为例,减法同理。

#include<stdio.h>

int main() {
	int arr[5] = { 1,2,3,4,5 };
	int* point = arr;
    
	printf("%p\n", point);
	printf("%p\n", point+2);
	printf("%d\n", *point);
	printf("%d\n", *(point+2));
	
	return 0;
}
/*
结果:
00DDFD00
00DDFD08
1
3
*/

在这里插入图片描述

0x 00 DD FD 00 -- 0x 00 DD FD 08,一共是相差8个字节,即两个int类型变量所占的空间

用"+"运算符把指针和整数相加(相减),整数都会和指针所指向类型的大小相乘(以字节为单位),再将结果与初始地址相加。
即,0x00DDFD00 + 2 * 4Byte = 00DDFD08

point + 2后的值就是arr[2]的地址,即(point + 2)与&arr[2]等价
2.3.2、指针递增
#include<stdio.h>

int main() {
	int arr[5] = { 1,2,3,4,5 };
	int* point = &arr[2];

	printf("%p\n", point);
	printf("%d\n", *point);
	printf("%p\n", ++point);
	printf("%d\n", *point);
	
	return 0;
}
/*
结果:
00F8FE78
3
00F8FE7C
4
*/

在这里插入图片描述

0x 00 F8 FE 78 --- 0x 00 F8 FE 7C,一共相差4个字节

一开始point指向arr[2],其值为"00F8FE78";而后point递增后指向arr[3],其值为"00F8FE7C"

所以,point++相当于把point的值加上4,即 加上(1 * 4 Byte)
2.3.3、指针求差
#include<stdio.h>

int main() {
	int arr[5] = { 1,2,3,4,5 };
	int* point01 = &arr[2];
	int* point02 = &arr[5];

	printf("%p\n", point01);
	printf("%p\n", point02);
	printf("%d\n", point02 - point01);

	return 0;
}
/*
结果:
006FFB38
006FFB44
3
*/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDuYawj3-1651588023408)(E:\Blog\C语言\数组、指针和函数.assets\image-20220430223535900.png)]

0x 00 6F FB 38 --- 0x 00 6F FB 44,一共相差12个字节

point02 - point01 得3,意思是两个指针所指向得两个元素之间相差3个int,即12个字节
2.3.4、& 和 * 同时使用
//若有此语句
int a = 18;
int* point = &a;

&*point 和 *&a 的含义和区别

“&”和“*”两个运算符的优先级别相同,但要按自右向左的方向结合。

因此,&* point 与 &a 相同,都表示变量a的地址,也就是point。

而*&a,首先进行&a运算,得到a的地址,再进行*运算。*&a和*point的作用是一样的,它们都等价于变量a,即*&a 与a等价。


2.4、指针类型与地址

地址应该和指针类型兼容,也就是说,我们不能把int型变量的地址赋值给指向double类型的指针,只能将int型变量的地址赋值给指向int类型的指针变量中。

用代码表示如下:

int num01 = 18;
double num02 = 3.14;
int* point01 = &num01;		//正确,歪瑞good
double* point02 = &num01; 	//毫无意义而且会出错
double* poinr03 = &num02;	//正确,歪瑞good

我们在开发过程中,应该避免出现这种问题。

但是,如果硬要这么做的话,又有什么影响呢?

其实是可以赋值的,但是会出现数据截断等错误,而且不能进行指针操作,由于篇幅所限,详情请看《C语言_地址与指针类型不兼容造成的影响》。


3、函数

3.1、全局变量和局部变量

#include<stdio.h>

int i = 18;	// 全局变量

void print(int a) {
	printf("print -- &a = %p\n", &a);
	printf("print -- a = %d\n", a);
}

int main() {
	printf("main -- &i = %p\n", &i);
	printf("main -- i = %d\n", i);

	print(i);

	int i = 5;	//局部变量
	printf("main -- &i = %p\n", &i);
	printf("main -- i = %d\n", i);

	print(i);
	return 0;
}

在这里插入图片描述

全局变量i存储在数据段,所以main函数和my_print函数都是可见的。

全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效,因此开发中应尽量避免使用全局变量!

我们在函数内定义的变量都称为局部变量,局部变量存储在自己的函数对应的栈空间内,函数执行结束后,函数内的局部变量所分配的空间将会得到释放。

如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值。

在这里插入图片描述


3.2、堆空间和栈空间的差异

我们用一个例子来看一看栈空间和堆空间的差异。

#include <stdio.h>

char* print_stack()
{
	char c[17] = "I am print_stack";
	puts(c);//能正常打印
	return c;
}

char* print_malloc()
{
    //malloc是在堆空间中开辟一块内存
	char* p = (char*)malloc(30);
	strcpy(p, "I am print_malloc");
	puts(p);
	return p;
}

int main()
{
	char* p;
	p = print_stack();//栈空间会随着函数的执行结束而释放,相当于p=c
	puts(p);//打印不出来
	p = print_malloc();//堆空间不会随子函数的结束而释放,必须自己free
	puts(p);
	free(p);
	return 0;
}

在这里插入图片描述


4、函数、数组和指针

假设我们有一个需求,是要对数组进行求和,我们可以怎样定义函数呢?

#include<stdio.h>
#define NUM 10

int getSum01(int arr[], int num);

int getSum02(int* arr, int num);

int getSum03(int* start, int* end);

int main() {
    int arr[NUM] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* point = arr;
    int result01 = getSum01(arr, NUM);
    int result02 = getSum02(point, NUM);
    int result03 = getSum03(point, point + NUM);
    printf("result01 = %d\n", result01);
    printf("result02 = %d\n", result02);
    printf("result03 = %d\n", result03);
    return 0;
}

int getSum01(int arr[], int num) {
    printf("getSum01:arr = %p\n", arr);
    int result = 0;
    for (int i = 0; i < num; i++) {
        result += arr[i];
    }
    return result;
}

int getSum02(int* arr, int num) {
    printf("getSum02:arr = %p\n", arr);
    int result = 0;
    for (int i = 0; i < num; i++) {
        result += *(arr + i);
    }
    return result;
}

int getSum03(int* start, int* end) {
    printf("getSum03:start = %p\n", start);
    printf("getSum03:end = %p\n", end);
    int result = 0;
    while (start < end) {
        result += *start;
        /*
         * 让指针指向下一个元素
         * 指针++并不是指向下一个字节的地址;
         * 而是指向下一个元素的地址,增量依据类型而定
         */
        start++;
    }
    return result;
}


/*
结果:
getSum01:arr = 006FFB30
getSum02:arr = 006FFB30
getSum03:start = 006FFB30
getSum03:end = 006FFB58
result01 = 55
result02 = 55
result03 = 55
*/

在这里插入图片描述

int getSum01(int arr[], int num);
int getSum02(int* arr, int num);
int getSum03(int* start, int* end);

从输出的结果可以出,以上三个函数是等价的!

之前,我们也说过,数组名就是数组首元素地址。

无论是将函数形参定义成“数组”亦或是“指针”,我们传入的“数组名”亦或是“指针”,其实在底层最后传的都是“指针”,也就是“地址”,对应到本例就是“数组首元素的地址”。

这样也就能明白,为什么我们定义函数时需要两个形式参数,即“数组地址”和“数组大小”。

因为在函数实现中,我们并不知道数组的长度,盲目运算可能会造成空指针异常,因此我们要手动地传入“数组长度”。当然,我们也可以传入“数组首地址”和“数组尾地址”,其实原理都是一样的。


emmm,大概就唠这么多吧,最近发现基础真的重要!!!

注:如有错误,敬请指正!!!

C语言指针数组函数笔记 C语言指针数组函数笔记,自己总结的,希望有帮助 立即下载

相关推荐

Springboot(五十六)SpringBoot3集成SkyWalking

Dskywalking.collector.backend_service:表示skywalking的collector服务的IP及端口。注意上图中红框标注的位置,我们可以根据启动Springboot项目启动时候控制台输出的ID进行筛选。注意上图中红框标注的位置,我们可以根据启动Springboot项目启动时候控制台输出的ID进行筛选。其他的版本的 APM 地址(这个我不需要,我们是在docker中部署的,留给有需要的同学)其他的java 版本的 Agents 地址。

camellia的博客 2599

C语言(函数指针数组)详解

指针,指针数组,数组指针,函数指针数组

Raikkonen_love的博客 2万+

毕设案例|保安公司智慧管理系统的建设与开发方案

"保安公司智慧管理系统是一款专为保安行业设计的高效信息化解决方案,其核心功能涵盖了从人力资源管理到业务运营的各个方面。系统以Java作为开发语言,结合SpringMVC框架,确保了稳定性和可扩展性,而MySQL数据库则提供了强大的数据存储和处理能力。首先,系统允许用户设置各类保安,如门卫、巡逻员等,实现精细化分类管理。其次,保安信息管理模块包括详细的人事档案、资格证书、培训记录等,便于人员追踪和考核。部门和城市设置方便公司层级管理和异地项目管理。岗位设置与时间安排精确到分钟,确保排班科学合理。

muyu2980的专栏 935

C 语言 —— 指针、函数、数组

在这一部分的学习中,我们主要是回顾一下之前学习的函数和数组的知识,并看一下它们和指针的联系。 1. 指针和函数 1.1. 分析地址传递 我们首先来看看指针和函数的联系。在讲函数参数传递机制的时候,我们提到了地址传递,当时用到的程序是交换函数的两个参数的值,代码如下: #include<stdio.h> int swap(int *a, int *b); int main() { int a = 30, b = 24; printf("main 交换前:a=%d, b=%d\n",

柚咖小窝 650

RIGOL DS1102E示波器新手必看:从开机到捕获第一个波形的5分钟指南

本文为RIGOL DS1102E示波器新手提供了一份快速上手指南。内容涵盖从开机、探头校准到捕获第一个波形的完整流程,重点解析了触发设置、自动测量等核心操作,并指出了探头衰减比设置等常见误区,帮助用户快速掌握这款经典数字示波器的基本使用方法。

dream的博客 739

STM32 伺服电机 指令脉冲+方向位置控制模式 主从定时器 实现PWM脉冲数精确控制

实验室有个项目涉及到多个步进电机以及伺服电机的控制,需要电机得到一个触发信号后精确移动一段距离。 下面以单个伺服电机控制为例,利用伺服电机的指令脉冲加方向位置控制模式实现jing'que ...

phcdd的博客 2万+

【C语言】函数指针|函数指针数组|函数指针数组指针|如何理解?

【C语言】函数指针|函数指针数组|函数指针数组指针|如何理解?函数指针实现简易计算器|库函数qsort|使用sqort函数对结构体变量的成员排序|模拟qsort实现冒泡排序|泛型指针|结构体数组

m0_64538862的博客 2899

Score Matching(得分匹配)

Score Matching(得分匹配)是一种统计学习方法,用于估计概率密度函数的梯度(即得分函数),而无需知道密度函数的归一化常数。这种方法由Hyvärinen在2005年提出,主要用于无监督学习,特别是在密度估计和生成模型中。

weixin_39699362的博客 2167

C语言的函数指针数组

简要介绍以下函数指针以及一些函数调用需要输入函数指针作为形参的情况。

qq_46284844的博客 2992

数学之美 | 图论和网络爬虫

文章目录概述图论网络爬虫分析BFSDFSBFS + DFS参考资料 概述 这篇文章主要给大家介绍一下计算机算法中非常重要的一部分——图论,以及它在生活中的应用——网络爬虫。   图论 离散数学是当代数学的一个重要的分支,也是计算机科学的数学基础。它又包括四个分支,而图论便是其中一支,另外三个分别是:数理逻辑、集合论、近代数学。 图论中的图由一些节点和边构成,比如下面这张图 。 在图论中一个很基础...

小小酥梨的博客 3229

FPGA实现串口升级及MultiBoot(九)BPI FLASH相关实例演示

与SPI相关知识并没有什么太多区别(别杠硬件或者架构上),相关知识点都在上面指出,大家有什么其他需要注意点,欢迎留言讨论。关于“四样错误”就不演示了,没什么太大区别,下一讲我们开始串口升级。PS:为了扩展ICAPE2原语的使用,在原来的基础上增加了几个常用FPGA ID,并在触发启动时候增加了可选电平触发或上升/下降沿触发(例化IP时选择),新版本号V1.1。

OpenFPGA的博客 1444

数组指针、函数指针、指针数组、函数 指针数组、指针函数详细总结

数组指针、函数指针、指针数组、函数 指针数组、指针函数详细总结

wei_Embedded_z的博客 4959

PINN求解PDE

PINN求解Dirichlet泊松方程 泊松方程引入 −Δu=f,x∈Ω=(0,1)2-\Delta u=f,x \in \Omega=(0,1)^2−Δu=f,x∈Ω=(0,1)2 u=g,x∈∂Ωu=g,x \in \partial \Omegau=g,x∈∂Ω import torch import time import numpy as np import matplotlib.pyplot as plt import torch.nn as nn def UU(X, order,prob):

forrestguang的博客 3804

C语言进阶:指针进阶(数组参数和指针参数、函数指针和函数指针数组)

C语言进阶,介绍数组参数和指针参数、函数指针和函数指针数组的内容

2501_91709575的博客 3250

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C语言——函数指针和函数指针数组

函数指针是指向函数的指针变量,它可以用来存储和调用函数的地址。回调函数:函数指针可以作为参数传递给其他函数,使得被调用的函数能够在特定条件或事件发生时,调用传入的函数指针进行回调操作。这样可以实现函数之间的灵活交互和扩展,常见于事件处理、消息传递等场景。函数指针数组:可以使用函数指针数组来实现多态性或动态分派。通过在数组中存储不同的函数指针,并根据需要调用其中的函数,可以在运行时决定调用哪个函数,从而实现动态切换功能。函数指针作为返回值。

qq_51504544的博客 942

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

c语言函数的数组返回和指针返回

函数内的数组和指针都是属于函数栈空间。如果把数组或指针返回给外部指针,会发生什么? 1先看看第一种,返回数组; #include "stdio.h" char * get_c() { char ch[]="hello"; //这是一个栈空间 char *p; //这也是栈空间 p=ch; printf("%x\n",&ch); printf("%x\n",...

HES_C的博客 9547

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C语言之指针、数组和函数

    基本解释  1、指针的本质是一个与地址相关的复合类型,它的值是数据存放的位置(地址);数组的本质则是一系列的变量。  2、数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。  3、当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。   问题:指针与数

dazhiqian的专栏 560

【C语言进阶】指针和数组(指针与数组名;指针数组,数组指针;数组传参,指针传参;字符指针,函数指针,函数指针数组,回调函数)

【初级C语言】指针和数组(指针与数组名;指针数组,数组指针;数组传参,指针传参;字符指针,函数指针,函数指针数组,回调函数)

52Herzt的博客 1491

c语言所有函数指针变量,C语言函数指针变量和指针函数以及指针数组

C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针变量”。函数指针变量定义的一般形式为:类型说明符 (*指针变量名)();其中“类型说明符”表示被指函数的返回值的类型。“(* 指针变量名)”表示“*...

weixin_32667439的博客 422

【C语言】指针的进阶篇,深入理解指针和数组,函数之间的关系

在上一篇博客中,我们了解了strlen的模拟实现,以及冒泡排序,并且为了熟悉指针数组,我们还学习了用指针数组来模拟实现二维数组,本期博客将用其他指针类型来开篇,并学习指针和数组,函数之间的关系。

cilmy23的note 1636

C语言-指针高级(二级指针和多级指针、数组指针、指针数组、函数指针);

本章详细讲解:C语言二级指针和多级指针、数组指针、指针数组、函数指针、函数指针数组;

李永奉的博客 1467

C语言高级编程:数组和指针作为函数形参

测试环境: win10 x86-64 gcc: x86_64-pc-cygwin 1. 说明 1)作为函数形参时(也只有这种情况),数组与指针等同,int *p = int a[],p和a在函数内部都是指针 2)数组作为函数形参时将被转化为指向数组第一个元素的指针,无论是int a[10]还是int a[]形式的形参,在函数内部都是int *a形式指针 3)没有办法直接将数组本身...

ARM-Linux 5974

C语言--函数指针变量和函数指针数组的区别(详解)

函数指针是指向函数的指针变量。它可以用来存储函数的地址,从而可以通过指针调用函数。返回类型 (*指针变量名)(参数列表);其中,返回值类型是函数的返回值类型,指针变量名是函数指针的名称,参数列表是函数的参数类型和个数。例如,我们有一个函数 add,它接受两个整数参数并返回它们的和。我们可以声明一个函数指针 p 来指向该函数:// 声明一个函数指针变量pp = add;// 将函数add的地址赋值给p// 通过函数指针调用add函数。

Jason_from_China的博客 1255

C语言-数组的初始化,数组和指针,函数与数组

C语言之数组22. 初始化2.1 整体初始化2.2 部分初始化2.3 大小2.4 赋值3. 数组与指针4. 函数与数组4.1 传递数组给函数4.2 从函数返回数组 2. 初始化 2.1 整体初始化 对比下面三段代码,分析输出。 代码一 int arr[12]; for(int i=0;i<12;++i){ printf("%d ",arr[i]); } 代码二 int ar...

kuanyew的博客 1200

C语言指针复习—— 空指针和野指针、数组与指针、指针与函数传参、指针与函数

1. 普通指针的理解 2. 数组与指针 3. 指针与函数传参 4. 对于函数的理解

不善挽留 1189

C语言---指针数组和数组指针,函数指针

/指针数组int *arr[5] //函数指针int (*p)(int,int) = add;printf("请选择你要进行什么计算(1:加、2:减、3:乘、4.除):");//(arr[choice-1])将数组内用户选择的算法函数指针传递给p。int (*p)[5] 这个声明定义了一个指针 ,指向一个包含5个整数的数组。printf("请输入你要进行计算的两个整数(之间用/分隔):");//int (*p)(int,int)定义一个函数指针。例:int (*p)(int,int) = add;

2301_80464770的博客 600
上一篇: C语言_地址与指针类型不兼容造成的影响
下一篇: C语言_指针和引用的区别
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

窝在角落里学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值