C语言指针引用数组案例讲解

前言:C语言中指针玩的是什么,是内存,要想学好指针的小伙伴们要先对数据在内存中是怎么玩的做一番了解~

      当在程序中定义一个变量时,系统会根据其数据类型为其开辟内存空间,例如Visual C++为整型变量分配四个字节的空间,为单精度浮点型变量分配四个字节,为字符型变量分配一个字节,内存中每个字节都有自己独立且唯一的一个编号,这就是地址 ,如下图,系统为变量i分配了2000~2004的存储单元。

_访问变量的方式_有如下图两种:
第一种直接访问方式,直接通过变量名访问,变量名与地址有一一对应关系,因此按此地址直接对变量i的存储单元进行访问;
第二种间接访问方式,先通过i_pointer找到i的地址的位置,再通过i的所存地址的位置找到i的地址2000,随后对变量i进行存取操作。间接访问的方式就要用到指针,所谓指针(2000)即为一个变量的地址,指针变量(i_pointer)是存储这个地址的用来指向另一个对象的变量。

关键字 变量类型
int 整型变量
char 字符变量
类型名* 指针变量

它们之间的关系为:指针变量的值是指针,指针是变量i的地址,变量i存放所需要存放的存储内容。
图片的中*为取值运算符,*i_pointer表示对i_pointer中存放的地址进行取值,相当于 变量i。

指针的定义:

基类型 *变量名
例:int *p
char *p
float p
注意 : 此时的
与上文中提到的取值运算符并不是一个概念,此时的
*意思是定义一个变量,这个变量是指针变量。

指针的引用:

对指针进行赋值:
以下面程序为例:

int *p;
int a = 3;
p = &a;
*p = 2;

p = &a(&为取地址符,意思是取变量a的地址赋给指针变量P)
*p = 2(p上文中已经提到是对指针变量P中存储的地址进行取值p相当于变量a,对
*p进行赋值即相当于对变量a进行赋值)

指针变量做函数参数

以定义两个变量a和b,使其值进行交换为例进行阐述

#include<stdio.h>

//值传递
void swap1(int x, int y) {
	int z;
	z = x;
	x = y;
	y = z;
}

//地址传递
void swap2(int *p1, int *p2) {
	int t = *p1;
	*p1 = *p2;
	*p2 = t;
}

/*
错误,指针变量t所指向的内容不可预见,对*t赋值就是向一个未知存储单元赋值 ,可能操纵到有用信息,
破坏系统的正常工作状态 ,这种指针叫做**野指针**;

那么如何解决野指针危险性呢:
可以对该指针进行初始化,使其指向NULL,NULL为地址为0的内存地址,在大多数操作系统上,该内存为操作系统保留,
用户不可操控
*/
//void swap3(int *p1, int *p2) {
//	int *t;
//	*t = *p1;
//	p1 = *p2;	//报错
//	p2 = *t;
//}

/*
C语言中实参变量与形参变量之间的数据传递是单向的“值传递”方式。
不能通过操作形参中指针变量的值企图改变实参中指针变量的值,但是可以通过形参接收到的实参传过来的地址
对指针变量指向的值进行操作。
*/
void swap4(int *p1, int *p2) {
	int *t = NULL;
	t = p1;
	p1 = p2;
	p2 = t;
}

int main() {
	int a, b;
	scanf("%d %d", &a, &b);

	swap1(a, b);
	printf("%d %d\n", a, b);

	int *p1 = &a, *p2 = &b;

	swap2(p1, p2);
	printf("%d %d\n", a, b);

	swap4(p1, p2);
	printf("%d %d\n", *p1, *p2);	//注:在swap2()函数中,a b的值发生了交换 

	return 0;
}

/*
运行结果:
1 3
1 3
3 1
3 1
*/

指针指向数组

盆友们一定要记住这两句话再往下看***!!!***
首地址:一段内存空间的第一个存储单元,而不是第一个字节;
指针变量的加减:以指针指向的类型空间为单元进行偏移;

以定义一个数组,输入数值,最后输出数组中所有元素为例进行阐述

/*
须知:
1.单目运算符优先级比双目运算符高,同级下从右往左结合
2.*(p + i)等价于*(a+i)等价于a[i]
3.数组名为数组的首地址,即p = a相当于p = &a[0]
4.p + 1指向数组中的下一个元素,加上的不是简单的字节数,而是定义的指针的基类型的字节数
5.a[i]的[]为变址运算符,下标法中对下标的处理是转换成地址的,也是按照a + i计算地址,然后找出此地址单元中的值
6.利用p++的指针自加操作,指针直接指向元素,不必每次都计算地址,比下标法和计算指针地址后再取值的方法快
7.a是一个类型为int *的指针常量,指向数组首个元素的地址,不能企图使用a++的方式便利数组中的元素
8.指针的加减运算往往作用在同一数组下的元素上,虽然指针变量可以指向数组元素以后的存储单元,但是得到的数据往往是
不被我们所期待的数据,这种操作是毫无意义的
*/
#include<stdio.h>
int main() {
	int a[5];

	//下标法
	for(int i = 0; i < 5; i++)
		scanf("%d", &a[i]);				//等价于scanf("%d", a + i);
	int *p, *p1;

	p = a;								//等价于p = &a[0]
	p1 = &a[0];

	for(int i = 0; i < 5; i++)
		printf("%d ", a[i]);
	printf("\n");

	//指针法
	/*
	p:a[0]的地址	*p:a[0]的值
	*(p+1):a[1]的值
	*(p+2):a[2]的值 

	p的值并未改变 

	test:
	#include<stdio.h>
	int main() {
		int a[5] = {1, 2, 3, 4, 5};
		int *p = a;
		printf("%d\n", p);
		for(int i = 0; i < 5; i++) {
			printf("%d ", *(p + i));
		}
		printf("\n%d\n", p);

		for(int i = 0; i < 5; i++) {
			printf("%d ", *p++);
		}
		printf("\n%d\n", p);
	}
	/*
	运行结果:
	6618608
	1 2 3 4 5
	6618608
	6618628
	*/

	*/

	for(int i = 0; i < 5; i++)
		printf("%d ", *(p + i));
	printf("\n");

	//a为指针常量不能改变,变的是 a + i本身
	for(int i = 0; i < 5; i++)
		printf("%d ", *(a + i));
	printf("\n");

	//指针p在不断移动,因此在学习的过程中要时刻注意指针的位置!!!
	for(int p = a; p < (a + 5); p++)
		printf("%d ", *p);
	return 0;
}

/*
补充:
1.由于在C语言编程系统中,对下标的处理是转换成地址的,因此, p[i]即被处理成*(p + i) 

数组名做函数参数

/*
须知:
1.数组名为数组首元素的地址,实参传递a传递给形参的是地址,形参需要用一个指针变量来接实参的地址
2.C编译都是将形参数组名作为指针变量来处理的,也就是说,程序中形参的a[]的a实际上是一个指针,对a[]的操控实际上
就是对指向实参中的数组进行操控
3.上文中已经提到p[i]与*(p+i)无条件等价
*/
#include<stdio.h>

void swap(int a[], int n) {
	int h = 0, t = n - 1, m = (n - 1) / 2;
	for(h; h <= m; h++) {
		int tmp = a[h];
		a[h] = a[t];		//根据须知3: a[h]等价于*(a+h)
		a[t] = tmp;
		t--;
	}
}

void swap1(int *x, int n) {
	int *p, *i, *j;
	i = x;
	j = x + n - 1;
	p = x + (n - 1) / 2;

	for( ; i <= p; i++, j--) {
		int tmp = *i;
		*i = *j;
		*j = tmp;
	}
}

int main() {
	int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
	for(int i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	swap(a, 10);
	for(int i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");

	swap1(a, 10);
	for(int i = 0; i < 10; i++)
		printf("%d ", a[i]);
	printf("\n");
	return 0;
}
/*
运行结果:
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9
*/ 

指针指向 二维数组

a[3][4]数组的结构:
三个一维数组
四个int类型的元素

**
再次强调:
1.指针变量的加减:以指针指向的类型空间为单元进行偏移;
2.数组名代表数组元素的首地址

a是二维数组名,指向的第一个存储单元是a[0]这个一维数组,a的类型是指向一维数组的指针常量, a+1即偏移一个一维数组;
a[0],a[1], a[2]是一维数组名,代表一维数组中的元素的首地址,也就是说a[0]的值是&a[0][0],a[1]的值是&a[1][0],a[2]的值是&a[2][0]。    a[0],a[1], a[2]分别指向的第一个存储单元是a[0][0], a[1][0], a[2][0]这几个元素,它们的类型是指向元素的指针常量,a[0]+1即偏移一个元素;

为了让大家看清除,博主以表格形式展示出来:

首地址 指向 类型 移动一位 移动字节数
二维数组的首地址a a[0]这个一维数组 int(*)[4] a+1 16B
以为数组的首地址a[0] a[0][0]元素 int* a[0]+1 4B

指针指向二维数组的各种表现形式

#include<stdio.h>
int main() {
	int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23};
	/*
	a是指向行的指针常量,代表二维数组的首地址,即首行的首地址,
	a + 1:序号为 1的行的首地址
	a + 2:序号为 2的行的首地址

	在行指针前面加一个 * ,则转换成列指针(列地址),指向一维数组的首地址 ;

	*a:对第0行的地址进行取值,由于a并不是一个变量的存储单元,取值后得到一维数组的地址a[0]<->*(a+0)<->*a,指向列地址,即第0行一维数组的首地址
	*(a + 1): 第1行一维数组的首地址
	*(a + 2): 第2行一维数组的首地址
	由于a[0] = *a, a[1] = *(a + 1), a[2] = *(a + 2),所以两种形式等价
	a[0]: 代表0行一维数组的首地址,指向0行0列元素地址
	a[1]: 代表1行一维数组的首地址,指向1行0列元素地址
	a[2]: 代表2行一维数组的首地址,指向2行0列元素地址 

	a[0] + 0: 代表0行0列元素的地址
	a[0] + 1: 代表0行1列元素的地址
	a[0] + 2: 代表0行2列元素的地址 

	*(a[0] + 0): 0行0列元素的值,a[0][0],*(*(a + 0) + 0)
	*(a[0] + 1): 0行1列元素的值,a[0][1], *(*(a + 0) + 1)
	*(a[0] + 2): 0行2列元素的值,a[0][2], *(*(a + 0) + 2)

	在列指针前面加一个 & , 则转换成行指针(行地址),行指针前面加上一个 &,则转换成指向整个数组的指针(表示为整个数组的地址),指向二维数组的首地址。
	&a: 指向整个二维数组
	&a[0]: 指向第0行的一维数组
	&a[0][0]: 指向0行0列的元素,即指向a[0][0], 是a[0][0]这个元素的地址 

	*/
	printf("%d %d\n", a, *a);			//a:表示0行首地址	  *a:表示0行0列元素的地址	二者的值一样,但是指针类型不同
	printf("%d %d\n", a[0], *(a + 0));
	printf("%d %d\n", &a[0], &a[0][0]);
	printf("%d %d\n", a[1], a + 1);
	printf("%d %d\n", &a[1][0], *(a + 1) + 0);
	printf("%d %d\n", a[2], *(a + 2));
	printf("%d %d\n", &a[2], a + 2);
	printf("%d %d\n", a[1][0], *(*(a + 1) + 0));
	printf("%d %d\n", *a[2], *(*(a + 2) + 0));
	return 0;
}

/*
运行结果:
6618608 6618608
6618608 6618608
6618608 6618608
6618624 6618624
6618624 6618624
6618640 6618640
6618640 6618640
9 9
17 17
*/ 

指向二维数组的指针变量

#include<stdio.h>
int main() {
	int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23};

	/*
	指向数组元素的指针变量
	*/
	int *p;
	for(p = a[0]; p < a[0] + 12; p++) {
		printf("%2d ", *p);

	}
	printf("\n\n");

	/*
	指向一维数组的指针变量
	*/
	int (*q)[4] = a;				//指针变量p指向4个整形元素的一维数组
	for(int i = 0; i < 3; i++) {
		for(int j = 0; j < 4; j++) {
			printf("%2d ", *(*(q + i) + j));
		}
	}

	return 0;
} 

用指向数组的指针做函数参数

#include<stdio.h>
//指向变量的指针变量
void avg(float *p, int n) {
	float sum = 0, ans = 0;
	float *p1 = p + 11;
	for(p; p <= p1; p++)
		sum = sum + *p;
	ans = sum / n;
	printf("%.2f\n", ans);
}
//指向一维数组的指针变量
void search(float (*p)[4], int n) {
	for(int i = 0; i < 4; i++) {
		printf("%.2f ", *(*(p + n) + i));
	}
}
int main() {
	float score[3][4] = {78, 90, 89, 34, 91, 61, 71, 84, 67, 76, 100, 53};
	avg(*score,12);
	search(score, 2);
	return 0;
}

指针指向 三维数组

a[2][3][4]数组的结构:
两个二维数组
三个一维数组
四个int类型的元素

首地址 指向 类型 移动一位 移动字节数
a a[0]这个二维数组 int(*)[3][4] a+1 48B
a[0] a[0][0]这个一维数组 int(*)[4] a[0]+1 16B
a[0][0] a[0][0][0]元素 int * a[0][0]+1 4B

指针指向 多维数组

原理与二维数组三维数组一样
取元素的值:

到此这篇关于C语言指针引用数组案例讲解的文章就介绍到这了,更多相关C语言指针引用数组内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C语言中Char型指针数组与字符数组的区别

    详解C语言中Char型指针数组与字符数组的区别 1.char 类型的指针数组:每个元素都指向一个字符串,指向可以改变 char *name[3] = { "abc", "def", "gbk" }; for(int i = 0 ; i < strlen(name); i ++){ printf("%s\n", *(name+i)); //printf("%s\n", name[i]); } //指向改

  • C语言 指针与二维数组详解

    二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的,它们之间没有"缝隙".以下面的二维数组 a 为例: int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; 从概念上理解,a 的分布像一个矩阵: 0   1   2   3 4   5   6   7 8   9  10  11 但在内存中,a 的分布是一维线性的,整个数组占用一块连续的内存: C语言中的二维数组是按行排列的,也就是先存放 a[

  • C语言 数组指针详解及示例代码

    数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element).数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存.以int arr[] = { 99, 15, 100, 888, 252 };为例,该数组在内存中的分布如下图所示: 定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第 0 个元素.在C语言中,我们将第 0 个元素的地址称为数组的首地址.以上面的数组为例,下图是 arr 的指向: 下面的例子演示了如何以指针的方

  • C语言 指针数组详解及示例代码

    如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组.指针数组的定义形式一般为: dataType *arrayName[length]; [ ]的优先级高于*,该定义形式应该理解为: dataType *(arrayName[length]); 括号里面说明arrayName是一个数组,包含了length个元素,括号外面说明每个元素的类型为dataType *. 除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子: #include <stdi

  • 使用Python向C语言的链接库传递数组、结构体、指针类型的数据

    使用python向C语言的链接库传递数组.结构体.指针类型的数据 由于最近的项目频繁使用python调用同事的C语言代码,在调用过程中踩了很多坑,一点一点写出来供大家参考,我们仍然是使用ctypes来调用C语言的代码库. 至于如何调用基础数据类型的数据,请大家参考我的另外一篇文章:Python使用ctypes调用C/C++的方法 1. 使用python给C语言函数传递数组类型的参数 想必很多时候,C语言会使用数组作为参数,在之前我们使用过ctypes的一些数据类型作为C语言参数类型,包括byte

  • C语言指针引用数组案例讲解

    前言:C语言中指针玩的是什么,是内存,要想学好指针的小伙伴们要先对数据在内存中是怎么玩的做一番了解~       当在程序中定义一个变量时,系统会根据其数据类型为其开辟内存空间,例如Visual C++为整型变量分配四个字节的空间,为单精度浮点型变量分配四个字节,为字符型变量分配一个字节,内存中每个字节都有自己独立且唯一的一个编号,这就是地址 ,如下图,系统为变量i分配了2000~2004的存储单元. _访问变量的方式_有如下图两种: 第一种直接访问方式,直接通过变量名访问,变量名与地址有一一对

  • C语言指针基础知识实例讲解

    对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小.比如int整型变量分配4个字节,char字符型变量分配1个字节等等.被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方.指针作为一个变量用来存放地址,可以通过指针来改动变量. 上图就是一个简单的定义一个一级指针变量和利用指针改变变量数值的过程.int*表示整型指针,*p表示解引用操作,就是利用指针找到a的地址然后再改

  • C语言指针和数组深入探究使用方法

    目录 1.数组参数和指针参数 1.1 一维数组传参 1.2 一级指针传参 1.3 二维数组参数和二级指针参数 1.4 野指针的问题 2.函数指针 3.函数指针数组 4.指向函数数组的指针 5.回调函数 6.一道笔试题 1.数组参数和指针参数 1.1 一维数组传参 这里在前几期我们已经初略的见识过了,但是这里我们要提一个概念,数组给函数传参是会发生降维的,降维成什么呢?我们看代码: 这里通过打印形参的大小,发现是 4,其实也不奇怪,目前我们是 32 位操作环境,所以一个指针也就是 4 个字节,所以

  • C语言 指针与数组的详解及区别

    C语言 指针与数组的详解及对比 通俗理解数组指针和指针数组 数组指针: eg:int( *arr)[10]; 数组指针通俗理解就是这个数组作为指针,指向某一个变量. 指针数组: eg:int*arr[10]; 指针数组简言之就是存放指针的数组: --数组并非指针&&指针并非数组 (1)定义一个外部变量: eg:int value=10; int *p=&value; 举例:当需要在一个函数中用这个变量时:externa int*p;而非extern int p[]; 分析:当用:e

  • Go语言指针使用分析与讲解

    普通指针 和C语言一样, 允许用一个变量来存放其它变量的地址, 这种专门用于存储其它变量地址的变量, 我们称之为指针变量 和C语言一样, Go语言中的指针无论是什么类型占用内存都一样(32位4个字节, 64位8个字节) package main import ( "fmt" "unsafe" ) func main() { var p1 *int; var p2 *float64; var p3 *bool; fmt.Println(unsafe.Sizeof(p1

  • 赌你会懵的C语言指针进阶数组场景解析

    目录 正片开始 一维数组 字符数组 二维数组 整点硬菜 正片开始 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕头警告) 直接给大家盘个套餐: 一维数组 int a[] = {1,2,3,4,5}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); pri

  • C语言例题讲解指针与数组

    目录 1.概要复习 2.指针与数组笔试题 2.1一维数组 2.2字符数组 2.3字符串数组 2.4字符串指针 2.5二维数组 1.概要复习 本篇的内容主要围绕指针与数组.指针与字符串等之间的关系,以及进一步理解sizeof .strlen 的使用与意义. 数组是指具有相同类型元素的集合,字符串常量是一个指向在连续空间里存放的字符的首字符的地址的指针.我们会在下面理解数组与字符串数组的不同. sizeof 是一个操作符,是计算类型空间大小的.strlen 是针对字符串的库函数,用来求字符串的长度.

  • C语言零基础讲解指针和数组

    目录 一.指针和数组分析-上 1.数组的本质 2.指针的运算 3.指针的比较 4.小结 二.指针与数组分析-下 1.数组的访问方式 2.下标形式 VS 指针形式 3.a 和 &a 的区别 4.数组参数 5.小结 一.指针和数组分析-上 1.数组的本质 数组是一段连续的内存空间 数组的空间大小为 sizeof(array_type) * array_size 数组名可看做指向数组第一个元素的常量指针 下面看一段代码: #include <stdio.h> int main() { int

  • 深入理解C语言中使用频率较高的指针与数组

    目录 定义 指针与二维数组 指针数组与数组指针 数组指针的应用 操作 总结 定义 指针:C语言中某种数据类型的数据存储的内存地址,例如:指向各种整型的指针或者指向某个结构体的指针. 数组:若干个相同C语言数据类型的元素在连续内存中储存的一种形态. 数组在编译时就已经被确定下来,而指针直到运行时才能被真正的确定到底指向何方.所以数组的这些身份(内存)一旦确定下来就不能轻易的改变了,它们(内存)会伴随数组一生. 而指针则有很多的选择,在其一生他可以选择不同的生活方式,比如一个字符指针可以指向单个字符

  • C语言中指针和数组试题详解分析

    目录 数组题: 程序一(一维数组): 字符数组 程序二(字符数组): 程序三(字符数组): 程序四(字符数组): 程序五(字符数组): 二维数组 程序六( 二维数组): 指针题 程序七( 指针): 程序八( 指针): 程序九( 指针): 程序十( 指针): 程序十( 图): 程序十一( 指针): 程序十二( 指针): 程序十三( 指针): 指针 和 数组 试题解析 小编,在这里想说一下,c语言的最后一节 C预处理,可能还需要一些时间,因为小编,昨天才下载了虚拟机 和 linux 系统,还没开始安

随机推荐