一、字符指针
我们可以定义一个字符指针,指向一个字符变量,并通过指针来修改这个字符变量.
#include <stdio.h> int main() { char a = 'x'; char* p = &a; *p = 'y';//通过指针来修改 printf("%c\n", *p); printf("%c", a); return 0; }
那么字符指针还可以怎么用呢?
下面这段代码的运行结果是什么呢?
int main() { char* s1 = "aabbccdd" ; for (int i = 0; i < 3; i++) { *(s1 + i) = '0'; } printf("%s", s1); return 0; }
答案:
运行错误,原因是"aabbccdd"是常量字符串,它们存放在"常量区"(里面的东西是只读的),是不允许被修改的.
分析:
这段代码是将常量字符串存放进s1指针里面了吗?
显然不可能,想放也放不进去呀.
正确理解是:将该常量字符串的首元素放进了s1,即s1指向该字符串的首元素.
const关键字在后续的库函数模拟中会详细介绍,这里可以理解为被修饰的字符指针指向的内容,使其不能被修改.
使用字符指针打印字符串
int main() { const char* s1 = "aabbccdd" ; printf("%s", s1); return 0; }
如果对上面的代码还是不理解可以看一下,下面的这一道曾经的笔试题:
#include <stdio.h> int main() { char str1[] = "你好,初阶牛!."; char str2[] = "你好,初阶牛!"; const char* str3 = "你好,初阶牛!"; const char* str4 = "你好,初阶牛!"; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
答案:
str1 and str2 are not same
str3 and str4 are same
原因分析:
str1和str2是两个的字符数组,两个数组是相互独立的,它们在创建时,会各自向内存申请空间,所以地址必然不一样,只不过是在内存中两块不同内存区域存放着相同的内容罢了.
由于字符串常量是不能修改的,在内存中这类常量并没有必要保存两份,他们存储在内存中的常量区(只读型)所以str3和str4指向的是同一块空间.
图解:
二、指针数组
1.1 指针数组的定义
指针数组是数组还是指针?
答案是数组.
我们知道,
整形数组:存放整形的数组.
字符数组,:存放字符的数组.
浮点型数组:存放浮点型数据的数组,
int arr1[] = { 1,2,3,4,5 };//整形数组 char arrr2[] = "abcdef";//字符型数组 double arr3[] = { 3.4,5.8,1.9 };//浮点型数组
那么用于存放指针的数组自然被称为指针数组了.
那么下面哪个是指针数组?
int* arr1[10]; int(*arr2)[10];
由于" * “'(星号)的优先级要低于”[ ]"(中括号)
arr1指针数组: arr1先与[10]先结合,故arr1是指针数组
arr2数组指针: 由于括号的优先级更高,所以(*arr2)是指针,指向的对象是数组,故arr2是数组指针.
常见的指针数组:
int* arr1[10]; //整形指针的数组 char *arr2[4]; //一级字符指针的数组 char **arr3[5];//二级字符指针的数组
2.2 使用指针数组模拟二维数组
#include <stdio.h> int main() { int arr1[4] = { 1,2,3,4 }; int arr2[4] = { 2,3,4,5 }; int arr3[4] = { 3,4,5,6 }; int* arr[3] = { arr1,arr2,arr3 }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", *(arr[i] + j)); } printf("\n"); } return 0; }
arr1数组,arr2数组,arr3数组在内存中都有自己独立的内存空间,
我们将这三个一维数组的首元素地址放在一个新的指针的数组(arr)中,通过指针数组来访问这三个一维数组,效果就如同二维数组一样,但并不是真正的二维数组.
图解:
内存中存储的图:
三、数组指针
指向数组的指针被称为数组指针.
#include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p //指针p就是数组指针,指向arr数组 return 0; }
数组指针对于一维数组作用并不是很大,数组指针一般用于二维数组.
在二维数组传参时可以使用数组指针接收.
对于初学者,数组指针和指针数组经常容易搞混.
试着分析下面这些代码的含义吧!
int arr[5];//1 int *arr1[10];//2 int (*arr2)[10];//3 int (*arr3[10])[5];//4
答案:
1.整形数组:一个包含5个整形元素的整形数组
2.指针数组:数组中包含十个元素,每个元素都是一个整形指针(int*).
3.数组指针:指向一个拥有十个元素的整形指针.
4.数组指针数组:arr3先与[10]结合,说明arr3是一个数组,
其次剩下的int(*)[5]是arr3数组中的元素,显然是数组的每个元素都是一个数组指针.故arr3是存放了10个指向有5个整形元素数组的数组指针数组.
四、一维数组的传参
4.1 整形一维数组传参:
整形一维数组传过来的arr的是数组首元素地址,即一个整形的地址.
整形变量的地址用一级整形指针接收或者同样用数组接收都行.
void test1(int arr[])//使用不指定具体大小的一维数组接收 {} void test2(int arr[10])//使用具体大小的一维数组接收 {} void test3(int *arr)//使用指针接收 {}
具体实现:
#include <stdio.h> //使用同样一维数组接收 void test1(int arr[],int sz)//arr1 { for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); } //与test1一样的道理,用指定大小接收当然也没问题 void test2(int arr[5],int sz)//arr1 {} //使用指针接收 void test3(int* arr,int sz)//arr1 { for (int i = 0; i < sz; i++) { printf("%d ", *(arr+i));//等价于printf("%d ", arr[i]); } printf("\n"); } int main() { int arr1[] = { 1,2,3,4,5 }; int sz = sizeof(arr1) / sizeof(arr1[0]); test1(arr1,sz); test2(arr1,sz); test3(arr1,sz); return 0; }
4.2 一维指针数组传参
这三种有一种是错误的.
void test4(int *arr[20]) {} void test5(int* arr) {} void test6(int **arr) {}
一维指针数组的数组名表示首元素的地址,而首元素是一个整形指针,一级(整形)指针的地址可以用二级指针接收.也可以用相同类型的一维指针数组接收.
#include <stdio.h> //用整形指针数组接收整形指针数组 void test4(int* arr[3]) { for (int i = 0; i < 3; i++)//决定访问整形指针的个数 { for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数 { printf("%d ", *(arr[i] + j)); ///等价于printf("%d ", arr[i][j]); } printf("\n"); } printf("\n"); } //错误,不能使用 void test5(int* arr) { } //用二级指针接收一维指针数组 void test6(int** arr) { for (int i = 0; i < 3; i++)//决定访问整形指针的个数 { for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数 { printf("%d ", *(*(arr+i) + j));//这种一般不好理解 //等价于 printf("%d ", *(arr[i] + j)); //等价于 printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr1[4] = { 1,2,3,4 }; int arr2[4] = { 2,3,4,5 }; int arr3[4] = { 3,4,5,6 }; int* arr[3] = { arr1,arr2,arr3 }; test4(arr); test5(arr); test6(arr); }
五、二维数组传参
给定一个二维整形数组:
二维数组的数组名代表首元素地址,即第一行的地址(int*[ ]);
int arr[2][4] = { {1,2,3,4 },{5,6,7,8} };
下面七种传参方式,哪些是正确的.
void test1(int arr[2][4]) void test2(int arr[][]) void test3(int arr[][4]) void test4(int *arr) void test5(int* arr[4]) void test6(int (*arr)[4]) void test7(int **arr)
正确传参方式:test1()、test3()和test6()
#include <stdio.h> void test1(int arr[2][4]) { int i = 0, j = 0; printf("test1\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } printf("\n"); } void test3(int arr[][4]) { int i = 0, j = 0; printf("test2\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } printf("\n"); } void test6(int(*arr)[4])//ok? { int i = 0, j = 0; printf("test3\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } printf("\n"); } int main() { int arr[2][4] = { {1,2,3,4 },{5,6,7,8} }; test1(arr); test3(arr); test6(arr); }
二级指针作为参数
二级指针可以接收一级指针的地址.
#include <stdio.h> void print1(int** aa) { printf("%d\n", **aa); } void print2(char** pp) { printf("%c\n", **pp); } int main() { int a = 5; int* aa = &a; char* p = "abcdef"; char** pp = &p; print1(&aa); print2(&p); print2(pp); return 0; }