一、初级指针
1、什么是指针
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。意思是通过它能找到以它为地址的内存单元。
指针是一个变量,存放内存单元的地址(编号)。
对应到代码:
int main(){
int a = 10;//a占四个字节,&a指向四个字节中的第一个字节
int * pa = &a;//将a的地址存放在pa变量中,pa就是一个指针变量
*pa = 20;
return 0;
}
指针的大小在32位平台是4个字节,在64位平台是8个字节
2、指针和指针类型
1)指针类型的意义
所有类型的指针都为4个字节
1、指针类型决定了:指针解引用的权限有多大(能操作几个字节)
2、指针类型决定了:指针走一步能走多远(步长)
2)野指针
1、指针未初始化
int main(){
int* p;//p是一个局部的指针变量,局部变量不初始化,默认为随机值
*p = 20;//非法访问内存
return 0;
}
2、指针越界访问
int main(){
int arr[10] = {0};
int* p = arr;
int i = 0;
for (i=0; i<= 10; i++){//指针越界
*p = i;
p++;
}
}
3、指针指向的空间释放
解决:
1)不知道p应该初始化为什么地址是,可以指向NULL
2)小心指针越界
3)指针指向空间释放及时置NULL
4)指针使用之前检查有效性
3、指针运算
1)指针+-整数
#define N 5
float values[N];
float* vp;
//指针+-整数运算
for (vp = &values[0]; vp<&values[N]; ){
*vp++ = 0;
}
2)指针-指针
int main(){
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
//运行结果为9
指针-指针得到两个指针之间的元素个数
应用:求字符串长度
int my_strlen(char *s){
char *p = s;
while(*p != '\0')
p++;
return p-s;
}
4、指针与数组
int main(){
int arr[10] = {0};
int *p = arr;
return 0;
}
其中:arr[2] 相当于*(arr+2)相当于*(p+2) 相当于p[2]
5、二级指针
int main(){
int a = 10;
int* pa = &a;
//ppa为二级指针变量
int* *ppa = &pa;
return 0;
}
其中:**ppa == a
6、指针数组
指针数组仍然是数组,是存放指针的数组。
int main(){
int arr[10] = {0};//整形数组
char ch[5] = {0}; //字符数组
//指针数组-存放指针的数组
int* parr[10];//整形指针的数组
return 0;
}
二、指针进阶
1、字符指针
使用:
int main(){
char ch = 'w';
char *pc = &ch;
*pc = 'w';
//本质上是把“hello”这个字符串的首字符的地址存放在了ps当中
char* ps = "hello";
char arr[] = "hello"
printf("%c\n", ps);//h
printf("%s\n", ps);//hello
printf("%s\n", arr);//hello
return 0;
}
经典题目
其中str3/4指向的是常量字符串
2、指针数组
int main(){
int *arr[3]; //存放整形指针的数组
char ch[5] = {0}; //字符数组
int a[5] = {1, 2, 3, 4, 5};
int b[5] = {2, 3, 4, 5, 6};
int a[5] = {3, 4, 5, 6, 7};
int *arr[3] = {a, b, c};
int i = 0;
for (i=0; i<3; i++){
int j = 0;
for (j = 0; j<5; j++){
printf("%d ", *(arr[i] + j);//==arr[i][j]
}
printf("\n");
}
return 0;
}
3、数组指针
是一种指针,是指向数组的指针
eg.: int (*prr2)[10] 该指针能够指向一个数组,数组10个元素,每个元素的类型为int
eg.: int (*prr3[10])[5] 该指针是一个存放数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组。数组5个元素,每个元素是int类型
int main(){
int a = 10;
int *pa = &a;
char ch = 'w';
char *pc = &ch;
int arr[10] = {1, 2, 3, 4, 5};
int (*parr)[10] = &arr;//取出的是数组的地址
//parr就是一个数组指针
double* d[5];
double* (*pd)[5] = &d;
return 0;
}
4、数组传参与指针传参
void print1(int arr[3][5], int r, int c){
int i = 0;
int j = 0;
for (i=0; i<r; i++){
int j = 0;
for (j = 0; j<c; j++){
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2(int (*p)[5], int r, int c){
int i = 0;
int j = 0;
for (i=0; i<r; i++){
int j = 0;
for (j = 0; j<c; 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}};
//print1(arr, 3, 5);
print2(arr, 3, 5);
return 0;
}
1、一维数组传参
#include <stdio.h>
void test(int arr[]){} //ok
void test(int arr[10]){} //ok
void test(int *arr){} //ok
void test2(int *arr[20]) //ok
void test2(int **arr) //ok
int main(){
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test(arr2);
}
2、二维数组传参
#include <stdio.h>
void test(int arr[3][5]){} //ok
void test(int arr[][]){} //错误,可以不知道有多少行,但是必须知道一行有多少个元素
void test(int arr[][5]){} //ok
void test2(int *arr){} //错误
void test2(int* arr[5]){} //错误
void test2(int (*arr)[5]){} //ok
void test2(int **arr){} //错误
int main(){
int arr[3][5] = {0};
test(arr);
}
3、一级指针传参
void print(int *ptr, int sz){
int i = 0;
for (i = 0; i<sz; i++){
printf("%d\n", *(ptr+i));
}
}
int main(){
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
print(p, sz);
return 0;
}
4、二级指针传参
void print(int **p2){
**p2 = 20;
}
int main(){
int a = 10;
int *pa = &a;
int **ppa = &pa;
test(ppa);
printf("%d\n", a);
return 0;
}
5、函数指针
指向函数的指针,存放函数地址的指针
int Add(int a, int b){
return a+b;
}
int main(){
//pf为一个函数指针变量
int(*pf)(int , int) = &Add;
int ret = (*pf)(3, 5);
//int ret = pf(3, 5);
//int ret = Add(3, 5);//三种均可
//int ret = *pf(3, 5);//错误!
//printf("%p\n", &Add);
//printf("%p\n", Add);//两个完全相同
return 0;
}