关于C指针

字符指针

概念
char* pc;
功能

        1. 存放字符地址:

#include<stdio.h>

int main(){
	char ch = 'w';
	char* pc = &ch;
	return 0;
}

        2. 存放字符串首字母地址

#include<stdio.h>

int main(){
	char arr[] = "abcdef";
	char* pc = arr;

	printf("%s\n", arr);
	printf("%s\n", pc);

	return 0;
}

linux@linux:~/Lesson/Pointer/char*$ gcc 2.c
linux@linux:~/Lesson/Pointer/char*$ ./a.out
abcdef
abcdef

  • 数组名 arr存放字母 a 的地址
  • pc = arr,故pc存的也是a的地址
  • %s打印字符串,遇到\n结束
    在这里插入图片描述

        3.1 存放常量字符串首字母地址

#include<stdio.h>

int main(){
	char* p = "abcdef"; //"abcdef" 是常量字符串

	printf("%c\n", *p); //p里存的是'a'的地址 
	printf("%s\n", p);

	return 0;
}

linux@linux:~/Lesson/Pointer/char*$ gcc 3.c
linux@linux:~/Lesson/Pointer/char*$ ./a.out
a
abcdef

  • 这里只是把字符串首字母地址给了p,可不是把字符串给它了

        3.2 常量字符串

#include<stdio.h>

int main(){
	char* p = "abcdef"; //常量字符串

	*p = 'w'; //赋值前,*p = 'a'; 可以把 'a' 换成 'w' 吗? 
	printf("%s\n", p);

	return 0;
}

linux@linux:~/Lesson/Pointer/char*$ gcc 4.c
linux@linux:~/Lesson/Pointer/char*$ ./a.out
Segmentation fault (core dumped) //段错误

  • 通过上边可知,常量字符串的内容不可修改

        3.3 用const维护字符串常量

#include<stdio.h>

int main(){
	const char* p = "abcdef"; //此时const 修饰 *p,故 *p的内容不可修改
	
	*p = 'w';
	printf("%s\n", p);

	return 0;
}

linux@linux:~/Lesson/Pointer/char*$ gcc 5.c
5.c: In function ‘main’:
5.c:6:2: error: assignment of read-only location ‘*p’
*p = 'w';
 ^
// 有了const,编译直接报错,*p只能用来读

  • 当我们用指针指向一个字符串常量时,用const修饰,安全
示例:
#include<stdio.h>

int main(){
	char arr1[] = "abcdef"; //字符串
	char arr2[] = "abcdef";
	const char* p1 = "abcdef"; //常量字符串
	const char* p2 = "abcdef";

	if (arr1 == arr2){ //它们相等吗?为什么?
		printf("arr1 == arr2\n");
	}else{
		printf("arr1 != arr2\n");
	}

	if (p1 == p2){ //它们相等吗?为什么?
		printf("p1 == p2\n");
	}else{
		printf("p1 != p2\n");
	}

	return 0;
}

linux@linux:~/Lesson/Pointer/char*$ gcc 6.c
linux@linux:~/Lesson/Pointer/char*$ ./a.out
arr1 != arr2
p1 == p2

  • 可知,arr1arr2的里'a'的地址不同
               p1p2里的'a'地址相同,也就是说p1、p2指向同一个地址,也就是说只有一个常量字符串
    在这里插入图片描述

指针数组

概念

        指针数组是数组,用来存放指针

#include<stdio.h>

#define N 10
int main(){
	int arr[N] = {0}; //整形数组
	char ch[N] = {0}; //字符数组
	int* parr[N]; //存放整形指针的数组 - 指针数组
	char* pch[N]; //存放字符指针的数组 - 指针数组

	return 0;
}
功能

        指针数组里存的是地址

#include<stdio.h>

int main(){
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int* arr[4] = {&a, &b, &c, &d};

	int i = 0;
	for(i=0; i<4; i++){ //打印指针数组里的地址
		printf("%p  ", arr[i]);
	}
	puts(""); //换行

	printf("&a=%p &b=%p &c=%p &d=%p\n", &a, &b, &c, &d); //打印abcd的地址

	return 0;
}

linux@linux:~/Lesson/Pointer/pointer_arr$ gcc 2.c
linux@linux:~/Lesson/Pointer/pointer_arr$ ./a.out
0xbfb520ec 0xbfb520f0 0xbfb520f4 0xbfb520f8 //数组里存的地址
&a=0xbfb520ec &b=0xbfb520f0 &c=0xbfb520f4 &d=0xbfb520f8

        怎么通过指针数组里存的地址打印出abcd的内容

#include<stdio.h>

int main(){
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int* arr[4] = {&a, &b, &c, &d};

	int i = 0;
	for(i=0; i<4; i++){ 
		printf("%d  ", *arr[i]); //加*取对应地址的内容
	}
	puts("");

	return 0;
}

linux@linux:~/Lesson/Pointer/pointer_arr$ gcc 3.c
linux@linux:~/Lesson/Pointer/pointer_arr$ ./a.out
10 20 30 40

        鹏哥说:指针数组不是上边这样用,谁说是这样用,那就是胡扯。往下看

#include<stdio.h>

int main(){
	int arr1[] = {1,1,1};
	int arr2[] = {2,2,2};
	int arr3[] = {3,3,3};
	int* parr[] = {arr1, arr2, arr3};	

	int i,j;
	for (i=0; i<3; i++){  //用指针数组打印每个数组里的元素
		for (j=0; j<3; j++){
			printf("%d ", *(parr[i] + j)); //parr[i] + j, 通过循环可表示每个数组里,每个元素的地址
		}
		puts("");
	}

	return 0;
}

在这里插入图片描述

linux@linux:~/Lesson/Pointer/pointer_arr$ gcc 4.c
linux@linux:~/Lesson/Pointer/pointer_arr$ ./a.out
1 1 1
2 2 2
3 3 3

  • 上边是指针数组的一种用法,遇到别的你也要弄清楚怎么搞⛏

思考一下下边指针数组的意思

int* arr1[6]; //整型指针的数组
char *arr2[6]; //一级字符指针的数组
char **arr3[6]; //二级字符指针的数组

数组指针

概念

        数组指针是数组还是指针?

    答:指针

        我们知道

int *pint; //整型指针,指向整型数据
float *pf; //浮点型指针,指向浮点型数据

        那么

    数组指针应该是:指向数组的指针

        动一下你机灵的小脑袋💡,下边那个是数组指针?

int *p1[10];
int (*p2)[10];

//p1, p2分别是什么?

        数组指针

#include<stdio.h>

int main(){
	int *p = NULL; //p是整形指针--指向整形(数据)--存放整形的地址
	char *pc = NULL; //pc是字符指针--指向字符(数据)--存放字符的地址

	int arr[10] = {0};
	//arr -- 首元素的地址
	//&arr[0] -- 首元素的地址
	//&arr -- 数组的地址
	
	int arr[10] = { 6, 6, 6};
	int (*p)[10] = &arr; //p就是数组指针,存数组的地址 
	
	return 0;
}

        对比上边,思考下,用什么样的数组放 char* arr[5]?

#include<stdio.h>

int main(){
	char* arr[5];
	char* (*pa)[5] = &arr;
	
return 0;
}

在这里插入图片描述
        到这里,应该会有人会想 &arr 是什么?,和 arr 有什么区别呢?

#include<stdio.h>

int main(){
	int arr[10] = {0};

	printf("arr  = %p\n", arr);
	printf("&arr = %p\n", &arr);

	printf("arr+1  = %p\n", arr+1);  
	printf("&arr+1 = %p\n", &arr+1); 

	return 0;
}

linux@linux:~/Lesson/Pointer/arr_pointer$ gcc 2.c
linux@linux:~/Lesson/Pointer/arr_pointer$ ./a.out
arr = 0xbf935368 //发现arr 和 &arr都是首元素的地址
&arr = 0xbf935368
arr+1 = 0xbf93536c //数组名+1,跳过一个元素
&arr+1 = 0xbf935390 //取数组名地址+1,跳过一个数组

  • 事实上,&arr表示数组的地址,不是数组首元素的地址。(细细体会一下)

        🐱‍👤你悟了吗?

功能

        打印,数组指针所指向的,一维数组的所有元素

#include<stdio.h>

/* 第一种打印方式*/
int main(){
	int arr[5] = {1,2,3,4,5}; 
	int (*p)[5] = &arr;

	int i;
	for (i=0; i<5; i++){
		printf("%d ", (*p)[i]); //p存的是&arr,那*p就是arr
	}                           //(*p)[i] 相当于 arr[i]
	puts("");
}
/* 第二种打印方式 */
int main(){
	int arr[5] = {1,2,3,4,5}; 
	int (*p)[5] = &arr;

	int i;
	for (i=0; i<5; i++){
		printf("%d ", *((*p) + i)); //p存的是&arr,那*p就是arr
	}                               //((*p) + i)  相当于 &arr[i]
	puts("");                       //*((*p) + i) 相当于 arr[i]
} 
/*第三种打印方式*/
int main(){
	int arr[5] = {1,2,3,4,5}; 
	int *p = arr;

	int i;
	for (i=0; i<5; i++){
		printf("%d ", *(p+i)); //*(p+i) 相当于 p[i]、*(arr+i)、arr[i]
	}                           
	puts("");
}
  • 你会觉得直接用第三种方法不就行了,搞那么麻烦
  • 其实,上边只是做个示范,往下看

        打印二维数组的所有元素

#include<stdio.h>

void print_1(int arr[3][6], int x, int y);
void print_2(int (*p)[6], int x, int y);

int main(){
	int arr[3][6] = {{6,6,6,6,6,6},{6,6,6,6,6,6},{6,6,6,6,6,6}};

	print_2(arr, 3, 6);

	return 0;
}

void print_1(int arr[3][6], int x, int y){
	int i,j;

	for (i=0; i<x; i++){
		for (j=0; j<y; j++){
			printf("%d ", arr[i][j]);
		}
		puts("");
	}
}

void print_2(int (*p)[6], int x, int y){ //这里为什么用数组指针?参考下图
	int i,j;

	for (i=0; i<x; i++){
		for (j=0; j<y; j++){
			printf("%d ", *(*(p+i) + j)); //p是一个数组的地址,*(p+i)是数组里的首元素地址
		}                                
		puts("");	                     
	}
} 

在这里插入图片描述

  • 总的说:二维数组的首元素地址,就是一个一维数组的地址

        到这里,你知道arr[i]*(arr + i)是怎么个关系吗?

#include<stdio.h>

int main(){
	int arr[6] = {6,6,6,6,6,6};
	int *p = arr;
	
	int i = 0;
	for (i = 0; i < 6; i++){
		printf("%d ", p[i]);
		printf("%d ", *(p + i));
		printf("%d ", arr[i]);
		printf("%d ", *(arr + 1));
		puts("");
	}

	return 0;
}

linux@linux:~/Lesson/Pointer/arr_pointer$ gcc 7.c
linux@linux:~/Lesson/Pointer/arr_pointer$ ./a.out
6 6 6 6
6 6 6 6
6 6 6 6
6 6 6 6
6 6 6 6
6 6 6 6

  • 可知p[i]、*(p+i)、arr[i]、*(arr+i)是等价的

数组传参和指针传参

函数指针

函数指针数组

指向函数指针数组的指针

回调函数

指针和数组面试题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值