当数组出现在函数当中时,怎样可以传递变量的值 、、指针与数组的关系

#include<iostream>
using namespace std;
template <class T>
void sort(T* a, int n)//***注意***
{
int t;
T temp;
for (int i=0; i<n-1; i++)
{
 t=i;
for (int j=i+1; j<n; j++)
 {
if (*(a+t)>*(a+j))
 t=j;
 }
 temp=*(a+i);
 *(a+i)=*(a+t);
 *(a+t)=temp;
}
for(int i=0;i<n;i++){
	cout<<a[i]<<endl; 
}
}

int main()
{
	int a[3];
	a[0]=12,a[1]=41,a[2]=1;
	float a1[3];
	a1[0]=12,a1[1]=41,a1[2]=1;
	double a2[3];
	a2[0]=12,a2[1]=41,a2[2]=1;
	sort(a,3); //***注意注意***
	return 0;
}

在此次当中,要首先注意的是当数组出现在函数当中时,怎样可以传递变量的值,以上两个点是要点,以后可以这样使用,以下是网上搜寻来的知识点,概括较为详细


指针与数组的关系

在第一小节中,声明了一个数组 int a[10];,假设这里我们又定义了一个指针变量 pa 如下:

int *pa;

则说明 pa 是一个指向整型数据的指针,那么赋值语句:

pa = &a[0];

表示将指针 pa 指向数组 a 的第 0 个元素,也就是说,pa 的值为数组元素 a[0] 的地址,如下图:
在这里插入图片描述

那么赋值语句 int x = *pa; 表示将把数组元素 a[0] 中的值复制到变量 x中,与 int x = a[0]; 是等价的。

如果 pa 指向数组中的某个特定元素,那么,根据指针运算的定义,pa+1 将指向下一个元素,pa+i 将指向 pa 所指向的数组元素之后的第 i 个元素,而 pa-i 将指向 pa 所指向的数组元素之前的第 i 个元素。

因此,如果指针 pa 指向 a[0],那么 *(pa+1) 引用的是数组元素 a[1] 的内容,pa+i 是数组元素 a[i] 的地址,*(pa+i) 引用的是数组元素 a[i] 的内容,如下图所示:
在这里插入图片描述

无论数组 a 中的元素类型或者数组长度是什么,上面结论都成立。“指针加 1”就意味着,pa+1 表示 pa 所指向的元素的下一个元素。

数组下标和指针运算之间具有密切的对应关系。C 语言规定,数组名代表数组中首个元素(即序号为 0 的元素)的地址。也就是说,数组名可以理解为是一个指针常量。

所以,执行赋值语句 pa = &a[0]; 后,pa 与 a 具有相同的值。因为数组名所代表的就是该数组最开始的一个元素的地址,因此, pa = &a[0]; 也可以写成 pa = a;。

对数组元素 a[i] 的引用也可以写成 *(a+i) 的形式。实际上,在编译过程中,编译器也是先把 a[i] 转换成 *(a+i) 这种指针表示形式然后再进行求值,所以这两种形式是等价的。

当我们对 a[i] 和 *(a+i) 分别进行取址运算,可以知道 &a[i]& *(a+i)(简化为 a+i)也是相同的,a+i 表示 a 之后第 i 个元素的地址。

相应的,如果 pa 是一个指针,那么,在表达式中也可以在它的后面添加下标。pa[i]*(pa+i) 是等价的。简而言之,一个通过(数组和下标)实现的表达式可以等价地通过(指针和偏移量)实现。

最后,需要注意指针和数组名两者的一个不同之处:

指针是一个变量,因此对于赋值语句 pa = a 和自增运算 pa++ 都是合法的,但数组名是常量不是变量,所以类似 a = pa 或者 a++ 等语句是非法的。

以上,希望对你理解指针和数组的关系能有所帮助。

下面我们将继续介绍与它们两个相关的其他一些知识点。

字符串与数组
在 C 语言中,有一种基本数据类型叫“字符型数据”,用 char 表示。

字符常量

用单引号括起来的一个字符,例如 ‘a’、‘A’、‘5’、’?’、’\n’、’\0’ 等都是字符常量,总共有 128 个符号(其中有 32 个符号是不可显示的控制符)。

字符变量

字符型变量用来存放字符常量,但它只能存放一个字符,不要以为在一个字符变量中可以存放一个字符串(包含若干个字符)。字符变量的定义如下:

char c1, c2;
c1 = ‘a’;
c2 = 97; // 没有看错,这是合法的,c2 变量此时也存放字符 ‘a’

在所有的编译系统中都规定以一个字节来存放一个字符,或者说,一个字符变量在内存中占一个字节。

字符串常量

或许你已经发现了,在 C 语言中,基本数据类型并没有“字符串”类型,因此只能通过“字符数组”的方式来存放字符串。字符串常量是通过双引号括起来的字符序列。例如:“hello, world!”、“CHINA” 等等。

注意:不要将字符常量和字符串常量混淆,‘a’ 是字符常量,“a” 是字符串常量,两者是不同的,更不能把一个字符串常量赋给一个字符变量。

char c;
c = 'a'; // 合法
c = "a"; // 错误的
c = "CHINA"; // 也是错误的

字符串常量是一个字符数组。C 语言中又规定,在每一个字符串常量的结尾加一个“字符串结束标志” \0,以便系统据此判断字符串是否结束。例如,有一个字符串常量 “hello”,它在内存中是这样存储的:
在这里插入图片描述

它占的内存单元不是 5 个字符,而是 6 个字符,最后一个字符为 \0。所以字符串常量占据的存储单元数比其字面量(双引号内的字符数)大 1。不过用 C 语言的 strlen 函数对一个字符串取长度,获得的数值并没有包括终止符 \0。

字符串变量(字符数组)

因为没有“字符串类型”,所以在 C 语言中,并没有真正意义上的“字符串变量”!本节我们来介绍一下字符数组的概念及与字符串的关系。

用来存放字符型数据的数组称为“字符数组”,字符数组中的每一个元素存放一个字符。

如前面所述,C 语言中是将字符串作为字符数组来处理的。所以,对于一个字符数组,如果其结尾为空字符 \0,那么就可以把它视为一个“字符串变量”(并不严谨),例如:

char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'};

注:对于一个字符数组不管有多长,从头开始遍历,一旦遇到结束空字符 \0就表示字符串结束,\0 前面的字符组成字符串,\0 后面的字符元素将被忽略。所以程序一般都是通过靠检测 \0 的位置来判断字符串是否结束,而不是根据字符数组的长度来决定字符串的长度。

所以有了结束标志 \0 后,字符数组的长度就显得不那么重要了。当然了,字符串实际长度加上 \0 的总长度是不能超过存放它的字符数组的长度的。

另外,我们也可以使用字符串常量来初始化字符数组:

char str[] = {"hello"}; 

此时,系统会自动在字符串常量的末尾加上一个结束空字符 \0。也可以省略花括号,直接写成:

char str[] = "hello";

上述两种写法与下面的初始化写法等价:

char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};

但与下面的写法是不等价:

char str[] = {'h', 'e', 'l', 'l', 'o'};

前者的长度为 6,而后者的长度为 5。

需要说明的是,对于字符数组,并不要求它的最后一个字符为 \0,甚至都可以不包含 \0。但如果用字符数组来存储字符串常量,则必须在末尾有 \0 结束标示符,通常系统会自动加上,也可以人为添加。

字符串与指针
前面一节讲了可以用字符数组来表示和存储字符串,而数组又与指针有着密切的联系。所以,也可以用指针来表示和管理字符串。

对于如下定义:

char str[] = "hello";

我们知道 str 是数组名,它代表着字符数组的首个元素的地址。因此我们可以定义一个字符指针指向它:

char *pStr = &str[0];

在这里插入图片描述

根据前面讨论,此时 pStr = str。因此,我们可以不定义字符数组,而直接定义一个字符指针,用字符指针指向字符串中的字符,如下:

char *pStr = "hello";

其中,pStr 指向字符 ‘h’ 的地址,pStr 的值,即 pStr[0],为 ‘h’,pStr+1 指向字符 ‘e’ 的地址;(pStr+1) 的值,即 pStr[1],为 ‘e’,依次类推,最后一个字符为结束空字符 \0。

这里,pStr 只是一个字符型指针变量,它指向字符串 “hello” 的第一个字符的地址(即:存放字符串常量 “hello” 的字符数组的首个元素的地址)。而不能理解为:“pStr 是一个字符串变量,在定义时把 “hello” 这几个字符赋给该字符串变量。”。

最后,在 C 语言中,对字符和字符串的输出格式符分别为 %c 和 %s,例如:

char c = 'a';
char *str = "hello, world!";
printf("%c", c);
printf("%s", str);

指针常量与常量指针
指针常量

指针本身是一个常量,它指向的地址不可以发生变化,但指向的地址的内容可以变化,声明方式:

int * const p;

常量指针

指向常量的指针,也称为常指针,即指针指向的地址对应的值不可变,但指针可以指向其它(常量)地址,声明方式:

int const * p;
const int * p;

指向常量的常指针

const int * const p;

指针函数与函数指针
指针函数

它是一个函数,即返回指针值的函数。对于一个函数,可以返回一个整型值、字符值、实型值等,也可以返回指针类型的数据,即地址,声明形式如下:

类型名 * 函数名(参数列表)

示例:

int * func(int x, int y);

此处,func 为函数名,调用它后将得到一个指向整型数据的指针(地址)。

函数指针

它是一个指针,即指向函数的指针,声明形式如下:

类型名 (* 指针变量名)(函数参数列表)

示例:

int a, b;
int max(int, int);   // 声明一个函数 max(假设已在其它地方实现)
int (* p)(int, int); // 声明一个函数指针 *p
p = max;             // 把函数名 max 赋值给函数指针 *p
a = max(1, 2);       // 通过函数名调用
b = (* p)(1, 2);     // 通过函数指针调用

与数组名代表数组首个元素地址类似,函数名代表该函数的入口地址,赋值语句 p = max; 的作用是将函数 max 的入口地址赋给指针变量 p。

此时,p 和 max 都指向函数的开头,调用 * p 就是调用 max 函数。

因此,函数的调用可以通过函数名调用,也可以通过函数指针调用,上述 a = max(1, 2); 与 b = (* p)(1, 2) 本质上是一样的。

另外,需要说明的是,int (* p)(int, int); 表示定义了一个指向函数的指针变量 p,它并不是就固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。在程序中把另一个函数(该函数的返回值应该是整型,且有两个整型参数)的地址赋给它,它就指向这一个函数。也就是,一个函数指针变量可以先后指向同类型的不同函数。

指针数组与指向指针的指针
指针数组

对于一个数组,如果其元素均为指针类型的数据,称其为“指针数组”,也就是说,指针数组中的每一个元素都相当于一个指针变量,声明方式如下:

类型名 * 数组名[数组长度];

例如:int * p[4]; 表示声明一个数组 p,它有 4 个元素,每个元素的类型为 int * 指针类型,即每个元素都可指向一个整型变量。

指针数组的使用场景:比较适合用于存放若干个字符串(也可理解为是“字符串数组”),使字符串处理更加方便灵活。例如:

char * names[] = {"Li Lei", "Han Meimei", "Kang Zubin"};

指针数组另一个重要作用是:作为 main 函数的形式参数,如下:

int main (int argc, char * argv[]);

数组指针

注意上面不要写成 int (* p)[4],它表示一个指向一维数组(数组长度为 4)的指针变量,也称为“数组指针”。

指向指针的指针

在理解了指针数组的概念的基础上,下面介绍指向指针数据的指针变量,简称为“指向指针的指针”。

例如前面定义的指针数组:

char * names[] = {"Li Lei", "Han Meimei", "Kang Zubin"};

它的示意图如下所示:

在这里插入图片描述

这里,names 是一个指针数组,它的每一个元素都是一个指针类型的数据(值为地址),指向某一个字符串常量(字符数组),而数组中每个元素都有相应自己的地址。

根据前面定义,数组名 names 代表该指针数组首个元素的地址,names+i是元素 names[i] 的地址。所以,指针数组的数组名,本身就是一个指向指针的指针。

此外,我们也可以定义一个指针变量 p,让它指向指针数组的元素(而数组的元素也是指针),那么 p 就是一个指向指针型数据的指针变量。

char ** p = &names[0]; // 等价于
char ** p = names;

上述 p 前面有两个 * 号,它相当于 * (* p),其中 * p 是指针变量的声明形式,它表示定义了一个指向字符数据的指针变量,现在在它前面又加了一个 *号,表示指针变量 p 此时是指向“一个字符指针变量”的,即 p 是一个“指向指针的指针”。(有点绕,慢慢理解)

接下来对双指针 p 的相关操作就相当于是对指针数组 names 的操作,我们不再赘述。

空指针与野指针
void 指针类型

ANSI 标准增加了一种 void 指针类型,即可定义一个指针变量,但它不指定它是指向哪一种类型数据的。

它可以用来指向一个抽象的类型的数据,在将它的值赋给另一个指针变量时,要进行强制类型转换使之适合于被赋值的变量的类型,例如:

char *p1;
void *p2;
// ...
p1 = (char *)p2;

同样可以用 (void *)p1 将 p1 的值转成 void * 类型,例如:p2 = (void *)p1;,也可以将一个函数的返回值定义一个为 void * 类型,例如:

void * func(char ch1, char ch2);

表示函数 func 返回的是一个地址,它指向“空类型”,如果需要引用此地址,需要根据情况对之进行类型转换,例如 char *p1 = (char *)func(ch1, ch2);。

空指针

上述介绍的 void * 表示空指针类型,它可以转换为其他指向类型。而在 C 语言中又双叒叕定义,(void *)0 表示的空指针常量。

如果 p 是一个指针变量,当它的值为空指针常量时,即 p = (void *)0,则此时称 p 为空指针,表示 p 指向一个空地址,即地址 0(不是常数 0),或者说指向 NULL。

野指针

根据定义,野指针是指向一个已删除的对象或未申请访问受限内存区域的指针,也就是它指向了不合法的内存区域。野指针也称作“迷途指针”或者“悬空指针”。

对于一个指针 p,当它所指向的对象被释放或者收回,但是对该指针没有作任何的修改(比如没有置为 NULL),以至于该指针仍旧指向已经回收的内存地址,此时 p 就成为一个野指针。后续如果再对指针 p 进行操作,可能就会造成程序崩溃或产生不可预知的结果。

下面是主要来源:
https://mp.weixin.qq.com/s?src=11&timestamp=1587265981&ver=2287&signature=RM4bHEp9ZqSZr0h95lJVIi0spaTGwm-aCzIFwkrqqyC4B9J9m4enkXHOBbP-eAKReIf52wBONAY9Z5FqlkCTSGJ9UT0adlZq7u4qaa0giesOfPZhDcJm6IHAaP0Tyo&new=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值