
经过上篇文章【指针基础】,咱们对指针有了基础的了解,所以这文章就来深入学习指针。所以这篇文章适合有一定指针基础的学习。
在指针的类型中我们知道有一种指针类型为字符指针 char* ; 一般我们这样用:
int main()
{
char ch = 'c';
char* pc = &ch;
*pc = 'c';
return 0;
}咱们来看另外一种使用方式:
int main()
{
const char* pstr = "hello world";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}输出:

解释:
const char* pstr = "hello world"并不是将一整个字符串放到指针变量中,而是将常量字符串hello world的首字符的地址放到指针变量中。
有了这个理解,那咱们来看一下下面这串代码输出值为?
#include <stdio.h>
int main()
{
char str1[] = "hello world";
char str2[] = "hello world";
const char* str3 = "hello world";
const char* str4 = "hello world";
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与str2:
这是两个独立的字符数组初始化 编译器会为每个数组在栈上分配新的内存空间 虽然内容相同,但 str1 和 str2 是不同的内存地址 所以 str1 == str2 比较的是地址,结果为假
对于str3与str4:
这是两个指向字符串常量的指针 编译器会将相同的字符串字面量合并存储(存储在只读数据段) str3 和 str4 都指向同一个内存位置 所以 str3 == str4 比较的是地址,结果为真
咱们再来看一下这个const的作用,去掉它可以吗?
答案是不推荐,虽然它有时可以通过编译,但是不建议这样做,原因:
在【指针基础】篇我们也学了指针数组,指针数组是一个存放指针的数组。 这里我们主要是再回顾一边, 看一下下面指针数组是什么意思?
int* arr1[10];
char* arr2[4];
char** arr3[5];咱们依次来看:
int* arr1[10];//拆分
arr[10];//数组,且有10个元素
int* ;//数组中元素类型都为int*
int* arr1[10];//合起来为整形指针数组char* arr2[4]; //拆分
arr2[4]//数组,且有4个元素
char* ;//数组中元素类型都为char*
char* arr2[4];//即为字符型指针数组char** arr3[5]; //拆分
arr3[5]//数组,且有5个元素
char** ;//数组中元素类型都为char**
char** arr3[5];//即为二级字符型指针数组首先,明白一个点,数组指针是数组还是指针?
我的回答是:指针。
我们前面说过int*指向整型数据的指针……
那数组指针就是指向数组的指针,那应该用代码字母表示呢:
int *p1[10];
int (*p2)[10];
//那个是数组指针呢?解释
int (*p2)[10];是数组指针,因为p2先和结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p2是一个指针,指向一个数组,叫数组指针;int *p1[10];咱们前面已经见过了,指针数组嘛。但咱们也可以在分析一下,p1先于[10]结合,说明是大小为10个元素的数组,再于int*结合,说明数组里面的元素都为int类型的,故为整形指针数组; 还要知道一个点:[]的优先级要高于*号的,所以必须加上()来保证p2先和*结合。
对于
int arr[10];来说,&arr与arr,有什么区别呢,咱们直接来看代码:
#include<stdio.h>
int main()
{
int arr[10];
printf(" arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}输出:

可见,两者在数值上是一样的,那么两者代表的意思也是一样的吗? 再看一串代码:
int main()
{
int arr[10];
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1= %p\n", &arr+1);
return 0;
}输出:

看到这里,我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。 数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40 总结: &arr 表示的是数组的地址,而不是数组首元素的地址。 那咱们再来看一下:本例中 &arr 的类型是
解释:
int(*)[10],是一种数组指针类型:表示"指向包含 10 个整数的数组的指针"。
那数组指针是怎么使用的呢? 既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。 看代码:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码,因为没有什么意义
return 0;
}咱们来看一个数组指针的使用吧:
#include<stdio.h>
void print_arr( , int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
print_arr(arr, 3, 5);
return 0;
}针对上面的代码,咱们来思考对于printf_arr函数的第一个参数怎么设置?
第一个肯定这样想的
void print_arr(int arr[3][5], int row, int col)//传的是二维数组的数组名,用二维数组接收那另外的方法怎么想的,在这里,咱们要明白一个点:
数组名arr,表示首元素的地址 但是二维数组的首元素是二维数组的第一行 所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址 可以数组指针来接收
所以,咱们也可以这样写:
void print_arr(int (*arr)[5], int row, int col)在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
咱们来看一下下面代码中test1与test2函数参数有没有问题?
#include <stdio.h>
void test1(int arr[])
{
}
void test1(int arr[10])
{
}
void test1(int* arr)
{
}
void test2(int* arr[20])
{
}
void test2(int** arr)
{
}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test1(arr);
test2(arr2);
return 0;
}解释:
void test1(int arr[])//没问题,传什么用什么接收就行
{
}
void test1(int arr[10])//也没问题,这个10有没有都行(编译器忽略数组长度)
{
}
void test1(int* arr)//一维数组数组名就是首元素地址,用指针接受没什么问题
{
}
void test2(int* arr[20])//没问题
{
}
void test2(int** arr)//数组名就是首元素地址,首元素又是一级指针,所以没问题
{
}咱们来看一下下面代码中test2函数参数有没有问题?
void test(int arr[3][5])
{
}
void test(int arr[][])
{
}
void test(int arr[][5])
{
}
void test(int* arr)
{
}
void test(int* arr[5])
{
}
void test(int (*arr)[5])
{
}
void test(int** arr)
{
}
int main()
{
int arr[3][5] = { 0 };
test(arr);
}解释:
void test(int arr[3][5])//没问题
{
}
void test(int arr[][])//错误
{
}
void test(int arr[][5])//没问题
{
}
//二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//错误
{
}
void test(int* arr[5])//错误
{
}
void test(int (*arr)[5])//没问题
{
}
void test(int** arr)//错误
{
}
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//要用数组指针来接收,即int (*arr)[5]
int main()
{
int arr[3][5] = { 0 };
test(arr);
}主要看一下应用,看代码:
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}也是主要看一下应用,看代码:
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p);
return 0;
}sizeof 和 & 操作)[] 和 * 的优先级差异(int* p[10] 是指针数组,int (*p)[10] 是数组指针)