字符指针
概念
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
- 可知,
arr1
和arr2
的里'a'
的地址不同
p1
和p2
里的'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)
是等价的