指针的进阶(上)

1. 指针(这里的指针指的是指针变量,口头上说的是指针)就是个变量,用来存放地址,地址唯一标识一块内存空间。

(内存会划分为小的内存单元,每个内存单元都有一个编号,这个编号就被称为地址,把地址也叫指针。内存编号=地址=指针)

(指针或者地址要存储,就可以存在指针变量里)

(int*p我们口头说的p是指针其实p是指针变量)

2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

(整型指针在解引用的时候访问四个字节,如果是字符指针解引用的时候只能访问一个字节)

4. 指针的运算

1. 字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char*

int main()
{
    char ch = 'w';
    char* c = &ch;
    *c = 'c';
    printf("%c", *c);
    return 0;
}

还有一种使用方式如下:

int main()
{
    const char* p = "hello ";//这里是把一个字符串放到p指针变量里了吗?
    printf("%s\n", p);
    return 0;
}

显然不是,在x86环境下,char类型指针是4个字节大小,这里的hello算是\0,总共六个字节,显然不是存储六个字节的字符串的

上面代码的意思是把一个常量字符串的首字符 h 的地址存放到指针变量 p 中

int main()
{
    const char* p = "abcdef";//这里看似是指向一个字符串,其实是指向字符串的第一个字符
    printf("%c\n", *p);//打印字符串的第一个字符
    printf("%s\n", p);//想打印字符串就打印它的第一个地址,由于字符串在内存上连续存放
    return 0;
}
 这里的abcdef是在内存的常量区中存放的,常量区的特点是只读的,得加const修饰,不可以改

int main()
{
    char str1[] = "hello ";
    char str2[] = "hello ";
    const char* p1= "hello ";
    const char* p2 = "hello ";
    if (str1 == str2)
        printf("str1 == str2 \n");
    else
        printf("str1!= str2 \n");

    if (p1 == p2)
        printf("str3==str4\n");
    else
        printf("str3!=str4\n");

    return 0;
}

 

分析:p1和p2存放的字符串指针 ,访问的是常量区是只读不可改,所以在存储的时候只存一个地址(是字符串的元素首地址),所以在比较的时候 ,都是同一个地址,所以肯定是等号

(char指针数组初始化成了字符串常量数组,常量在常量区中,不可更改,所以必须用const修饰。)

str1和str2两个申请了两个不同的地址保存两个首字符。所以在比较的时候是不等于的。

字符串指针数组是存在常量区,字符串数组是在栈区,str3和str4也是在栈区但是数组在常量区

2. 指针数组

指针数组
存放指针的数组
整型数组
int arr[10];//存放整型的数组,每个元素都是int
字符数组
char arr2[5];//存放字符的数组

int main()
{
    int a = 10;
    int b = 20;
    int c = 30;

    int* p1 = &a;
    int* p2 = &b;
    int* p3 = &c;
    int* arr[3] = { &a,&b,&c };
    for (int i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);
    }
    return 0;
}
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	for ( i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

这里模拟成一个二维数组。

数组名就是数组首元素地址

有两个例外

printf("%p\n", sizeof(arr));数组名不是首元素地址,表示整个数组

&数组名,数组名不是首元素地址,数组名表示整个数组。取出的是整个数组的地址

这里的parr[i][j]可以写成*parr([i]+j);

数组名表示首元素地址,parr【0】的类型是int*首元素地址+n就访问第n个元素

*(arr+5)=arr[5]

3. 数组指针

3.1 数组指针的定义

数组指针是指针?还是数组? 答案是:指针。

int main()
{
    int a = 10;
    int* p=&a;//p就是一个整型指针,是存放整型变量地址的,是指向整型的指针
    char ch = 'w';
    char* c = &ch;//字符指针是指向字符的指针,存放的是字符变量的地址
    //数组指针-指向数组的指针,不是数组首地址
    int* p1[10];//p1和[]运算符直接结合,p1是数组,指针数组
    int(*p2)[10];//有()*和p2先结合,p2是个指针,指针指向了数组,数组里面有十个元素,每个元素类型是int,它是一个数组指针
    return 0;
}

数组名确实是首元素地址,首元素是int类型,当你取地址的时候,arr拿出的是int*类型的,&arr[0]也是拿出int*。arr是数组,这么长的数组也是从首元素开始 。

 对于arr来说,它是一个int*,给arr+1就相当于arr跳过一个整型(所以5c->60),&arr[0]跟arr道理相同都是首元素地址,&arr+1,就加了40,因为它是一个数组的地址。&arr是一个什么呢?

&arr肯定是存到一个指针中,p=&arr,要书写这个指针怎么写?因为它是指针先要跟*结合,指向数组,所以int(*p)[10]=&arr;所以p叫数组指针。去掉名字就是它的类型int(*)[10],所以&arr,它的类型就是int(*)【10】(数组指针类型),这种数组指向10个元素,每个元素都是int类型。/
给它加1,它加的就是十个整型元素大小的数组,数组指针是专门存放一个数组地址的。这样的一个指针它指向的数组十个元素每个元素是int,给它加一,它会十个整型的一个数组。

arr【0】是int*,加1是跳过一个整型。&arr是int(*)【10】的数组指针类型加1跳过一个数组。数组的地址想存起来就得一个数组指针。int(*p)[10]是个指针,只有四个字节。p指针存的是十个整型元素的数组的地址。

int*p=arr;

int(*p)[10]=&arr;

第一个是把数组首元素地址放进去第二个是把整个数组地址放进去

char* arr[5];//这是个数组指针,数组里的五个元素都是指针
p=&arr;
p的类型?
p是个指针,加个*变成(*p),五个元素都是char*,所以p的类型是char *(*p)[5],没有这个*说明
我们是数组指针是char类型而非char*

&数组名VS数组名

通常情况下,我们说的数组名都是数组首元素地址,但是有两个例外,sizeof(arr)这里的arr表示整个数组。sizeof计算的是整个数组大小。第二个是&arr,取地址取出的是整个数组的地址。其余都是首元素地址

3.3 数组指针的使用

数组指针一般不用在一维数组上,因为会十分的别扭

//这是传入数组地址的时候访问,一维数组访问方式
void print1(int (*p)[10],int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		//*p:这里*p相当于拿到整个元素地址,也就是arr/(arr[0]),arr是首元素地址
		printf("%d ", *(*p + i));
	}
	printf("\n");
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//写一个函数打印arr数组的内容
	int sz = sizeof(arr) / sizeof(arr[0]);
	print1(&arr, sz);
	return 0;

}
//形参可以写成数组
//void print1(int arr[], int sz)
//{
//     int i = 0;
//     for (i = 0; i < sz; i++)
//   {
//	 printf("%d ", arr[i]);
//   }
//    printf("\n");
//}
//形参写成指针的形式
void print1(int *arr,int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
	printf("\n");
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//写一个函数打印arr数组的内容
	int sz = sizeof(arr) / sizeof(arr[0]);
	print1(arr, sz);
	return 0;
}

 由此可以看出,一维数组在使用数组指针的时候还是很别扭的

当讨论二维数组的时候,arr首元素数组地址表示的就是第一行。每一行都是一个元素

void print2(int (*p)[5],int col,int row)
{
	int i = 0;
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 0; j < row; j++)
		{
			//*(p+i),这样会拿到第i行,就是第i行的数组名
			//因为二维数组的数组名是二维数组的第一行
			printf("%d ", *(*(p + i) + j));
			//printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//写一个函数,打印arr数组
	print2(arr, 3, 5);
	return 0;
}

两种打印实际是一样的,编译器打印的原理是第一种。

arr[i]==*(arr+i);

arr[i][j]==*(arr+i)[j]==*(*arr+i)+j);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-Taco-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值