C语言–指针进阶
文章目录
- C语言--指针进阶
- 一、字符指针
- 1.1 两种使用方式
- 1.2 经典面试题
- 1.2.1 最终结果
- 1.2.2 总结
- 二、数组指针
- 2.1 数组指针的语法
- 2.2 &数组名与数组名的区别
- 2.2.1 运行结果
- 2.3 一维数组笔试题
- 2.4 字符数组笔试题
- 2.4.1 字符数组
- 2.4.2 字符串数组
- 2.4.3 字符串指针
- 2.4 二维数组笔试题
- 2.5 数组指针的使用
- 2.6 总结
- 三、指针类型的甄别
- 四、数组参数,指针参数
- 4.1 一维数组传参
- 4.2 二维数组传参
- 4.3 一级指针传参
- 4.4 二级指针传参
- 五、函数指针
- 5.1 函数指针的语法
- 5.2 经典面试题
- 六、函数指针数组
- 6.1 函数指针数组的语法
- 七、指针笔试题
一、字符指针
1.1 两种使用方式
第一种:
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'w';
return 0;
}
第二种:
int main()
{
const char* pstr = "hello";//这里是把一个字符串放到pstr指针变量里了吗?
//加入const的原因是因为常量字符串不能更改,防止后面有人更改常量字符串导致程序崩坏
printf("%s\n", pstr);//不能*pstr,因为这不是解引用打印,因为pstr里面放的是字符串的首地址
return 0;
}
//printf("%c\n", *pstr);这样打印出来的结果就是h自己,不能把整个字符串打印出来
pstr是一个指针变量,‘ hello\0 ’这个字符串是没有办法被赋值到pstr这个指针变量中,所以pstr接收到的应该是字符串首元素的地址
1.2 经典面试题
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
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;
}
1.2.1 最终结果
1.2.2 总结
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
二、数组指针
字符指针—存放字符地址的指针—指向字符的指针 char*
整形指针—存放整形地址的指针—指向整形的指针 int*
浮点型指针—存放浮点型地址的指针—指向浮点型的指针 float* double*
数组指针—存放数组地址的指针—指向数组的指针
2.1 数组指针的语法
int (*p2)[10];
int (p)[10]; 解释:p先和 * 结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
这里要注意:[]的优先级要高于号的,所以必须加上()来保证p先和*结合。
2.2 &数组名与数组名的区别
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
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;
}
2.2.1 运行结果
运行结果的意义:
- 数组名–数组首元素的地址
- &数组名-数组的地址
- 数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样
造成移位不同的原因 - 指针的类型不同:arr的类型是int* &arr的类型是int( * )[10]
2.3 一维数组笔试题
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
- sizeof(数组名),此时数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,数组名表示整个数组,取出的是整个数组的地址
- 除此之外,所有的数组名都是数组首元素的地址
2.4 字符数组笔试题
2.4.1 字符数组
#include<stdio.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr + 0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr + 1));
printf("%d\n", sizeof(&arr[0] + 1));
return 0;
}
使用strlen函数求字符串长度的时候,不同类型的值应该是什么呢?
strlen函数的结束标志是 " \ 0" ,没有出现就会一直计算,直到出现结束标志
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
2.4.2 字符串数组
- sizeof是操作符,只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
- strlen是库函数,是求字符串长度的,统计的是 ‘\0’ 之前 出现的字符个数,一定要找到\0 才算结束,所以可能存在越界访问的情况
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr + 0));
//printf("%d\n", strlen(*arr));
//printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr + 1));
printf("%d\n", strlen(&arr[0] + 1));
return 0;
2.4.3 字符串指针
sizeof与strlen计算指针
char* p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p + 1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p + 1));
printf("%d\n", sizeof(&p[0] + 1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
2.4 二维数组笔试题
二维数组在内存中的存储方式
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[0][0]));
printf("%d\n", sizeof(a[0]));
printf("%d\n", sizeof(a[0] + 1));
printf("%d\n", sizeof(*(a[0] + 1)));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(*(a + 1)));
printf("%d\n", sizeof(&a[0] + 1));
printf("%d\n", sizeof(*(&a[0] + 1)));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a[3]));
关于sizeof是否参与计算的问题
#include<stdio.h>
int main()
{
//二维数组
short s = 3;
int a = 10;
printf("%d\n", sizeof(s = a + 2));
printf("%d\n", s);
}
2.5 数组指针的使用
数组指针对于一维数组来说不是很友好,主要还是在二维数组中使用
#include <stdio.h>
void print_arr1(int arr[2][5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int(*arr)[5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", (*(arr+i))[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_arr1(arr, 2, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 2, 5);
return 0;
}
2.6 总结
数组名的意义:一维二维一样
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
- 二维数组的首元素的地址是第一行的地址
三、指针类型的甄别
要考虑优先级再判断指针的类型
int arr[5]//整形数组,数组是5个元素
int *parr1[10]//指针数组,数组10个元素,每个元素是int* 类型
这里就涉及到优先级的问题,[ ]优先级是高于 *,所以parr1是先与[ ]结合形成数组,然后这个数组的类型是int * ,代表这个数组里面包含的元素是指针类型
int (*parr2)[10]//数组指针
*parr2代表其为一个指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的
int (*parr3[10])[5]
parr3是数组,数组有10个元素,数组的每个元素的类型是:int(*)[5]的数组指针类型
四、数组参数,指针参数
4.1 一维数组传参
#include <stdio.h>
void test(int arr[])//数组传参,数组接收,接收时数组可以不指定大小
{}
void test(int arr[10])//数组传参,数组接收,接收时指定大小
{}
void test(int* arr)//数组传参,指针接收,数组为int类型,指针为int* 类型
{}
void test2(int* arr[20])//指针数组传参,指针数组接收,指定大小或者不指定大小都可以
{}
void test2(int** arr)//指针数组传参,指针接收,数组类型是int* 那么对应接收的指针类型就是int**
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
4.2 二维数组传参
二维数组传参,要不就是二维数组接收,要不然就使用数组指针接收
void test(int arr[3][5])
{}
void test(int arr[][])//错误
{}
void test(int arr[][5])//
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//数组名表示首元素的地址,二维数组的首元素的地址是第一行数组的地址,而不是首元素的地址,不能用一级指针表示
{}
void test(int* arr[5])//指针数组 arr[5]表示一维数组,数组里面存放的还是int* 类型的指针,所以错误
{}
void test(int(*arr)[5])//数组指针,并且每个数组是5个元素,类型是int
{}
void test(int** arr)//二级指针是用来接收一级指针的地址,类型无法匹配,所以错误
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);
4.3 一级指针传参
#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;
}
一级指针传参就用一级指针接收
当函数的参数部分为一级指针的时候,函数能接收什么参数
void test(int* p)
{
}
int a = 10;
int* p = &a;
test(&a);//整形变量地址可以使用一级指针接收
test(p);//直接传一级指针
int arr[10];
test(arr)//一维数组名是首元素的地址,数组是int类型,所以指针就是int*
4.4 二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
int* arr[10];
test(pp);//二级指针传参
test(&p);//一级指针的地址传参
test(arr);//指针数组的数组名
return 0;
}
五、函数指针
5.1 函数指针的语法
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = &Add;//函数指针语法
int(*pf)(int, int) = Add;//函数指针语法
//pf是一个存放函数地址的指针变量--函数指针
//******************************************
//函数指针的调用
int ret = (*pf)(2,3);
int ret = pf(2,3);//不写*也可以的
}
&函数名和函数名都是函数的地址
5.2 经典面试题
//代码1
int main()
{
(* ( void (*)() ) 0 ) ();
return ;
}
void ()() 这是函数指针类型
( void ()())0 就是要对0进行强制类型转换成函数指针类型,就是0被当成一个函数的地址
(* ( void (*)() ) 0 ) 对地址进行解引用操作,无参数传参,后面只写()即可
总结下来就是对0地址处的函数进行调用
//代码2
void (*signal(int, void(*)(int)))(int);
该代码是一次函数的声明,声明的函数名字叫signal ,signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针 void(*)(int) 指向的那个函数的参数是int,返回类型是void
六、函数指针数组
数组里面的元素是函数指针的数组就是函数指针数组
6.1 函数指针数组的语法
int (*pfa[5])(int,int)
//pfa[5]代表pfa是一个数组
//int (*)(int,int)代表数组元素的类型
七、指针笔试题
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}